26 Commits
Author SHA1 Message Date
KeatonTheBot cb37aea614 Update Kenji-NX to 2.0.3 2025-04-27 21:39:26 -05:00
KeatonTheBot b40265e029 Revert "UI: Update Avalonia to 11.2.8, FluentAvalonia to 2.3.0"
This reverts commit 01f037ae83.
2025-04-27 21:24:58 -05:00
KeatonTheBot 10c9e46fbe nuget: bump Microsoft.IdentityModel.JsonWebToken to 8.9.0 2025-04-27 20:03:49 -05:00
KeatonTheBot 01f037ae83 UI: Update Avalonia to 11.2.8, FluentAvalonia to 2.3.0
* FluentAvalonia: Disabled NavigationView selection indicator animations due to bugged implementation in 2.1.0+, restoring previous behavior

* Avalonia: Fixed text on certain buttons being larger than normal

* Avalonia: Fixed ComboBox code inserting extra space to the left of selected items
2025-04-27 20:01:53 -05:00
KeatonTheBot bf618dc0b3 Revert "Custom refresh rate default value changed from 200% to 100%"
This reverts commit c48866c9ec.
2025-04-27 19:38:22 -05:00
LotP1 8b36e9fb39 fix: PPTC blacklist trigger conditions 2025-04-27 19:23:23 -05:00
Evan Husted 0cf29113c0 UI: Button to open screenshots folder in File menu 2025-04-27 18:25:40 -05:00
Evan Husted e75d162dbd UI: Always save screenshots to the Ryujinx data directory. 2025-04-27 18:25:31 -05:00
KeatonTheBot 038f8352e0 Vulkan: Minimize errors with feedback loop detection for AMD Radeon RX GPUs + Qualcomm SoCs 2025-04-27 00:37:22 -05:00
KeatonTheBot 2f48a5007a Update 'About' window 2025-04-26 23:04:35 -05:00
KeatonTheBot 45db10220e Increase # of maximum log files from 3 to 4 2025-04-26 20:05:51 -05:00
Evan HustedandMutantAura ebe623bc07 Stick Visualizer
![](https://i.imgur.com/iSaXRMr.png)

---------

Co-authored-by: MutantAura <domw0401@gmail.com>
2025-04-26 00:56:19 -05:00
LotP1 4bcfae5905 Reset PPTC Carriers on invalidation 2025-04-24 14:09:26 -05:00
LotP1 eb6a7b9fea reset infoStreams when the cache is invalid 2025-04-23 14:39:18 -05:00
LotP1 df322e6d57 Fix loading multiple mods with partially matching names
* Fix all mods always active
2025-04-23 14:38:48 -05:00
KeatonTheBot d46a6bfed5 Vulkan: Restrict feedback loop detection to AMD Radeon RX GPUs + Qualcomm SoCs 2025-04-21 22:18:59 -05:00
KeatonTheBot 0e810e1e96 Re-merge "Vulkan: Feedback loop detection and barriers (#7226)"
This re-merges commit ca59c3f499.
2025-04-21 22:18:59 -05:00
KeatonTheBot 5da6c490b3 Revert "Support VK_EXT_extended_dynamic_state and VK_EXT_extended_dynamic_state2"
This reverts commit 0cef9647
2025-04-21 22:18:58 -05:00
gdkchan 2ec9dda408 Optimize XMAD instruction sequence into a single 32-bit multiply when possible 2025-04-21 16:40:54 -05:00
KeatonTheBot 5b03721db6 misc: chore: Merge duplicated switch sections 2025-04-12 12:27:26 -05:00
KeatonTheBot 3c644a712d misc: chore: Fix possible System.NullReferenceExceptions 2025-04-12 12:27:15 -05:00
KeatonTheBot 19013d360a misc: chore: Remove redundant qualifiers 2025-04-11 22:08:30 -05:00
KeatonTheBot 9bcb744a6a misc: chore: Remove redundant initializer, join declaration and assignment 2025-04-11 22:07:48 -05:00
KeatonTheBot 06ea0c32d3 misc: chore: Remove unnecessary usings 2025-04-11 21:56:23 -05:00
KeatonTheBot feb3d9d31f misc: chore: Fix XML errors 2025-04-11 21:56:20 -05:00
RyllGanda17 198ee01437 Implement GetCacheStorageMax (#5)
Fixes this problem
`|W| HLE.OsThread.47 KernelIpc CallCmifMethod: Missing service Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.IApplicationFunctions: 29 ignored`

Allows add-on content from Just Dance 2023/2024/2025 to be loaded.
2025-04-10 23:03:16 -05:00
131 changed files with 2208 additions and 1756 deletions
+1 -1
View File
@@ -22,7 +22,7 @@
<PackageVersion Include="LibHac" Version="0.19.0" /> <PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.8.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
@@ -283,8 +283,6 @@ namespace ARMeilleure.Instructions
switch (op.ShiftType) switch (op.ShiftType)
{ {
case ShiftType.Lsr: case ShiftType.Lsr:
shift = 32;
break;
case ShiftType.Asr: case ShiftType.Asr:
shift = 32; shift = 32;
break; break;
@@ -332,8 +330,6 @@ namespace ARMeilleure.Instructions
switch (shiftType) switch (shiftType)
{ {
case ShiftType.Lsr: case ShiftType.Lsr:
shift = 32;
break;
case ShiftType.Asr: case ShiftType.Asr:
shift = 32; shift = 32;
break; break;
@@ -305,8 +305,6 @@ namespace ARMeilleure.Instructions
context.Store16(physAddr, value); context.Store16(physAddr, value);
break; break;
case 2: case 2:
context.Store(physAddr, value);
break;
case 3: case 3:
context.Store(physAddr, value); context.Store(physAddr, value);
break; break;
@@ -591,8 +589,6 @@ namespace ARMeilleure.Instructions
value = context.VectorInsert16(vector, value, elem); value = context.VectorInsert16(vector, value, elem);
break; break;
case 2: case 2:
value = context.VectorInsert(vector, value, elem);
break;
case 3: case 3:
value = context.VectorInsert(vector, value, elem); value = context.VectorInsert(vector, value, elem);
break; break;
@@ -733,8 +729,6 @@ namespace ARMeilleure.Instructions
switch (op.ShiftType) switch (op.ShiftType)
{ {
case ShiftType.Lsr: case ShiftType.Lsr:
shift = 32;
break;
case ShiftType.Asr: case ShiftType.Asr:
shift = 32; shift = 32;
break; break;
@@ -2082,8 +2082,6 @@ namespace ARMeilleure.Instructions
vector = context.VectorInsert16(vector, value, index); vector = context.VectorInsert16(vector, value, index);
break; break;
case 2: case 2:
vector = context.VectorInsert(vector, value, index);
break;
case 3: case 3:
vector = context.VectorInsert(vector, value, index); vector = context.VectorInsert(vector, value, index);
break; break;
@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ARMeilleure.Instructions namespace ARMeilleure.Instructions
{ {
+2 -2
View File
@@ -1,7 +1,7 @@
namespace ARMeilleure namespace ARMeilleure
{ {
using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities; using Arm64HardwareCapabilities = CodeGen.Arm64.HardwareCapabilities;
using X86HardwareCapabilities = ARMeilleure.CodeGen.X86.HardwareCapabilities; using X86HardwareCapabilities = CodeGen.X86.HardwareCapabilities;
public static class Optimizations public static class Optimizations
{ {
+3 -2
View File
@@ -30,7 +30,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 = 6998; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 7008; //! 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";
@@ -563,6 +563,7 @@ namespace ARMeilleure.Translation.PTC
{ {
if (AreCarriersEmpty() || ContainsBlacklistedFunctions()) if (AreCarriersEmpty() || ContainsBlacklistedFunctions())
{ {
ResetCarriersIfNeeded();
return; return;
} }
@@ -869,7 +870,7 @@ namespace ARMeilleure.Translation.PTC
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address)); Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
TranslatedFunction func = translator.Translate(address, executionMode, highCq); TranslatedFunction func = translator.Translate(address, executionMode, highCq, pptcTranslation: true);
if (func == null) if (func == null)
{ {
+8 -4
View File
@@ -219,7 +219,7 @@ namespace ARMeilleure.Translation
} }
} }
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false) internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false, bool pptcTranslation = false)
{ {
var context = new ArmEmitterContext( var context = new ArmEmitterContext(
Memory, Memory,
@@ -246,7 +246,7 @@ namespace ARMeilleure.Translation
context.Branch(context.GetLabel(address)); context.Branch(context.GetLabel(address));
} }
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter); ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter, pptcTranslation);
if (cfg == null) if (cfg == null)
{ {
@@ -326,7 +326,8 @@ namespace ARMeilleure.Translation
ArmEmitterContext context, ArmEmitterContext context,
Block[] blocks, Block[] blocks,
out Range range, out Range range,
out Counter<uint> counter) out Counter<uint> counter,
bool pptcTranslation)
{ {
counter = null; counter = null;
@@ -411,7 +412,10 @@ namespace ARMeilleure.Translation
if (opCode.Instruction.Emitter != null) if (opCode.Instruction.Emitter != null)
{ {
opCode.Instruction.Emitter(context); opCode.Instruction.Emitter(context);
if (opCode.Instruction.Name == InstName.Und && blkIndex == 0) // if we're pre-compiling PPTC functions, and we hit an Undefined instruction as the first
// instruction in the block, mark the function as blacklisted
// this way, we don't pre-compile Exlaunch hooks, which allows ExeFS mods to run with PPTC
if (pptcTranslation && opCode.Instruction.Name == InstName.Und && blkIndex == 0)
{ {
range = new Range(rangeStart, rangeEnd); range = new Range(rangeStart, rangeEnd);
return null; return null;
@@ -75,7 +75,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
/// Map decibel to linear. /// Map decibel to linear.
/// </summary> /// </summary>
/// <param name="db">The decibel value to convert</param> /// <param name="db">The decibel value to convert</param>
/// <returns>Converted linear value/returns> /// <returns>Converted linear value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DecibelToLinear(float db) public static float DecibelToLinear(float db)
{ {
@@ -496,12 +496,6 @@ namespace Ryujinx.Audio.Renderer.Server
return (10090.9f, 3490.9f); return (10090.9f, 3490.9f);
case SampleRateConversionQuality.High: case SampleRateConversionQuality.High:
if (sampleCount == 160)
{
return (9446.36f, 2308.91f);
}
return (12520.85f, 3480.61f);
case SampleRateConversionQuality.Low: case SampleRateConversionQuality.Low:
if (sampleCount == 160) if (sampleCount == 160)
{ {
@@ -1,13 +1,33 @@
using Ryujinx.Common.Utilities;
using System; using System;
namespace Ryujinx.Common.GraphicsDriver namespace Ryujinx.Common.GraphicsDriver
{ {
public static class DriverUtilities public static class DriverUtilities
{ {
private static void AddMesaFlags(string envVar, string newFlags)
{
string existingFlags = Environment.GetEnvironmentVariable(envVar);
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
}
public static void InitDriverConfig(bool oglThreading)
{
if (OperatingSystem.IsLinux())
{
AddMesaFlags("RADV_DEBUG", "nodcc");
}
ToggleOGLThreading(oglThreading);
}
public static void ToggleOGLThreading(bool enabled) public static void ToggleOGLThreading(bool enabled)
{ {
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower()); OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0"); OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
try try
{ {
@@ -46,9 +46,9 @@ namespace Ryujinx.Common.Logging.Targets
return null; return null;
} }
// Clean up old logs, should only keep 3 // Clean up old logs, should only keep 4
FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray(); FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray();
for (int i = 0; i < files.Length - 2; i++) for (int i = 0; i < files.Length - 3; i++)
{ {
try try
{ {
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.Utilities
{
public partial class OsUtils
{
[LibraryImport("libc", SetLastError = true)]
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
public static void SetEnvironmentVariableNoCaching(string key, string value)
{
// Set the value in the cached environment variables, too.
Environment.SetEnvironmentVariable(key, value);
if (!OperatingSystem.IsWindows())
{
int res = setenv(key, value, 1);
Debug.Assert(res != -1);
}
}
}
}
+14 -14
View File
@@ -191,7 +191,7 @@ namespace Ryujinx.Common.Utilities
if (timedSw.Elapsed.TotalSeconds > 0) if (timedSw.Elapsed.TotalSeconds > 0)
{ {
Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec"); Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
} }
if (freeSpaceValid) if (freeSpaceValid)
@@ -219,7 +219,7 @@ namespace Ryujinx.Common.Utilities
private bool CheckPadding(long readSizeB, CancellationToken? cancelToken = null) private bool CheckPadding(long readSizeB, CancellationToken? cancelToken = null)
{ {
long maxReads = readSizeB / XCIFileTrimmer.BufferSize; long maxReads = readSizeB / BufferSize;
long read = 0; long read = 0;
var buffer = new byte[BufferSize]; var buffer = new byte[BufferSize];
@@ -230,12 +230,12 @@ namespace Ryujinx.Common.Utilities
return false; return false;
} }
int bytes = _fileStream.Read(buffer, 0, XCIFileTrimmer.BufferSize); int bytes = _fileStream.Read(buffer, 0, BufferSize);
if (bytes == 0) if (bytes == 0)
break; break;
Log?.Progress(read, maxReads, "Verifying file can be trimmed", false); Log?.Progress(read, maxReads, "Verifying file can be trimmed", false);
if (buffer.Take(bytes).AsParallel().Any(b => b != XCIFileTrimmer.PaddingByte)) if (buffer.Take(bytes).AsParallel().Any(b => b != PaddingByte))
{ {
Log?.Write(LogType.Warn, "Free space is NOT valid"); Log?.Write(LogType.Warn, "Free space is NOT valid");
return false; return false;
@@ -380,7 +380,7 @@ namespace Ryujinx.Common.Utilities
if (timedSw.Elapsed.TotalSeconds > 0) if (timedSw.Elapsed.TotalSeconds > 0)
{ {
Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec"); Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
} }
if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested) if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
@@ -408,13 +408,13 @@ namespace Ryujinx.Common.Utilities
private void WritePadding(FileStream outfileStream, long bytesToWriteB, CancellationToken? cancelToken = null) private void WritePadding(FileStream outfileStream, long bytesToWriteB, CancellationToken? cancelToken = null)
{ {
long bytesLeftToWriteB = bytesToWriteB; long bytesLeftToWriteB = bytesToWriteB;
long writes = bytesLeftToWriteB / XCIFileTrimmer.BufferSize; long writes = bytesLeftToWriteB / BufferSize;
int write = 0; int write = 0;
try try
{ {
var buffer = new byte[BufferSize]; var buffer = new byte[BufferSize];
Array.Fill<byte>(buffer, XCIFileTrimmer.PaddingByte); Array.Fill<byte>(buffer, PaddingByte);
while (bytesLeftToWriteB > 0) while (bytesLeftToWriteB > 0)
{ {
@@ -423,7 +423,7 @@ namespace Ryujinx.Common.Utilities
return; return;
} }
long bytesToWrite = Math.Min(XCIFileTrimmer.BufferSize, bytesLeftToWriteB); long bytesToWrite = Math.Min(BufferSize, bytesLeftToWriteB);
#if !XCI_TRIMMER_READ_ONLY_MODE #if !XCI_TRIMMER_READ_ONLY_MODE
outfileStream.Write(buffer, 0, (int)bytesToWrite); outfileStream.Write(buffer, 0, (int)bytesToWrite);
@@ -504,12 +504,12 @@ namespace Ryujinx.Common.Utilities
} }
// Setup offset // Setup offset
_offsetB = (long)(assumeKeyArea ? XCIFileTrimmer.CartKeyAreaSize : 0); _offsetB = (long)(assumeKeyArea ? CartKeyAreaSize : 0);
// Check header // Check header
Pos = _offsetB + XCIFileTrimmer.HeaderFilePos; Pos = _offsetB + HeaderFilePos;
string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4)); string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4));
if (head != XCIFileTrimmer.HeaderMagicValue) if (head != HeaderMagicValue)
{ {
if (!assumeKeyArea) if (!assumeKeyArea)
{ {
@@ -524,17 +524,17 @@ namespace Ryujinx.Common.Utilities
} }
// Read Cart Size // Read Cart Size
Pos = _offsetB + XCIFileTrimmer.CartSizeFilePos; Pos = _offsetB + CartSizeFilePos;
byte cartSizeId = _binaryReader.ReadByte(); byte cartSizeId = _binaryReader.ReadByte();
if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB)) if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB))
{ {
Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})"); Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})");
return false; return false;
} }
_cartSizeB = cartSizeNGB * XCIFileTrimmer.CartSizeMBinFormattedGB * XCIFileTrimmer.BytesInAMegabyte; _cartSizeB = cartSizeNGB * CartSizeMBinFormattedGB * BytesInAMegabyte;
// Read data size // Read data size
Pos = _offsetB + XCIFileTrimmer.DataSizeFilePos; Pos = _offsetB + DataSizeFilePos;
long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0); long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0);
_dataSizeB = RecordsToByte(records); _dataSizeB = RecordsToByte(records);
@@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
-1
View File
@@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@@ -8,7 +8,6 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Cpu.Jit namespace Ryujinx.Cpu.Jit
@@ -703,8 +703,6 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
switch ((ArmShiftType)sType) switch ((ArmShiftType)sType)
{ {
case ArmShiftType.Lsr: case ArmShiftType.Lsr:
shift = 32;
break;
case ArmShiftType.Asr: case ArmShiftType.Asr:
shift = 32; shift = 32;
break; break;
-12
View File
@@ -50,10 +50,6 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsViewportSwizzle; public readonly bool SupportsViewportSwizzle;
public readonly bool SupportsIndirectParameters; public readonly bool SupportsIndirectParameters;
public readonly bool SupportsDepthClipControl; public readonly bool SupportsDepthClipControl;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsExtendedDynamicState2;
public readonly bool SupportsLogicOpDynamicState;
public readonly bool SupportsPatchControlPointsDynamicState;
public readonly int UniformBufferSetIndex; public readonly int UniformBufferSetIndex;
public readonly int StorageBufferSetIndex; public readonly int StorageBufferSetIndex;
@@ -122,10 +118,6 @@ namespace Ryujinx.Graphics.GAL
bool supportsViewportSwizzle, bool supportsViewportSwizzle,
bool supportsIndirectParameters, bool supportsIndirectParameters,
bool supportsDepthClipControl, bool supportsDepthClipControl,
bool supportsExtendedDynamicState,
bool supportsExtendedDynamicState2,
bool supportsLogicOpDynamicState,
bool supportsPatchControlPointsDynamicState,
int uniformBufferSetIndex, int uniformBufferSetIndex,
int storageBufferSetIndex, int storageBufferSetIndex,
int textureSetIndex, int textureSetIndex,
@@ -188,10 +180,6 @@ namespace Ryujinx.Graphics.GAL
SupportsViewportSwizzle = supportsViewportSwizzle; SupportsViewportSwizzle = supportsViewportSwizzle;
SupportsIndirectParameters = supportsIndirectParameters; SupportsIndirectParameters = supportsIndirectParameters;
SupportsDepthClipControl = supportsDepthClipControl; SupportsDepthClipControl = supportsDepthClipControl;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
SupportsLogicOpDynamicState = supportsLogicOpDynamicState;
SupportsPatchControlPointsDynamicState = supportsPatchControlPointsDynamicState;
UniformBufferSetIndex = uniformBufferSetIndex; UniformBufferSetIndex = uniformBufferSetIndex;
StorageBufferSetIndex = storageBufferSetIndex; StorageBufferSetIndex = storageBufferSetIndex;
TextureSetIndex = textureSetIndex; TextureSetIndex = textureSetIndex;
-1
View File
@@ -2,7 +2,6 @@ namespace Ryujinx.Graphics.GAL
{ {
public enum Face public enum Face
{ {
None = 0,
Front = 0x404, Front = 0x404,
Back = 0x405, Back = 0x405,
FrontAndBack = 0x408, FrontAndBack = 0x408,
-4
View File
@@ -275,8 +275,6 @@ namespace Ryujinx.Graphics.GAL
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
return 4; return 4;
case Format.D32FloatS8Uint: case Format.D32FloatS8Uint:
return 8;
case Format.Bc1RgbaUnorm: case Format.Bc1RgbaUnorm:
case Format.Bc1RgbaSrgb: case Format.Bc1RgbaSrgb:
return 8; return 8;
@@ -303,8 +301,6 @@ namespace Ryujinx.Graphics.GAL
case Format.Etc2RgbaUnorm: case Format.Etc2RgbaUnorm:
case Format.Etc2RgbaSrgb: case Format.Etc2RgbaSrgb:
return 16;
case Format.Astc4x4Unorm: case Format.Astc4x4Unorm:
case Format.Astc5x4Unorm: case Format.Astc5x4Unorm:
case Format.Astc5x5Unorm: case Format.Astc5x5Unorm:
+7 -7
View File
@@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.GAL
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
void SetDepthClamp(bool clamp); void SetDepthClamp(bool clamp);
void SetDepthMode(DepthMode mode); void SetDepthMode(DepthMode mode);
void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true); void SetDepthTest(DepthTestDescriptor depthTest);
void SetFaceCulling(Face face); void SetFaceCulling(bool enable, Face face);
void SetFrontFace(FrontFace frontFace); void SetFrontFace(FrontFace frontFace);
@@ -75,16 +75,16 @@ namespace Ryujinx.Graphics.GAL
void SetPrimitiveRestart(bool enable, int index); void SetPrimitiveRestart(bool enable, int index);
void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true); void SetPrimitiveTopology(PrimitiveTopology topology);
void SetProgram(IProgram program, bool signalChange = true); void SetProgram(IProgram program);
void SetRasterizerDiscard(bool discard); void SetRasterizerDiscard(bool discard);
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true); void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
void SetRenderTargets(ITexture[] colors, ITexture depthStencil); void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true); void SetScissors(ReadOnlySpan<Rectangle<int>> regions);
void SetStencilTest(StencilTestDescriptor stencilTest); void SetStencilTest(StencilTestDescriptor stencilTest);
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.GAL
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs); void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers); void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true); void SetViewports(ReadOnlySpan<Viewport> viewports);
void TextureBarrier(); void TextureBarrier();
void TextureBarrierTiled(); void TextureBarrierTiled();
@@ -3,16 +3,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand> struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand>
{ {
public readonly CommandType CommandType => CommandType.SetFaceCulling; public readonly CommandType CommandType => CommandType.SetFaceCulling;
private bool _enable;
private Face _face; private Face _face;
public void Set(Face face) public void Set(bool enable, Face face)
{ {
_enable = enable;
_face = face; _face = face;
} }
public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.SetFaceCulling(command._face); renderer.Pipeline.SetFaceCulling(command._enable, command._face);
} }
} }
} }
@@ -159,15 +159,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true) public void SetDepthTest(DepthTestDescriptor depthTest)
{ {
_renderer.New<SetDepthTestCommand>().Set(depthTest); _renderer.New<SetDepthTestCommand>().Set(depthTest);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetFaceCulling(Face face) public void SetFaceCulling(bool enable, Face face)
{ {
_renderer.New<SetFaceCullingCommand>().Set(face); _renderer.New<SetFaceCullingCommand>().Set(enable, face);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
@@ -243,13 +243,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true) public void SetPrimitiveTopology(PrimitiveTopology topology)
{ {
_renderer.New<SetPrimitiveTopologyCommand>().Set(topology); _renderer.New<SetPrimitiveTopologyCommand>().Set(topology);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetProgram(IProgram program, bool signalChange = true) public void SetProgram(IProgram program)
{ {
_renderer.New<SetProgramCommand>().Set(Ref(program)); _renderer.New<SetProgramCommand>().Set(Ref(program));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
{ {
_renderer.New<SetRenderTargetColorMasksCommand>().Set(_renderer.CopySpan(componentMask)); _renderer.New<SetRenderTargetColorMasksCommand>().Set(_renderer.CopySpan(componentMask));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors, bool signalChange = true) public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors)
{ {
_renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors)); _renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true) public void SetViewports(ReadOnlySpan<Viewport> viewports)
{ {
_renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports)); _renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -51,12 +51,11 @@ namespace Ryujinx.Graphics.GAL
public StencilTestDescriptor StencilTest; public StencilTestDescriptor StencilTest;
public FrontFace FrontFace; public FrontFace FrontFace;
public Face CullMode; public Face CullMode;
public bool CullEnable;
public PolygonModeMask BiasEnable; public PolygonModeMask BiasEnable;
public bool AlphaToCoverageEnable; public float LineWidth;
public bool AlphaToOneEnable;
// TODO: Polygon mode. // TODO: Polygon mode.
public bool DepthClampEnable; public bool DepthClampEnable;
public bool RasterizerDiscard; public bool RasterizerDiscard;
@@ -64,9 +63,6 @@ namespace Ryujinx.Graphics.GAL
public bool PrimitiveRestartEnable; public bool PrimitiveRestartEnable;
public uint PatchControlPoints; public uint PatchControlPoints;
public float DepthBiasUnits;
public float DepthBiasFactor;
public DepthMode DepthMode; public DepthMode DepthMode;
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
@@ -506,7 +506,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
maxDrawCount, maxDrawCount,
stride, stride,
indexCount, indexCount,
Threed.IndirectDrawType.DrawIndexedIndirectCount); IndirectDrawType.DrawIndexedIndirectCount);
} }
/// <summary> /// <summary>
@@ -1,6 +1,5 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
@@ -853,9 +853,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0); enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
_pipeline.BiasEnable = enables; _pipeline.BiasEnable = enables;
_pipeline.DepthBiasUnits = units / 2f;
_pipeline.DepthBiasFactor = factor;
_context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp); _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
} }
@@ -1028,6 +1025,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
float width = _state.State.LineWidthSmooth; float width = _state.State.LineWidthSmooth;
bool smooth = _state.State.LineSmoothEnable; bool smooth = _state.State.LineSmoothEnable;
_pipeline.LineWidth = width;
_context.Renderer.Pipeline.SetLineParameters(width, smooth); _context.Renderer.Pipeline.SetLineParameters(width, smooth);
} }
@@ -1197,16 +1195,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var yControl = _state.State.YControl; var yControl = _state.State.YControl;
var face = _state.State.FaceState; var face = _state.State.FaceState;
if (face.CullEnable) _pipeline.CullEnable = face.CullEnable;
{
_pipeline.CullMode = face.CullFace; _pipeline.CullMode = face.CullFace;
_context.Renderer.Pipeline.SetFaceCulling(face.CullFace); _context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
}
else
{
_pipeline.CullMode = Face.None;
_context.Renderer.Pipeline.SetFaceCulling(Face.None);
}
UpdateFrontFace(yControl, face.FrontFace); UpdateFrontFace(yControl, face.FrontFace);
} }
@@ -1396,8 +1387,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0; bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0;
bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0; bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0;
_pipeline.AlphaToCoverageEnable = alphaToCoverageEnable;
_pipeline.AlphaToOneEnable = alphaToOneEnable;
_context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor( _context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor(
alphaToCoverageEnable, alphaToCoverageEnable,
_state.State.AlphaToCoverageDitherEnable, _state.State.AlphaToCoverageDitherEnable,
@@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return (AddressMode)(Word0 & 7); return (AddressMode)(Word0 & 7);
} }
// <summary> /// <summary>
/// Unpacks the texture wrap mode along the Y axis. /// Unpacks the texture wrap mode along the Y axis.
/// </summary> /// </summary>
/// <returns>The texture wrap mode enum</returns> /// <returns>The texture wrap mode enum</returns>
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return (AddressMode)((Word0 >> 3) & 7); return (AddressMode)((Word0 >> 3) & 7);
} }
// <summary> /// <summary>
/// Unpacks the texture wrap mode along the Z axis. /// Unpacks the texture wrap mode along the Z axis.
/// </summary> /// </summary>
/// <returns>The texture wrap mode enum</returns> /// <returns>The texture wrap mode enum</returns>
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texturePool">The current texture pool</param> /// <param name="texturePool">The current texture pool</param>
/// <param name="samplerPool">The current sampler pool</param> /// <param name="samplerPool">The current sampler pool</param>
/// <param name="stage">The shader stage using the textures to be bound</param> /// <param name="stage">The shader stage using the textures to be bound</param>
/// <param name="stageIndex">The stage number of the specified shader stage</param /// <param name="stageIndex">The stage number of the specified shader stage</param>
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param> /// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
/// <param name="specState">Specialization state for the bound shader</param> /// <param name="specState">Specialization state for the bound shader</param>
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns> /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
@@ -723,7 +723,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Checks if aliasing of two formats that would normally be considered incompatible be allowed, /// Checks if aliasing of two formats that would normally be considered incompatible be allowed,
/// using copy dependencies. /// using copy dependencies.
/// </summary> /// </summary>
/// <param name="lhsFormat">Format information of the first texture</param /// <param name="lhsFormat">Format information of the first texture</param>
/// <param name="rhsFormat">Format information of the second texture</param> /// <param name="rhsFormat">Format information of the second texture</param>
/// <returns>True if aliasing should be allowed, false otherwise</returns> /// <returns>True if aliasing should be allowed, false otherwise</returns>
private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat) private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat)
@@ -747,7 +747,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Check if the target of the first texture view information is compatible with the target of the second texture view information. /// Check if the target of the first texture view information is compatible with the target of the second texture view information.
/// This follows the host API target compatibility rules. /// This follows the host API target compatibility rules.
/// </summary> /// </summary>
/// <param name="lhs">Texture information of the texture view</param /// <param name="lhs">Texture information of the texture view</param>
/// <param name="rhs">Texture information of the texture view</param> /// <param name="rhs">Texture information of the texture view</param>
/// <param name="caps">Host GPU capabilities</param> /// <param name="caps">Host GPU capabilities</param>
/// <returns>True if the targets are compatible, false otherwise</returns> /// <returns>True if the targets are compatible, false otherwise</returns>
@@ -46,7 +46,6 @@ namespace Ryujinx.Graphics.Gpu.Image
case TextureTarget.Texture1D: case TextureTarget.Texture1D:
return Target.Texture1D; return Target.Texture1D;
case TextureTarget.Texture2D: case TextureTarget.Texture2D:
return Target.Texture2D;
case TextureTarget.Texture2DRect: case TextureTarget.Texture2DRect:
return Target.Texture2D; return Target.Texture2D;
case TextureTarget.Texture3D: case TextureTarget.Texture3D:
@@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Sets whether the format of a given render target is a BGRA format. /// Sets whether the format of a given render target is a BGRA format.
/// </summary> /// </summary>
/// <param name="index">Render target index</param> /// <param name="index">Render target index</param>
/// <param name="isBgra">True if the format is BGRA< false otherwise</param> /// <param name="isBgra">True if the format is BGRA, false otherwise</param>
public void SetRenderTargetIsBgra(int index, bool isBgra) public void SetRenderTargetIsBgra(int index, bool isBgra)
{ {
bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra; bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra;
@@ -2,7 +2,6 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine; using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Linq; using System.Linq;
namespace Ryujinx.Graphics.Gpu.Shader namespace Ryujinx.Graphics.Gpu.Shader
@@ -22,8 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly CancellationToken _cancellationToken; private readonly CancellationToken _cancellationToken;
private readonly Action<ShaderCacheState, int, int> _stateChangeCallback; private readonly Action<ShaderCacheState, int, int> _stateChangeCallback;
private readonly HashSet<ProgramPipelineState> _pipelineStateSet = new();
/// <summary> /// <summary>
/// Indicates if the cache should be loaded. /// Indicates if the cache should be loaded.
/// </summary> /// </summary>
@@ -234,7 +232,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
for (int index = 0; index < ThreadCount; index++) for (int index = 0; index < ThreadCount; index++)
{ {
workThreads[index] = new Thread(ProcessAsyncQueue) { Name = $"GPU.AsyncTranslationThread.{index}", }; workThreads[index] = new Thread(ProcessAsyncQueue)
{
Name = $"GPU.AsyncTranslationThread.{index}",
};
} }
int programCount = _hostStorage.GetProgramCount(); int programCount = _hostStorage.GetProgramCount();
@@ -304,8 +305,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
using var streams = _hostStorage.GetOutputStreams(_context); using var streams = _hostStorage.GetOutputStreams(_context);
int packagedShaders = 0; int packagedShaders = 0;
ProgramPipelineState currentPipelineState = default;
foreach (var kv in _programList) foreach (var kv in _programList)
{ {
if (!Active) if (!Active)
@@ -315,53 +314,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
(CachedShaderProgram program, byte[] binaryCode) = kv.Value; (CachedShaderProgram program, byte[] binaryCode) = kv.Value;
if (program.SpecializationState.PipelineState.HasValue && _context.Capabilities.SupportsExtendedDynamicState)
{
currentPipelineState = program.SpecializationState.PipelineState.Value;
if (_context.Capabilities.SupportsExtendedDynamicState)
{
currentPipelineState.StencilTest = default;
currentPipelineState.CullMode = 0;
currentPipelineState.FrontFace = 0;
currentPipelineState.DepthTest = default;
currentPipelineState.Topology = ConvertToClass(currentPipelineState.Topology);
}
if (_context.Capabilities.SupportsExtendedDynamicState2)
{
currentPipelineState.PrimitiveRestartEnable = false;
currentPipelineState.BiasEnable = 0;
currentPipelineState.RasterizerDiscard = false;
}
if (_context.Capabilities.SupportsLogicOpDynamicState)
{
currentPipelineState.LogicOp = 0;
}
if (_context.Capabilities.SupportsPatchControlPointsDynamicState)
{
currentPipelineState.PatchControlPoints = 0;
}
}
if (!_pipelineStateSet.Contains(currentPipelineState) ||
!_context.Capabilities.SupportsExtendedDynamicState ||
!program.SpecializationState.PipelineState.HasValue)
{
_hostStorage.AddShader(_context, program, binaryCode, streams); _hostStorage.AddShader(_context, program, binaryCode, streams);
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count); _stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
}
if (_context.Capabilities.SupportsExtendedDynamicState) Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");
{
_pipelineStateSet.Add(currentPipelineState);
}
}
}
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {packagedShaders} shaders successfully.");
} }
else else
{ {
@@ -385,29 +343,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount); _stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount);
} }
private PrimitiveTopology ConvertToClass(PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.Points => PrimitiveTopology.Points,
PrimitiveTopology.Lines or
PrimitiveTopology.LineStrip or
PrimitiveTopology.LinesAdjacency or
PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.Lines,
PrimitiveTopology.Triangles or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan or
PrimitiveTopology.TrianglesAdjacency or
PrimitiveTopology.TriangleStripAdjacency or
PrimitiveTopology.Polygon => PrimitiveTopology.TriangleStrip,
PrimitiveTopology.Patches => PrimitiveTopology.Patches,
PrimitiveTopology.Quads => PrimitiveTopology.Quads,
PrimitiveTopology.QuadStrip => PrimitiveTopology.QuadStrip,
PrimitiveTopology.LineLoop => PrimitiveTopology.LineLoop,
_ => PrimitiveTopology.TriangleStrip,
};
}
/// <summary> /// <summary>
/// Enqueues a host program for compilation. /// Enqueues a host program for compilation.
/// </summary> /// </summary>
@@ -136,8 +136,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
ulong frameSize = (ulong)(1 + (useHighbitdepth ? 1 : 0)) * (yplaneSize + (2 * uvplaneSize)); ulong frameSize = (ulong)(1 + (useHighbitdepth ? 1 : 0)) * (yplaneSize + (2 * uvplaneSize));
ArrayPtr<byte> buf = ArrayPtr<byte>.Null;
// frame_size is stored in buffer_alloc_sz, which is an int. If it won't // frame_size is stored in buffer_alloc_sz, which is an int. If it won't
// fit, fail early. // fit, fail early.
if (frameSize > int.MaxValue) if (frameSize > int.MaxValue)
@@ -212,7 +210,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
SubsamplingX = ssX; SubsamplingX = ssX;
SubsamplingY = ssY; SubsamplingY = ssY;
buf = BufferAlloc; ArrayPtr<byte> buf = BufferAlloc;
if (useHighbitdepth) if (useHighbitdepth)
{ {
// Store uint16 addresses when using 16bit framebuffers // Store uint16 addresses when using 16bit framebuffers
@@ -277,11 +277,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public CodecErr Decode(MemoryAllocator allocator, ArrayPtr<byte> data) public CodecErr Decode(MemoryAllocator allocator, ArrayPtr<byte> data)
{ {
ArrayPtr<byte> dataStart = data; ArrayPtr<byte> dataStart = data;
CodecErr res;
Array8<uint> frameSizes = new(); Array8<uint> frameSizes = new();
int frameCount = 0; int frameCount = 0;
res = Types.Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out frameCount); CodecErr res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out frameCount);
if (res != CodecErr.Ok) if (res != CodecErr.Ok)
{ {
return res; return res;
@@ -322,7 +321,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// Account for suboptimal termination by the encoder. // Account for suboptimal termination by the encoder.
while (dataStart.Length != 0) while (dataStart.Length != 0)
{ {
byte marker = Types.Decoder.ReadMarker(dataStart); byte marker = Decoder.ReadMarker(dataStart);
if (marker != 0) if (marker != 0)
{ {
break; break;
@@ -191,10 +191,6 @@ namespace Ryujinx.Graphics.OpenGL
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
supportsDepthClipControl: true, supportsDepthClipControl: true,
supportsExtendedDynamicState: false,
supportsExtendedDynamicState2: false,
supportsLogicOpDynamicState: false,
supportsPatchControlPointsDynamicState: false,
uniformBufferSetIndex: 0, uniformBufferSetIndex: 0,
storageBufferSetIndex: 1, storageBufferSetIndex: 1,
textureSetIndex: 2, textureSetIndex: 2,
+9 -9
View File
@@ -898,7 +898,7 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true) public void SetDepthTest(DepthTestDescriptor depthTest)
{ {
if (depthTest.TestEnable) if (depthTest.TestEnable)
{ {
@@ -915,11 +915,11 @@ namespace Ryujinx.Graphics.OpenGL
_depthTestEnable = depthTest.TestEnable; _depthTestEnable = depthTest.TestEnable;
} }
public void SetFaceCulling(Face face) public void SetFaceCulling(bool enable, Face face)
{ {
_cullEnable = face != Face.None; _cullEnable = enable;
if (!_cullEnable) if (!enable)
{ {
GL.Disable(EnableCap.CullFace); GL.Disable(EnableCap.CullFace);
return; return;
@@ -1107,12 +1107,12 @@ namespace Ryujinx.Graphics.OpenGL
GL.Enable(EnableCap.PrimitiveRestart); GL.Enable(EnableCap.PrimitiveRestart);
} }
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true) public void SetPrimitiveTopology(PrimitiveTopology topology)
{ {
_primitiveType = topology.Convert(); _primitiveType = topology.Convert();
} }
public void SetProgram(IProgram program, bool signalChange = true) public void SetProgram(IProgram program)
{ {
Program prg = (Program)program; Program prg = (Program)program;
@@ -1154,7 +1154,7 @@ namespace Ryujinx.Graphics.OpenGL
_rasterizerDiscard = discard; _rasterizerDiscard = discard;
} }
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks, bool signalChange = true) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
{ {
_componentMasks = 0; _componentMasks = 0;
@@ -1195,7 +1195,7 @@ namespace Ryujinx.Graphics.OpenGL
_framebuffer.SetDrawBuffers(colors.Length); _framebuffer.SetDrawBuffers(colors.Length);
} }
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true) public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
{ {
int count = Math.Min(regions.Length, Constants.MaxViewports); int count = Math.Min(regions.Length, Constants.MaxViewports);
@@ -1388,7 +1388,7 @@ namespace Ryujinx.Graphics.OpenGL
_vertexArray.SetVertexBuffers(vertexBuffers); _vertexArray.SetVertexBuffers(vertexBuffers);
} }
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true) public void SetViewports(ReadOnlySpan<Viewport> viewports)
{ {
Array.Resize(ref _viewportArray, viewports.Length * 4); Array.Resize(ref _viewportArray, viewports.Length * 4);
Array.Resize(ref _depthRangeArray, viewports.Length * 2); Array.Resize(ref _depthRangeArray, viewports.Length * 2);
@@ -9,6 +9,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{ {
public static void RunPass(TransformContext context) public static void RunPass(TransformContext context)
{ {
for (int blkIndex = 0; blkIndex < context.Blocks.Length; blkIndex++)
{
XmadOptimizer.RunPass(context.Blocks[blkIndex]);
}
RunOptimizationPasses(context.Blocks, context.ResourceManager); RunOptimizationPasses(context.Blocks, context.ResourceManager);
// TODO: Some of those are not optimizations and shouldn't be here. // TODO: Some of those are not optimizations and shouldn't be here.
@@ -355,7 +360,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
operation.TurnIntoCopy(attrMulLhs); operation.TurnIntoCopy(attrMulLhs);
} }
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode) public static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
{ {
// Remove a node from the nodes list, and also remove itself // Remove a node from the nodes list, and also remove itself
// from all the use lists on the operands that this node uses. // from all the use lists on the operands that this node uses.
@@ -0,0 +1,342 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class XmadOptimizer
{
public static void RunPass(BasicBlock block)
{
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
{
if (!(node.Value is Operation operation))
{
continue;
}
if (TryMatchXmadPattern(operation, out Operand x, out Operand y, out Operand addend))
{
LinkedListNode<INode> nextNode;
if (addend != null)
{
Operand temp = OperandHelper.Local();
nextNode = block.Operations.AddAfter(node, new Operation(Instruction.Multiply, temp, x, y));
nextNode = block.Operations.AddAfter(nextNode, new Operation(Instruction.Add, operation.Dest, temp, addend));
}
else
{
nextNode = block.Operations.AddAfter(node, new Operation(Instruction.Multiply, operation.Dest, x, y));
}
Optimizer.RemoveNode(block, node);
node = nextNode;
}
}
}
private static bool TryMatchXmadPattern(Operation operation, out Operand x, out Operand y, out Operand addend)
{
return TryMatchXmad32x32Pattern(operation, out x, out y, out addend) ||
TryMatchXmad32x16Pattern(operation, out x, out y, out addend);
}
private static bool TryMatchXmad32x32Pattern(Operation operation, out Operand x, out Operand y, out Operand addend)
{
x = null;
y = null;
addend = null;
if (operation.Inst != Instruction.Add)
{
return false;
}
Operand src1 = operation.GetSource(0);
Operand src2 = operation.GetSource(1);
if (!(src2.AsgOp is Operation addOp) || addOp.Inst != Instruction.Add)
{
return false;
}
Operand lowTimesLowResult = GetCopySource(addOp.GetSource(0));
if (!(lowTimesLowResult.AsgOp is Operation lowTimesLowOp))
{
return false;
}
if (!TryMatchLowTimesLow(lowTimesLowOp, out x, out y, out addend))
{
return false;
}
Operand lowTimesHighResult = GetCopySource(GetShifted16Source(addOp.GetSource(1), Instruction.ShiftLeft));
if (!(lowTimesHighResult.AsgOp is Operation lowTimesHighOp))
{
return false;
}
if (!TryMatchLowTimesHigh(lowTimesHighOp, x, y))
{
return false;
}
if (!(src1.AsgOp is Operation highTimesHighOp))
{
return false;
}
if (!TryMatchHighTimesHigh(highTimesHighOp, x, lowTimesHighResult))
{
return false;
}
return true;
}
private static bool TryMatchXmad32x16Pattern(Operation operation, out Operand x, out Operand y, out Operand addend)
{
x = null;
y = null;
addend = null;
if (operation.Inst != Instruction.Add)
{
return false;
}
Operand src1 = operation.GetSource(0);
Operand src2 = operation.GetSource(1);
Operand lowTimesLowResult = GetCopySource(src2);
if (!(lowTimesLowResult.AsgOp is Operation lowTimesLowOp))
{
return false;
}
if (!TryMatchLowTimesLow(lowTimesLowOp, out x, out y, out addend))
{
return false;
}
Operand highTimesLowResult = src1;
if (!(highTimesLowResult.AsgOp is Operation highTimesLowOp))
{
return false;
}
if (!TryMatchHighTimesLow(highTimesLowOp, x, y))
{
return false;
}
return y.Type == OperandType.Constant && (ushort)y.Value == y.Value;
}
private static bool TryMatchLowTimesLow(Operation operation, out Operand x, out Operand y, out Operand addend)
{
// x = x & 0xFFFF
// y = y & 0xFFFF
// lowTimesLow = x * y
x = null;
y = null;
addend = null;
if (operation.Inst == Instruction.Add)
{
if (!(operation.GetSource(0).AsgOp is Operation mulOp))
{
return false;
}
addend = operation.GetSource(1);
operation = mulOp;
}
if (operation.Inst != Instruction.Multiply)
{
return false;
}
Operand src1 = GetMasked16Source(operation.GetSource(0));
Operand src2 = GetMasked16Source(operation.GetSource(1));
if (src1 == null || src2 == null)
{
return false;
}
x = src1;
y = src2;
return true;
}
private static bool TryMatchLowTimesHigh(Operation operation, Operand x, Operand y)
{
// xLow = x & 0xFFFF
// yHigh = y >> 16
// lowTimesHigh = xLow * yHigh
// result = (lowTimesHigh & 0xFFFF) | (y << 16)
if (operation.Inst != Instruction.BitwiseOr)
{
return false;
}
Operand mulResult = GetMasked16Source(operation.GetSource(0));
if (mulResult == null)
{
return false;
}
mulResult = GetCopySource(mulResult);
if (!(mulResult.AsgOp is Operation mulOp) || mulOp.Inst != Instruction.Multiply)
{
return false;
}
if (GetMasked16Source(mulOp.GetSource(0)) != x)
{
return false;
}
if (GetShifted16Source(mulOp.GetSource(1), Instruction.ShiftRightU32) != y)
{
return false;
}
if (GetShifted16Source(operation.GetSource(1), Instruction.ShiftLeft) != y)
{
return false;
}
return true;
}
private static bool TryMatchHighTimesLow(Operation operation, Operand x, Operand y)
{
// xHigh = x >> 16
// yLow = y & 0xFFFF
// highTimesLow = xHigh * yLow
// result = highTimesLow << 16
if (operation.Inst != Instruction.ShiftLeft || !IsConst(operation.GetSource(1), 16))
{
return false;
}
Operand mulResult = operation.GetSource(0);
if (!(mulResult.AsgOp is Operation mulOp) || mulOp.Inst != Instruction.Multiply)
{
return false;
}
if (GetShifted16Source(mulOp.GetSource(0), Instruction.ShiftRightU32) != x)
{
return false;
}
Operand src2 = GetMasked16Source(mulOp.GetSource(1));
if (src2.Type != y.Type || src2.Value != y.Value)
{
return false;
}
return true;
}
private static bool TryMatchHighTimesHigh(Operation operation, Operand x, Operand lowTimesHighResult)
{
// xHigh = x >> 16
// lowTimesHighResultHigh = lowTimesHighResult >> 16
// highTimesHigh = xHigh * lowTimesHighResultHigh
// result = highTimesHigh << 16
if (operation.Inst != Instruction.ShiftLeft || !IsConst(operation.GetSource(1), 16))
{
return false;
}
Operand mulResult = operation.GetSource(0);
if (!(mulResult.AsgOp is Operation mulOp) || mulOp.Inst != Instruction.Multiply)
{
return false;
}
if (GetShifted16Source(mulOp.GetSource(0), Instruction.ShiftRightU32) != x)
{
return false;
}
if (GetCopySource(GetShifted16Source(mulOp.GetSource(1), Instruction.ShiftRightU32)) != lowTimesHighResult)
{
return false;
}
return true;
}
private static Operand GetMasked16Source(Operand value)
{
if (!(value.AsgOp is Operation maskOp))
{
return null;
}
if (maskOp.Inst != Instruction.BitwiseAnd || !IsConst(maskOp.GetSource(1), ushort.MaxValue))
{
return null;
}
return maskOp.GetSource(0);
}
private static Operand GetShifted16Source(Operand value, Instruction shiftInst)
{
if (!(value.AsgOp is Operation shiftOp))
{
return null;
}
if (shiftOp.Inst != shiftInst || !IsConst(shiftOp.GetSource(1), 16))
{
return null;
}
return shiftOp.GetSource(0);
}
private static Operand GetCopySource(Operand value)
{
while (value.AsgOp is Operation operation && IsCopy(operation))
{
value = operation.GetSource(0);
}
return value;
}
private static bool IsCopy(Operation operation)
{
return operation.Inst == Instruction.Copy || (operation.Inst == Instruction.Add && IsConst(operation.GetSource(1), 0));
}
private static bool IsConst(Operand operand, int value)
{
return operand.Type == OperandType.Constant && operand.Value == value;
}
}
}
+26 -13
View File
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
CommandBuffer CommandBuffer
} }
private bool _feedbackLoopActive;
private PipelineStageFlags _incoherentBufferWriteStages; private PipelineStageFlags _incoherentBufferWriteStages;
private PipelineStageFlags _incoherentTextureWriteStages; private PipelineStageFlags _incoherentTextureWriteStages;
private PipelineStageFlags _extraStages; private PipelineStageFlags _extraStages;
private IncoherentBarrierType _queuedIncoherentBarrier; private IncoherentBarrierType _queuedIncoherentBarrier;
private bool _queuedFeedbackLoopBarrier;
public BarrierBatch(VulkanRenderer gd) public BarrierBatch(VulkanRenderer gd)
{ {
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
stages |= PipelineStageFlags.TransformFeedbackBitExt; 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); return (access, stages);
} }
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
} }
_queuedIncoherentBarrier = IncoherentBarrierType.None; _queuedIncoherentBarrier = IncoherentBarrierType.None;
_queuedFeedbackLoopBarrier = false;
} }
else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
{
// Feedback loop barrier.
MemoryBarrier barrier = new MemoryBarrier()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = AccessFlags.ShaderWriteBit,
DstAccessMask = AccessFlags.ShaderReadBit
};
QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
_queuedFeedbackLoopBarrier = false;
}
_feedbackLoopActive = false;
} }
} }
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{ {
Flush(cbs, null, inRenderPass, rpHolder, endRenderPass); Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
} }
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{ {
if (program != null) if (program != null)
{ {
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages; _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
} }
_feedbackLoopActive |= feedbackLoopActive;
FlushMemoryBarrier(program, inRenderPass); FlushMemoryBarrier(program, inRenderPass);
if (!inRenderPass && rpHolder != null) if (!inRenderPass && rpHolder != null)
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_queuedIncoherentBarrier = type; _queuedIncoherentBarrier = type;
} }
_queuedFeedbackLoopBarrier = true;
} }
public void QueueTextureBarrier() public void QueueTextureBarrier()
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using CompareOp = Ryujinx.Graphics.GAL.CompareOp; using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
@@ -41,15 +42,15 @@ namespace Ryujinx.Graphics.Vulkan
private record struct TextureRef private record struct TextureRef
{ {
public ShaderStage Stage; public ShaderStage Stage;
public TextureStorage Storage; public TextureView View;
public Auto<DisposableImageView> View; public Auto<DisposableImageView> ImageView;
public Auto<DisposableSampler> Sampler; public Auto<DisposableSampler> Sampler;
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler) public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
{ {
Stage = stage; Stage = stage;
Storage = storage;
View = view; View = view;
ImageView = imageView;
Sampler = sampler; Sampler = sampler;
} }
} }
@@ -57,14 +58,14 @@ namespace Ryujinx.Graphics.Vulkan
private record struct ImageRef private record struct ImageRef
{ {
public ShaderStage Stage; public ShaderStage Stage;
public TextureStorage Storage; public TextureView View;
public Auto<DisposableImageView> View; public Auto<DisposableImageView> ImageView;
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view) public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
{ {
Stage = stage; Stage = stage;
Storage = storage;
View = view; View = view;
ImageView = imageView;
} }
} }
@@ -122,6 +123,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly TextureView _dummyTexture; private readonly TextureView _dummyTexture;
private readonly SamplerHolder _dummySampler; private readonly SamplerHolder _dummySampler;
public List<TextureView> FeedbackLoopHazards { get; private set; }
public DescriptorSetUpdater(VulkanRenderer gd, Device device) public DescriptorSetUpdater(VulkanRenderer gd, Device device)
{ {
_gd = gd; _gd = gd;
@@ -206,10 +209,15 @@ namespace Ryujinx.Graphics.Vulkan
_templateUpdater = new(); _templateUpdater = new();
} }
public void Initialize() public void Initialize(bool isMainPipeline)
{ {
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4); MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
_dummyTexture.SetData(dummyTextureData); _dummyTexture.SetData(dummyTextureData);
if (isMainPipeline)
{
FeedbackLoopHazards = new();
}
} }
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size) private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
@@ -272,6 +280,18 @@ namespace Ryujinx.Graphics.Vulkan
public void InsertBindingBarriers(CommandBufferScoped cbs) public void InsertBindingBarriers(CommandBufferScoped cbs)
{ {
if ((FeedbackLoopHazards?.Count ?? 0) > 0)
{
// Clear existing hazards - they will be rebuilt.
foreach (TextureView hazard in FeedbackLoopHazards)
{
hazard.DecrementHazardUses();
}
FeedbackLoopHazards.Clear();
}
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex]) foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
{ {
if (segment.Type == ResourceType.TextureAndSampler) if (segment.Type == ResourceType.TextureAndSampler)
@@ -281,7 +301,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < segment.Count; i++) for (int i = 0; i < segment.Count; i++)
{ {
ref var texture = ref _textureRefs[segment.Binding + i]; ref var texture = ref _textureRefs[segment.Binding + i];
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags()); texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
} }
} }
else else
@@ -302,7 +322,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < segment.Count; i++) for (int i = 0; i < segment.Count; i++)
{ {
ref var image = ref _imageRefs[segment.Binding + i]; ref var image = ref _imageRefs[segment.Binding + i];
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags()); image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
} }
} }
else else
@@ -376,9 +396,12 @@ namespace Ryujinx.Graphics.Vulkan
} }
else if (image is TextureView view) else if (image is TextureView view)
{ {
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); ref ImageRef iRef = ref _imageRefs[binding];
_imageRefs[binding]= new(stage, view.Storage, view.GetIdentityImageView()); iRef.View?.ClearUsage(FeedbackLoopHazards);
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
iRef = new(stage, view, view.GetIdentityImageView());
} }
else else
{ {
@@ -476,9 +499,12 @@ namespace Ryujinx.Graphics.Vulkan
} }
else if (texture is TextureView view) else if (texture is TextureView view)
{ {
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); ref TextureRef iRef = ref _textureRefs[binding];
_textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); iRef.View?.ClearUsage(FeedbackLoopHazards);
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
} }
else else
{ {
@@ -500,7 +526,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
SignalDirty(DirtyFlags.Texture); SignalDirty(DirtyFlags.Texture);
} }
@@ -826,7 +852,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var texture = ref textures[i]; ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i]; ref var refs = ref _textureRefs[binding + i];
texture.ImageView = refs.View?.Get(cbs).Value ?? default; texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0) if (texture.ImageView.Handle == 0)
@@ -876,7 +902,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default; images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
} }
tu.Push<DescriptorImageInfo>(images[..count]); tu.Push<DescriptorImageInfo>(images[..count]);
@@ -947,7 +973,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var texture = ref textures[i]; ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i]; ref var refs = ref _textureRefs[binding + i];
texture.ImageView = refs.View?.Get(cbs).Value ?? default; texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0) if (texture.ImageView.Handle == 0)
@@ -254,8 +254,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height); scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height);
_pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height, false); _pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height);
_pipeline.SetRenderTargetColorMasks(colorMasks, false); _pipeline.SetRenderTargetColorMasks(colorMasks);
_pipeline.SetScissors(scissors); _pipeline.SetScissors(scissors);
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
} }
@@ -238,7 +238,6 @@ namespace Ryujinx.Graphics.Vulkan
Face.Back => CullModeFlags.BackBit, Face.Back => CullModeFlags.BackBit,
Face.Front => CullModeFlags.FrontBit, Face.Front => CullModeFlags.FrontBit,
Face.FrontAndBack => CullModeFlags.FrontAndBack, Face.FrontAndBack => CullModeFlags.FrontAndBack,
Face.None => CullModeFlags.None,
_ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit), _ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit),
}; };
} }
@@ -311,25 +310,6 @@ namespace Ryujinx.Graphics.Vulkan
}; };
} }
public static PrimitiveTopology ConvertToClass(this PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.PointList => PrimitiveTopology.PointList,
PrimitiveTopology.LineList or
PrimitiveTopology.LineStrip or
PrimitiveTopology.LineListWithAdjacency or
PrimitiveTopology.LineStripWithAdjacency => PrimitiveTopology.LineList,
PrimitiveTopology.TriangleList or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan or
PrimitiveTopology.TriangleListWithAdjacency or
PrimitiveTopology.TriangleStripWithAdjacency => PrimitiveTopology.TriangleStrip,
PrimitiveTopology.PatchList => PrimitiveTopology.PatchList,
_ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), PrimitiveTopology.TriangleStrip),
};
}
public static StencilOp Convert(this GAL.StencilOp op) public static StencilOp Convert(this GAL.StencilOp op)
{ {
return op switch return op switch
@@ -0,0 +1,12 @@
using System;
namespace Ryujinx.Graphics.Vulkan
{
[Flags]
internal enum FeedbackLoopAspects
{
None = 0,
Color = 1 << 0,
Depth = 1 << 1,
}
}
@@ -9,48 +9,48 @@ namespace Ryujinx.Graphics.Vulkan
{ {
class FormatCapabilities class FormatCapabilities
{ {
private static readonly GAL.Format[] _scaledFormats = private static readonly Format[] _scaledFormats =
[ [
GAL.Format.R8Uscaled, Format.R8Uscaled,
GAL.Format.R8Sscaled, Format.R8Sscaled,
GAL.Format.R16Uscaled, Format.R16Uscaled,
GAL.Format.R16Sscaled, Format.R16Sscaled,
GAL.Format.R8G8Uscaled, Format.R8G8Uscaled,
GAL.Format.R8G8Sscaled, Format.R8G8Sscaled,
GAL.Format.R16G16Uscaled, Format.R16G16Uscaled,
GAL.Format.R16G16Sscaled, Format.R16G16Sscaled,
GAL.Format.R8G8B8Uscaled, Format.R8G8B8Uscaled,
GAL.Format.R8G8B8Sscaled, Format.R8G8B8Sscaled,
GAL.Format.R16G16B16Uscaled, Format.R16G16B16Uscaled,
GAL.Format.R16G16B16Sscaled, Format.R16G16B16Sscaled,
GAL.Format.R8G8B8A8Uscaled, Format.R8G8B8A8Uscaled,
GAL.Format.R8G8B8A8Sscaled, Format.R8G8B8A8Sscaled,
GAL.Format.R16G16B16A16Uscaled, Format.R16G16B16A16Uscaled,
GAL.Format.R16G16B16A16Sscaled, Format.R16G16B16A16Sscaled,
GAL.Format.R10G10B10A2Uscaled, Format.R10G10B10A2Uscaled,
GAL.Format.R10G10B10A2Sscaled Format.R10G10B10A2Sscaled
]; ];
private static readonly GAL.Format[] _intFormats = private static readonly Format[] _intFormats =
[ [
GAL.Format.R8Uint, Format.R8Uint,
GAL.Format.R8Sint, Format.R8Sint,
GAL.Format.R16Uint, Format.R16Uint,
GAL.Format.R16Sint, Format.R16Sint,
GAL.Format.R8G8Uint, Format.R8G8Uint,
GAL.Format.R8G8Sint, Format.R8G8Sint,
GAL.Format.R16G16Uint, Format.R16G16Uint,
GAL.Format.R16G16Sint, Format.R16G16Sint,
GAL.Format.R8G8B8Uint, Format.R8G8B8Uint,
GAL.Format.R8G8B8Sint, Format.R8G8B8Sint,
GAL.Format.R16G16B16Uint, Format.R16G16B16Uint,
GAL.Format.R16G16B16Sint, Format.R16G16B16Sint,
GAL.Format.R8G8B8A8Uint, Format.R8G8B8A8Uint,
GAL.Format.R8G8B8A8Sint, Format.R8G8B8A8Sint,
GAL.Format.R16G16B16A16Uint, Format.R16G16B16A16Uint,
GAL.Format.R16G16B16A16Sint, Format.R16G16B16A16Sint,
GAL.Format.R10G10B10A2Uint, Format.R10G10B10A2Uint,
GAL.Format.R10G10B10A2Sint Format.R10G10B10A2Sint
]; ];
private readonly FormatFeatureFlags[] _bufferTable; private readonly FormatFeatureFlags[] _bufferTable;
@@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
_depthStencil?.Storage?.AddStoreOpUsage(true); _depthStencil?.Storage?.AddStoreOpUsage(true);
} }
public void ClearBindings()
{
_depthStencil?.Storage.ClearBindings();
for (int i = 0; i < _colorsCanonical.Length; i++)
{
_colorsCanonical[i]?.Storage.ClearBindings();
}
}
public void AddBindings()
{
_depthStencil?.Storage.AddBinding(_depthStencil);
for (int i = 0; i < _colorsCanonical.Length; i++)
{
TextureView color = _colorsCanonical[i];
color?.Storage.AddBinding(color);
}
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd, VulkanRenderer gd,
Device device, Device device,
@@ -31,7 +31,6 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsShaderStorageImageMultisample; public readonly bool SupportsShaderStorageImageMultisample;
public readonly bool SupportsConditionalRendering; public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsExtendedDynamicState;
public readonly PhysicalDeviceExtendedDynamicState2FeaturesEXT SupportsExtendedDynamicState2;
public readonly bool SupportsMultiView; public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors; public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors; public readonly bool SupportsPushDescriptors;
@@ -47,14 +46,14 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsViewportArray2; public readonly bool SupportsViewportArray2;
public readonly bool SupportsHostImportedMemory; public readonly bool SupportsHostImportedMemory;
public readonly bool SupportsDepthClipControl; public readonly bool SupportsDepthClipControl;
public readonly bool SupportsWideLines; public readonly bool SupportsAttachmentFeedbackLoop;
public readonly bool SupportsDynamicAttachmentFeedbackLoop;
public readonly uint SubgroupSize; public readonly uint SubgroupSize;
public readonly SampleCountFlags SupportedSampleCounts; public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset; public readonly PortabilitySubsetFlags PortabilitySubset;
public readonly uint VertexBufferAlignment; public readonly uint VertexBufferAlignment;
public readonly uint SubTexelPrecisionBits; public readonly uint SubTexelPrecisionBits;
public readonly ulong MinResourceAlignment; public readonly ulong MinResourceAlignment;
public readonly uint MaxTessellationPatchSize;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
@@ -72,8 +71,6 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsShaderStorageImageMultisample, bool supportsShaderStorageImageMultisample,
bool supportsConditionalRendering, bool supportsConditionalRendering,
bool supportsExtendedDynamicState, bool supportsExtendedDynamicState,
PhysicalDeviceExtendedDynamicState2FeaturesEXT supportsExtendedDynamicState2,
uint maxTessellationPatchSize,
bool supportsMultiView, bool supportsMultiView,
bool supportsNullDescriptors, bool supportsNullDescriptors,
bool supportsPushDescriptors, bool supportsPushDescriptors,
@@ -89,7 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsViewportArray2, bool supportsViewportArray2,
bool supportsHostImportedMemory, bool supportsHostImportedMemory,
bool supportsDepthClipControl, bool supportsDepthClipControl,
bool supportsWideLines, bool supportsAttachmentFeedbackLoop,
bool supportsDynamicAttachmentFeedbackLoop,
uint subgroupSize, uint subgroupSize,
SampleCountFlags supportedSampleCounts, SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset, PortabilitySubsetFlags portabilitySubset,
@@ -112,8 +110,6 @@ namespace Ryujinx.Graphics.Vulkan
SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample; SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample;
SupportsConditionalRendering = supportsConditionalRendering; SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState; SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
MaxTessellationPatchSize = maxTessellationPatchSize;
SupportsMultiView = supportsMultiView; SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors; SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors; SupportsPushDescriptors = supportsPushDescriptors;
@@ -129,7 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
SupportsViewportArray2 = supportsViewportArray2; SupportsViewportArray2 = supportsViewportArray2;
SupportsHostImportedMemory = supportsHostImportedMemory; SupportsHostImportedMemory = supportsHostImportedMemory;
SupportsDepthClipControl = supportsDepthClipControl; SupportsDepthClipControl = supportsDepthClipControl;
SupportsWideLines = supportsWideLines; SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
SubgroupSize = subgroupSize; SubgroupSize = subgroupSize;
SupportedSampleCounts = supportedSampleCounts; SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset; PortabilitySubset = portabilitySubset;
+39 -40
View File
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using CompareOp = Ryujinx.Graphics.GAL.CompareOp; using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
using Format = Ryujinx.Graphics.GAL.Format; using Format = Ryujinx.Graphics.GAL.Format;
@@ -406,35 +405,35 @@ namespace Ryujinx.Graphics.Vulkan
if (dstIsDepthOrStencil) if (dstIsDepthOrStencil)
{ {
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit, false); _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always), false); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
} }
else if (src.Info.Target.IsMultisample()) else if (src.Info.Target.IsMultisample())
{ {
_pipeline.SetProgram(_programColorBlitMs, false); _pipeline.SetProgram(_programColorBlitMs);
} }
else if (clearAlpha) else if (clearAlpha)
{ {
_pipeline.SetProgram(_programColorBlitClearAlpha, false); _pipeline.SetProgram(_programColorBlitClearAlpha);
} }
else else
{ {
_pipeline.SetProgram(_programColorBlit, false); _pipeline.SetProgram(_programColorBlit);
} }
int dstWidth = dst.Width; int dstWidth = dst.Width;
int dstHeight = dst.Height; int dstHeight = dst.Height;
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetRenderTargetColorMasks([0xf], false); _pipeline.SetRenderTargetColorMasks([0xf]);
_pipeline.SetScissors([new Rectangle<int>(0, 0, dstWidth, dstHeight)], false); _pipeline.SetScissors([new Rectangle<int>(0, 0, dstWidth, dstHeight)]);
if (clearAlpha) if (clearAlpha)
{ {
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
} }
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);
@@ -501,10 +500,10 @@ namespace Ryujinx.Graphics.Vulkan
int dstWidth = dst.Width; int dstWidth = dst.Width;
int dstHeight = dst.Height; int dstHeight = dst.Height;
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetScissors([new Rectangle<int>(0, 0, dstWidth, dstHeight)], false); _pipeline.SetScissors([new Rectangle<int>(0, 0, dstWidth, dstHeight)]);
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
var aspectFlags = src.Info.Format.ConvertAspectFlags(); var aspectFlags = src.Info.Format.ConvertAspectFlags();
@@ -566,12 +565,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth) if (isDepth)
{ {
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit, false); _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
} }
else else
{ {
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit, false); _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
} }
@@ -661,11 +660,11 @@ namespace Ryujinx.Graphics.Vulkan
program = _programColorClearF; program = _programColorClearF;
} }
_pipeline.SetProgram(program, false); _pipeline.SetProgram(program);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetRenderTargetColorMasks(new[] { componentMask }, false); _pipeline.SetRenderTargetColorMasks(new[] { componentMask });
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports);
_pipeline.SetScissors([scissor], false); _pipeline.SetScissors([scissor]);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish(); _pipeline.Finish();
@@ -708,12 +707,12 @@ namespace Ryujinx.Graphics.Vulkan
0f, 0f,
1f); 1f);
_pipeline.SetProgram(_programDepthStencilClear, false); _pipeline.SetProgram(_programDepthStencilClear);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports);
_pipeline.SetScissors([scissor], false); _pipeline.SetScissors([scissor]);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always), false); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask));
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish(); _pipeline.Finish();
@@ -771,8 +770,8 @@ namespace Ryujinx.Graphics.Vulkan
0f, 0f,
1f); 1f);
pipeline.SetProgram(_programColorBlit, false); pipeline.SetProgram(_programColorBlit);
pipeline.SetViewports(viewports, false); pipeline.SetViewports(viewports);
pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
pipeline.Draw(4, 1, 0, 0); pipeline.Draw(4, 1, 0, 0);
@@ -1094,16 +1093,16 @@ namespace Ryujinx.Graphics.Vulkan
0f, 0f,
1f); 1f);
_pipeline.SetScissors([new Rectangle<int>(0, 0, dst.Width, dst.Height)], false); _pipeline.SetScissors([new Rectangle<int>(0, 0, dst.Width, dst.Height)]);
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
for (int z = 0; z < depth; z++) for (int z = 0; z < depth; z++)
{ {
var srcView = Create2DLayerView(src, srcLayer + z, 0); var srcView = Create2DLayerView(src, srcLayer + z, 0);
var dstView = Create2DLayerView(dst, dstLayer + z, 0); var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height, false); _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
CopyMSDraw(srcView, aspectFlags, fromMS: true); CopyMSDraw(srcView, aspectFlags, fromMS: true);
@@ -1216,9 +1215,9 @@ namespace Ryujinx.Graphics.Vulkan
1f); 1f);
_pipeline.SetRenderTargetColorMasks([0xf]); _pipeline.SetRenderTargetColorMasks([0xf]);
_pipeline.SetScissors([new Rectangle<int>(0, 0, dst.Width, dst.Height)], false); _pipeline.SetScissors([new Rectangle<int>(0, 0, dst.Width, dst.Height)]);
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
@@ -1229,7 +1228,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0); var srcView = Create2DLayerView(src, srcLayer + z, 0);
var dstView = Create2DLayerView(dst, dstLayer + z, 0); var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height, false); _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
CopyMSDraw(srcView, aspectFlags, fromMS: false); CopyMSDraw(srcView, aspectFlags, fromMS: false);
@@ -1246,7 +1245,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
else else
{ {
_pipeline.SetProgram(_programColorDrawToMs, false); _pipeline.SetProgram(_programColorDrawToMs);
var format = GetFormat(src.Info.BytesPerPixel); var format = GetFormat(src.Info.BytesPerPixel);
var vkFormat = FormatTable.GetFormat(format); var vkFormat = FormatTable.GetFormat(format);
@@ -1323,12 +1322,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth) if (isDepth)
{ {
_pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs, false); _pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
} }
else else
{ {
_pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs, false); _pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
} }
@@ -1,6 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
+159 -298
View File
@@ -1,9 +1,9 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly Action EndRenderPassDelegate; public readonly Action EndRenderPassDelegate;
protected PipelineDynamicState DynamicState; protected PipelineDynamicState DynamicState;
protected bool IsMainPipeline;
private PipelineState _newState; private PipelineState _newState;
private bool _graphicsStateDirty; private bool _graphicsStateDirty;
private bool _computeStateDirty; private bool _computeStateDirty;
@@ -73,7 +74,6 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferState[] _transformFeedbackBuffers; private readonly BufferState[] _transformFeedbackBuffers;
private readonly VertexBufferState[] _vertexBuffers; private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty; private ulong _vertexBuffersDirty;
private bool _bindingsSet;
protected Rectangle<int> ClearScissor; protected Rectangle<int> ClearScissor;
private readonly VertexBufferUpdater _vertexBufferUpdater; private readonly VertexBufferUpdater _vertexBufferUpdater;
@@ -87,15 +87,13 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled; private bool _tfEnabled;
private bool _tfActive; private bool _tfActive;
private readonly bool _supportExtDynamic; private FeedbackLoopAspects _feedbackLoop;
private readonly bool _supportExtDynamic2; private bool _passWritesDepthStencil;
private readonly PipelineColorBlendAttachmentState[] _storedBlend; private readonly PipelineColorBlendAttachmentState[] _storedBlend;
public ulong DrawCount { get; private set; } public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; } public bool RenderPassActive { get; private set; }
private readonly int[] _vertexBufferBindings;
public unsafe PipelineBase(VulkanRenderer gd, Device device) public unsafe PipelineBase(VulkanRenderer gd, Device device)
{ {
Gd = gd; Gd = gd;
@@ -128,24 +126,12 @@ namespace Ryujinx.Graphics.Vulkan
_storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets]; _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets];
_supportExtDynamic = gd.Capabilities.SupportsExtendedDynamicState; _newState.Initialize();
_supportExtDynamic2 = gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2;
_bindingsSet = false;
_vertexBufferBindings = new int[Constants.MaxVertexBuffers];
for (int i = 0; i < Constants.MaxVertexBuffers; i++)
{
_vertexBufferBindings[i] = i + 1;
}
_newState.Initialize(gd.Capabilities);
} }
public void Initialize() public void Initialize()
{ {
_descriptorSetUpdater.Initialize(); _descriptorSetUpdater.Initialize(IsMainPipeline);
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false); QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false);
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, [int.MinValue, -1, 0], 1, true); TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
@@ -646,46 +632,19 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (texture is TextureView srcTexture) if (texture is TextureView srcTexture)
{ {
CullModeFlags oldCullMode; var oldCullMode = _newState.CullMode;
bool oldStencilTestEnable; var oldStencilTestEnable = _newState.StencilTestEnable;
bool oldDepthTestEnable; var oldDepthTestEnable = _newState.DepthTestEnable;
bool oldDepthWriteEnable; var oldDepthWriteEnable = _newState.DepthWriteEnable;
PrimitiveTopology oldTopology; var oldViewports = DynamicState.Viewports;
Array16<Silk.NET.Vulkan.Viewport> oldViewports = DynamicState.Viewports; var oldViewportsCount = _newState.ViewportsCount;
uint oldViewportsCount; var oldTopology = _topology;
if (_supportExtDynamic)
{
oldCullMode = DynamicState.CullMode;
oldStencilTestEnable = DynamicState.StencilTestEnable;
oldDepthTestEnable = DynamicState.DepthTestEnable;
oldDepthWriteEnable = DynamicState.DepthWriteEnable;
oldTopology = _topology;
oldViewportsCount = DynamicState.ViewportsCount;
}
else
{
oldCullMode = _newState.CullMode;
oldStencilTestEnable = _newState.StencilTestEnable;
oldDepthTestEnable = _newState.DepthTestEnable;
oldDepthWriteEnable = _newState.DepthWriteEnable;
oldTopology = _topology;
oldViewportsCount = _newState.ViewportsCount;
}
if (_supportExtDynamic)
{
DynamicState.SetCullMode(CullModeFlags.None);
DynamicState.SetDepthTestBool(false, false);
DynamicState.SetStencilTest(false);
}
else
{
_newState.CullMode = CullModeFlags.None; _newState.CullMode = CullModeFlags.None;
_newState.StencilTestEnable = false; _newState.StencilTestEnable = false;
_newState.DepthTestEnable = false; _newState.DepthTestEnable = false;
_newState.DepthWriteEnable = false; _newState.DepthWriteEnable = false;
} SignalStateChange();
Gd.HelperShader.DrawTexture( Gd.HelperShader.DrawTexture(
Gd, Gd,
@@ -695,24 +654,16 @@ namespace Ryujinx.Graphics.Vulkan
srcRegion, srcRegion,
dstRegion); dstRegion);
if (_supportExtDynamic)
{
DynamicState.SetCullMode(oldCullMode);
DynamicState.SetStencilTest(oldStencilTestEnable);
DynamicState.SetDepthTestBool(oldDepthTestEnable, oldDepthWriteEnable);
}
else
{
_newState.CullMode = oldCullMode; _newState.CullMode = oldCullMode;
_newState.StencilTestEnable = oldStencilTestEnable; _newState.StencilTestEnable = oldStencilTestEnable;
_newState.DepthTestEnable = oldDepthTestEnable; _newState.DepthTestEnable = oldDepthTestEnable;
_newState.DepthWriteEnable = oldDepthWriteEnable; _newState.DepthWriteEnable = oldDepthWriteEnable;
_newState.ViewportsCount = oldViewportsCount;
}
SetPrimitiveTopology(oldTopology); SetPrimitiveTopology(oldTopology);
DynamicState.SetViewports(ref oldViewports, oldViewportsCount); DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
_newState.ViewportsCount = oldViewportsCount;
SignalStateChange();
} }
} }
@@ -840,103 +791,50 @@ namespace Ryujinx.Graphics.Vulkan
} }
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
{
bool depthBiasEnable = (enables != 0) && (factor != 0 && units != 0);
bool changed = false;
if (_supportExtDynamic2)
{
DynamicState.SetDepthBiasEnable(depthBiasEnable);
}
else if (_newState.DepthBiasEnable != depthBiasEnable)
{
_newState.DepthBiasEnable = depthBiasEnable;
changed = true;
}
if (depthBiasEnable)
{ {
DynamicState.SetDepthBias(factor, units, clamp); DynamicState.SetDepthBias(factor, units, clamp);
}
if (changed) _newState.DepthBiasEnable = enables != 0;
{
SignalStateChange(); SignalStateChange();
} }
}
public void SetDepthClamp(bool clamp) public void SetDepthClamp(bool clamp)
{ {
_newState.DepthClampEnable = clamp; _newState.DepthClampEnable = clamp;
SignalStateChange(); SignalStateChange();
} }
public void SetDepthMode(DepthMode mode) public void SetDepthMode(DepthMode mode)
{ {
bool newMode = mode == DepthMode.MinusOneToOne; bool oldMode = _newState.DepthMode;
_newState.DepthMode = mode == DepthMode.MinusOneToOne;
if (_newState.DepthMode == newMode) if (_newState.DepthMode != oldMode)
{ {
return;
}
_newState.DepthMode = newMode;
SignalStateChange(); SignalStateChange();
} }
}
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true) public void SetDepthTest(DepthTestDescriptor depthTest)
{
if (_supportExtDynamic)
{
DynamicState.SetDepthTestBool(depthTest.TestEnable, depthTest.WriteEnable);
if (depthTest.TestEnable)
{
DynamicState.SetDepthTestCompareOp(depthTest.Func.Convert());
}
}
else
{ {
_newState.DepthTestEnable = depthTest.TestEnable; _newState.DepthTestEnable = depthTest.TestEnable;
_newState.DepthWriteEnable = depthTest.WriteEnable; _newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert(); _newState.DepthCompareOp = depthTest.Func.Convert();
if (signalChange) UpdatePassDepthStencil();
{
SignalStateChange(); SignalStateChange();
} }
}
} public void SetFaceCulling(bool enable, Face face)
public void SetFaceCulling(Face face)
{ {
if (_supportExtDynamic) _newState.CullMode = enable ? face.Convert() : CullModeFlags.None;
{
DynamicState.SetCullMode(face.Convert());
}
else
{
_newState.CullMode = face.Convert();
SignalStateChange(); SignalStateChange();
} }
}
public void SetFrontFace(FrontFace frontFace) public void SetFrontFace(FrontFace frontFace)
{
if (_supportExtDynamic)
{
DynamicState.SetFrontFace(frontFace.Convert());
}
else
{ {
_newState.FrontFace = frontFace.Convert(); _newState.FrontFace = frontFace.Convert();
SignalStateChange(); SignalStateChange();
} }
}
public void SetImage(ShaderStage stage, int binding, ITexture image) public void SetImage(ShaderStage stage, int binding, ITexture image)
{ {
@@ -974,56 +872,28 @@ namespace Ryujinx.Graphics.Vulkan
public void SetLineParameters(float width, bool smooth) public void SetLineParameters(float width, bool smooth)
{ {
if (!Gd.IsMoltenVk) _newState.LineWidth = width;
{ SignalStateChange();
DynamicState.SetLineWidth(Gd.Capabilities.SupportsWideLines ? width : 1.0f);
}
} }
public void SetLogicOpState(bool enable, LogicalOp op) public void SetLogicOpState(bool enable, LogicalOp op)
{ {
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here.
bool logicOpEnable = enable && (Gd.Vendor == Vendor.Nvidia || _newState.Internal.LogicOpsAllowed);
_newState.LogicOpEnable = enable; _newState.LogicOpEnable = enable;
if (Gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
if (logicOpEnable)
{
DynamicState.SetLogicOp(op.Convert());
}
}
else
{
_newState.LogicOp = op.Convert(); _newState.LogicOp = op.Convert();
}
SignalStateChange(); SignalStateChange();
} }
public void SetMultisampleState(MultisampleDescriptor multisample) public void SetMultisampleState(MultisampleDescriptor multisample)
{ {
_newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; _newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable;
_newState.AlphaToOneEnable = multisample.AlphaToOneEnable; _newState.AlphaToOneEnable = multisample.AlphaToOneEnable;
SignalStateChange(); SignalStateChange();
} }
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
{
if (Gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
DynamicState.SetPatchControlPoints((uint)vertices);
}
else
{ {
_newState.PatchControlPoints = (uint)vertices; _newState.PatchControlPoints = (uint)vertices;
SignalStateChange(); SignalStateChange();
}
// TODO: Default levels (likely needs emulation on shaders?) // TODO: Default levels (likely needs emulation on shaders?)
} }
@@ -1039,22 +909,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
public void SetPrimitiveRestart(bool enable, int index) public void SetPrimitiveRestart(bool enable, int index)
{
if (_supportExtDynamic2)
{
DynamicState.SetPrimitiveRestartEnable(enable);
}
else
{ {
_newState.PrimitiveRestartEnable = enable; _newState.PrimitiveRestartEnable = enable;
// TODO: What to do about the index?
SignalStateChange(); SignalStateChange();
} }
// TODO: What to do about the index? public void SetPrimitiveTopology(PrimitiveTopology topology)
}
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{ {
_topology = topology; _topology = topology;
@@ -1062,18 +923,10 @@ namespace Ryujinx.Graphics.Vulkan
_newState.Topology = vkTopology; _newState.Topology = vkTopology;
if (_supportExtDynamic)
{
DynamicState.SetPrimitiveTopology(vkTopology);
}
if (signalChange)
{
SignalStateChange(); SignalStateChange();
} }
}
public void SetProgram(IProgram program, bool signalChange = true) public void SetProgram(IProgram program)
{ {
var internalProgram = (ShaderCollection)program; var internalProgram = (ShaderCollection)program;
var stages = internalProgram.GetInfos(); var stages = internalProgram.GetInfos();
@@ -1089,10 +942,7 @@ namespace Ryujinx.Graphics.Vulkan
stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]); stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]);
if (signalChange)
{
SignalStateChange(); SignalStateChange();
}
if (internalProgram.IsCompute) if (internalProgram.IsCompute)
{ {
@@ -1117,27 +967,19 @@ namespace Ryujinx.Graphics.Vulkan
} }
public void SetRasterizerDiscard(bool discard) public void SetRasterizerDiscard(bool discard)
{
if (_supportExtDynamic2)
{
DynamicState.SetRasterizerDiscard(discard);
}
else
{ {
_newState.RasterizerDiscardEnable = discard; _newState.RasterizerDiscardEnable = discard;
SignalStateChange(); SignalStateChange();
}
if (!discard && Gd.IsQualcommProprietary) if (!discard && Gd.IsQualcommProprietary)
{ {
// On Adreno, enabling rasterizer discard somehow corrupts the viewport state. // On Adreno, enabling rasterizer discard somehow corrupts the viewport state.
// Force it to be updated on next use to work around this bug. // Force it to be updated on next use to work around this bug.
DynamicState.ForceAllDirty(Gd); DynamicState.ForceAllDirty();
} }
} }
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
{ {
int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length); int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length);
int writtenAttachments = 0; int writtenAttachments = 0;
@@ -1176,11 +1018,8 @@ namespace Ryujinx.Graphics.Vulkan
SetRenderTargetsInternal(_preMaskColors, _preMaskDepthStencil, true); SetRenderTargetsInternal(_preMaskColors, _preMaskDepthStencil, true);
} }
else else
{
if (signalChange)
{ {
SignalStateChange(); SignalStateChange();
}
if (writtenAttachments != _writtenAttachmentCount) if (writtenAttachments != _writtenAttachmentCount)
{ {
@@ -1204,7 +1043,7 @@ namespace Ryujinx.Graphics.Vulkan
SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR); SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
} }
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true) public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
{ {
int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1;
int count = Math.Min(maxScissors, regions.Length); int count = Math.Min(maxScissors, regions.Length);
@@ -1213,8 +1052,6 @@ namespace Ryujinx.Graphics.Vulkan
ClearScissor = regions[0]; ClearScissor = regions[0];
} }
DynamicState.ScissorsCount = count;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
var region = regions[i]; var region = regions[i];
@@ -1224,36 +1061,23 @@ namespace Ryujinx.Graphics.Vulkan
DynamicState.SetScissor(i, new Rect2D(offset, extent)); DynamicState.SetScissor(i, new Rect2D(offset, extent));
} }
if (!_supportExtDynamic) DynamicState.ScissorsCount = count;
{
_newState.ScissorsCount = (uint)count;
if (signalChange) _newState.ScissorsCount = (uint)count;
{
SignalStateChange(); SignalStateChange();
} }
}
}
public void SetStencilTest(StencilTestDescriptor stencilTest) public void SetStencilTest(StencilTestDescriptor stencilTest)
{ {
if (_supportExtDynamic) DynamicState.SetStencilMasks(
{ (uint)stencilTest.BackFuncMask,
DynamicState.SetStencilTestandOp( (uint)stencilTest.BackMask,
stencilTest.BackSFail.Convert(), (uint)stencilTest.BackFuncRef,
stencilTest.BackDpPass.Convert(), (uint)stencilTest.FrontFuncMask,
stencilTest.BackDpFail.Convert(), (uint)stencilTest.FrontMask,
stencilTest.BackFunc.Convert(), (uint)stencilTest.FrontFuncRef);
stencilTest.FrontSFail.Convert(),
stencilTest.FrontDpPass.Convert(),
stencilTest.FrontDpFail.Convert(),
stencilTest.FrontFunc.Convert(),
stencilTest.TestEnable);
SignalStateChange(); _newState.StencilTestEnable = stencilTest.TestEnable;
}
else
{
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert(); _newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert(); _newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert(); _newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
@@ -1262,19 +1086,11 @@ namespace Ryujinx.Graphics.Vulkan
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert(); _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert(); _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert(); _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
_newState.StencilTestEnable = stencilTest.TestEnable;
UpdatePassDepthStencil();
SignalStateChange(); SignalStateChange();
} }
DynamicState.SetStencilMask((uint)stencilTest.BackFuncMask,
(uint)stencilTest.BackMask,
(uint)stencilTest.BackFuncRef,
(uint)stencilTest.FrontFuncMask,
(uint)stencilTest.FrontMask,
(uint)stencilTest.FrontFuncRef);
}
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers) public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
{ {
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers); _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers);
@@ -1391,24 +1207,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int count = Math.Min(Constants.MaxVertexBuffers, vertexBuffers.Length); int count = Math.Min(Constants.MaxVertexBuffers, vertexBuffers.Length);
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
int validCount = 1; int validCount = 1;
if (!_bindingsSet)
{
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, _supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
for (int i = 1; i < count; i++)
{
_newState.Internal.VertexBindingDescriptions[i] = new VertexInputBindingDescription((uint)i);
}
_bindingsSet = true;
}
BufferHandle lastHandle = default; BufferHandle lastHandle = default;
Auto<DisposableBuffer> lastBuffer = default; Auto<DisposableBuffer> lastBuffer = default;
bool vertexBindingDescriptionChanged = false;
bool vertexDescriptionCountChanged = false;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -1427,32 +1231,13 @@ namespace Ryujinx.Graphics.Vulkan
if (vb != null) if (vb != null)
{ {
int binding = i + 1;
int descriptorIndex = validCount++; int descriptorIndex = validCount++;
if (_supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31)) _newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription(
{ (uint)binding,
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate || (uint)vertexBuffer.Stride,
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i]) inputRate);
{
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
vertexBindingDescriptionChanged = true;
}
}
else
{
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride != vertexBuffer.Stride ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
{
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride = (uint)vertexBuffer.Stride;
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
vertexBindingDescriptionChanged = true;
}
}
int vbSize = vertexBuffer.Buffer.Size; int vbSize = vertexBuffer.Buffer.Size;
@@ -1468,7 +1253,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
ref var buffer = ref _vertexBuffers[_vertexBufferBindings[i]]; ref var buffer = ref _vertexBuffers[binding];
int oldScalarAlign = buffer.AttributeScalarAlignment; int oldScalarAlign = buffer.AttributeScalarAlignment;
if (Gd.Capabilities.VertexBufferAlignment < 2 && if (Gd.Capabilities.VertexBufferAlignment < 2 &&
@@ -1485,7 +1270,7 @@ namespace Ryujinx.Graphics.Vulkan
vbSize, vbSize,
vertexBuffer.Stride); vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)_vertexBufferBindings[i], ref _newState, _vertexBufferUpdater); buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater);
} }
} }
else else
@@ -1501,7 +1286,7 @@ namespace Ryujinx.Graphics.Vulkan
vbSize, vbSize,
vertexBuffer.Stride); vertexBuffer.Stride);
_vertexBuffersDirty |= 1UL << _vertexBufferBindings[i]; _vertexBuffersDirty |= 1UL << binding;
} }
buffer.AttributeScalarAlignment = oldScalarAlign; buffer.AttributeScalarAlignment = oldScalarAlign;
@@ -1511,19 +1296,11 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs); _vertexBufferUpdater.Commit(Cbs);
if (_newState.VertexBindingDescriptionsCount != validCount)
{
_newState.VertexBindingDescriptionsCount = (uint)validCount; _newState.VertexBindingDescriptionsCount = (uint)validCount;
vertexDescriptionCountChanged = true;
}
if (vertexDescriptionCountChanged || vertexBindingDescriptionChanged)
{
SignalStateChange(); SignalStateChange();
} }
}
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true) public void SetViewports(ReadOnlySpan<Viewport> viewports)
{ {
int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1;
int count = Math.Min(maxViewports, viewports.Length); int count = Math.Min(maxViewports, viewports.Length);
@@ -1548,16 +1325,9 @@ namespace Ryujinx.Graphics.Vulkan
Clamp(viewport.DepthFar))); Clamp(viewport.DepthFar)));
} }
if (!_supportExtDynamic)
{
_newState.ViewportsCount = (uint)count; _newState.ViewportsCount = (uint)count;
if (signalChange)
{
SignalStateChange(); SignalStateChange();
} }
}
}
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
{ {
@@ -1605,7 +1375,7 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
_descriptorSetUpdater.SignalCommandBufferChange(); _descriptorSetUpdater.SignalCommandBufferChange();
DynamicState.ForceAllDirty(Gd); DynamicState.ForceAllDirty();
_currentPipelineHandle = 0; _currentPipelineHandle = 0;
} }
@@ -1665,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
if (IsMainPipeline)
{
FramebufferParams?.ClearBindings();
}
FramebufferParams = new FramebufferParams(Device, colors, depthStencil); FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
if (IsMainPipeline)
{
FramebufferParams.AddBindings();
_newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
_bindingBarriersDirty = true;
}
_passWritesDepthStencil = false;
UpdatePassDepthStencil();
UpdatePipelineAttachmentFormats(); UpdatePipelineAttachmentFormats();
} }
@@ -1732,11 +1518,86 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
} }
private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
{
// AMD Radeon RX GPUs + Qualcomm SoCs only
if ((Gd.Vendor == Vendor.Amd && Gd.GpuRenderer.Contains("RX")) || Gd.Vendor == Vendor.Qualcomm)
{
if (_feedbackLoop != aspects)
{
if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
DynamicState.SetFeedbackLoop(aspects);
}
else
{
_newState.FeedbackLoopAspects = aspects;
}
_feedbackLoop = aspects;
return true;
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool UpdateFeedbackLoop()
{
List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards;
if ((hazards?.Count ?? 0) > 0)
{
FeedbackLoopAspects aspects = 0;
foreach (TextureView view in hazards)
{
// May need to enforce feedback loop layout here in the future.
// Though technically, it should always work with the general layout.
if (view.Info.Format.IsDepthOrStencil())
{
if (_passWritesDepthStencil)
{
// If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
aspects |= FeedbackLoopAspects.Depth;
}
}
else
{
aspects |= FeedbackLoopAspects.Color;
}
}
return ChangeFeedbackLoop(aspects);
}
else if (_feedbackLoop != 0)
{
return ChangeFeedbackLoop(FeedbackLoopAspects.None);
}
return false;
}
private void UpdatePassDepthStencil()
{
if (!RenderPassActive)
{
_passWritesDepthStencil = false;
}
// Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
}
private bool RecreateGraphicsPipelineIfNeeded() private bool RecreateGraphicsPipelineIfNeeded()
{ {
if (AutoFlush.ShouldFlushDraw(DrawCount)) if (AutoFlush.ShouldFlushDraw(DrawCount))
@@ -1778,7 +1639,15 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs); _vertexBufferUpdater.Commit(Cbs);
} }
if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) if (_bindingBarriersDirty)
{
// Stale barriers may have been activated by switching program. Emit any that are relevant.
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
_bindingBarriersDirty = false;
}
if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
{ {
if (!CreatePipeline(PipelineBindPoint.Graphics)) if (!CreatePipeline(PipelineBindPoint.Graphics))
{ {
@@ -1787,17 +1656,9 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false; _graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics; Pbp = PipelineBindPoint.Graphics;
if (_bindingBarriersDirty)
{
// Stale barriers may have been activated by switching program. Emit any that are relevant.
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
_bindingBarriersDirty = false;
}
} }
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
@@ -4,7 +4,6 @@ using Silk.NET.Vulkan;
using System; using System;
using Format = Silk.NET.Vulkan.Format; using Format = Silk.NET.Vulkan.Format;
using PolygonMode = Silk.NET.Vulkan.PolygonMode; using PolygonMode = Silk.NET.Vulkan.PolygonMode;
using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -161,38 +160,35 @@ namespace Ryujinx.Graphics.Vulkan
public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd) public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd)
{ {
var extendedDynamicState2 = gd.Capabilities.SupportsExtendedDynamicState2;
var extendedDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
PipelineState pipeline = new(); PipelineState pipeline = new();
pipeline.Initialize(gd.Capabilities); pipeline.Initialize();
// It is assumed that Dynamic State is enabled when this conversion is used. // It is assumed that Dynamic State is enabled when this conversion is used.
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
pipeline.DepthBoundsTestEnable = false; // Not implemented. pipeline.DepthBoundsTestEnable = false; // Not implemented.
pipeline.DepthClampEnable = state.DepthClampEnable; pipeline.DepthClampEnable = state.DepthClampEnable;
pipeline.AlphaToCoverageEnable = state.AlphaToCoverageEnable;
pipeline.AlphaToOneEnable = state.AlphaToOneEnable;
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
pipeline.HasDepthStencil = state.DepthStencilEnable;
pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
if (!extendedDynamicState)
{
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.CullMode = state.CullMode.Convert();
pipeline.DepthTestEnable = state.DepthTest.TestEnable; pipeline.DepthTestEnable = state.DepthTest.TestEnable;
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable; pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
pipeline.FrontFace = state.FrontFace.Convert(); pipeline.FrontFace = state.FrontFace.Convert();
pipeline.HasDepthStencil = state.DepthStencilEnable;
pipeline.LineWidth = state.LineWidth;
pipeline.LogicOpEnable = state.LogicOpEnable;
pipeline.LogicOp = state.LogicOp.Convert();
pipeline.PatchControlPoints = state.PatchControlPoints;
pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.SamplesCount = (uint)state.SamplesCount;
if (gd.Capabilities.SupportsMultiView) if (gd.Capabilities.SupportsMultiView)
{ {
pipeline.ScissorsCount = Constants.MaxViewports; pipeline.ScissorsCount = Constants.MaxViewports;
@@ -204,7 +200,9 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.ViewportsCount = 1; pipeline.ViewportsCount = 1;
} }
pipeline.StencilTestEnable = state.StencilTest.TestEnable; pipeline.DepthBiasEnable = state.BiasEnable != 0;
// Stencil masks and ref are dynamic, so are 0 in the Vulkan pipeline.
pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert(); pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert(); pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
@@ -215,29 +213,10 @@ namespace Ryujinx.Graphics.Vulkan
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();
}
if (!extendedDynamicState2.ExtendedDynamicState2) pipeline.StencilTestEnable = state.StencilTest.TestEnable;
{
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.DepthBiasEnable = (state.BiasEnable != 0) &&
(state.DepthBiasFactor != 0 && state.DepthBiasUnits != 0);
}
if (!extendedDynamicState2.ExtendedDynamicState2LogicOp) pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
{
pipeline.LogicOp = state.LogicOp.Convert();
}
if (!extendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
pipeline.PatchControlPoints = state.PatchControlPoints;
}
pipeline.SamplesCount = (uint)state.SamplesCount;
pipeline.LogicOpEnable = state.LogicOpEnable;
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
@@ -262,7 +241,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
int descriptorIndex = 1; int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, extendedDynamicState && (!gd.IsMoltenVk || gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex); pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
for (int i = 0; i < vbCount; i++) for (int i = 0; i < vbCount; i++)
{ {
@@ -282,7 +261,7 @@ namespace Ryujinx.Graphics.Vulkan
// TODO: Support divisor > 1 // TODO: Support divisor > 1
pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription( pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1, (uint)i + 1,
extendedDynamicState && (!gd.IsMoltenVk || gd.SupportsMTL31) ? null : (uint)alignedStride, (uint)alignedStride,
inputRate); inputRate);
} }
} }
@@ -2,7 +2,6 @@ using Ryujinx.Common.Memory;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.EXT;
using System; using System;
using System.Numerics;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -11,7 +10,6 @@ namespace Ryujinx.Graphics.Vulkan
private float _depthBiasSlopeFactor; private float _depthBiasSlopeFactor;
private float _depthBiasConstantFactor; private float _depthBiasConstantFactor;
private float _depthBiasClamp; private float _depthBiasClamp;
private bool _depthBiasEnable;
public int ScissorsCount; public int ScissorsCount;
private Array16<Rect2D> _scissors; private Array16<Rect2D> _scissors;
@@ -23,41 +21,13 @@ namespace Ryujinx.Graphics.Vulkan
private uint _frontWriteMask; private uint _frontWriteMask;
private uint _frontReference; private uint _frontReference;
private StencilOp _backFailOp;
private StencilOp _backPassOp;
private StencilOp _backDepthFailOp;
private CompareOp _backCompareOp;
private StencilOp _frontFailOp;
private StencilOp _frontPassOp;
private StencilOp _frontDepthFailOp;
private CompareOp _frontCompareOp;
private float _lineWidth;
public bool StencilTestEnable;
public bool DepthTestEnable;
public bool DepthWriteEnable;
private CompareOp _depthCompareOp;
private Array4<float> _blendConstants; private Array4<float> _blendConstants;
private FeedbackLoopAspects _feedbackLoopAspects;
public uint ViewportsCount; public uint ViewportsCount;
public Array16<Viewport> Viewports; public Array16<Viewport> Viewports;
public CullModeFlags CullMode;
private FrontFace _frontFace;
private bool _discard;
private LogicOp _logicOp;
private uint _patchControlPoints;
public PrimitiveTopology Topology;
private bool _primitiveRestartEnable;
[Flags] [Flags]
private enum DirtyFlags private enum DirtyFlags
{ {
@@ -67,21 +37,8 @@ namespace Ryujinx.Graphics.Vulkan
Scissor = 1 << 2, Scissor = 1 << 2,
Stencil = 1 << 3, Stencil = 1 << 3,
Viewport = 1 << 4, Viewport = 1 << 4,
CullMode = 1 << 5, FeedbackLoop = 1 << 5,
FrontFace = 1 << 6, All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
DepthTestBool = 1 << 7,
DepthTestCompareOp = 1 << 8,
StencilTestEnableAndStencilOp = 1 << 9,
LineWidth = 1 << 10,
RasterDiscard = 1 << 11,
LogicOp = 1 << 12,
PatchControlPoints = 1 << 13,
PrimitiveRestart = 1 << 14,
PrimitiveTopology = 1 << 15,
DepthBiasEnable = 1 << 16,
Standard = Blend | DepthBias | Scissor | Stencil | Viewport,
Extended = CullMode | FrontFace | DepthTestBool | DepthTestCompareOp | StencilTestEnableAndStencilOp | PrimitiveTopology,
Extended2 = RasterDiscard | PrimitiveRestart | DepthBiasEnable,
} }
private DirtyFlags _dirty; private DirtyFlags _dirty;
@@ -92,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
_blendConstants[1] = g; _blendConstants[1] = g;
_blendConstants[2] = b; _blendConstants[2] = b;
_blendConstants[3] = a; _blendConstants[3] = a;
_dirty |= DirtyFlags.Blend; _dirty |= DirtyFlags.Blend;
} }
@@ -104,64 +62,15 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.DepthBias; _dirty |= DirtyFlags.DepthBias;
} }
public void SetDepthBiasEnable(bool enable)
{
_depthBiasEnable = enable;
_dirty |= DirtyFlags.DepthBiasEnable;
}
public void SetScissor(int index, Rect2D scissor) public void SetScissor(int index, Rect2D scissor)
{ {
_scissors[index] = scissor; _scissors[index] = scissor;
_dirty |= DirtyFlags.Scissor; _dirty |= DirtyFlags.Scissor;
} }
public void SetDepthTestBool(bool testEnable, bool writeEnable) public void SetStencilMasks(
{ uint backCompareMask,
DepthTestEnable = testEnable;
DepthWriteEnable = writeEnable;
_dirty |= DirtyFlags.DepthTestBool;
}
public void SetDepthTestCompareOp(CompareOp depthTestOp)
{
_depthCompareOp = depthTestOp;
_dirty |= DirtyFlags.DepthTestCompareOp;
}
public void SetStencilTestandOp(
StencilOp backFailOp,
StencilOp backPassOp,
StencilOp backDepthFailOp,
CompareOp backCompareOp,
StencilOp frontFailOp,
StencilOp frontPassOp,
StencilOp frontDepthFailOp,
CompareOp frontCompareOp,
bool stencilTestEnable)
{
_backFailOp = backFailOp;
_backPassOp = backPassOp;
_backDepthFailOp = backDepthFailOp;
_backCompareOp = backCompareOp;
_frontFailOp = frontFailOp;
_frontPassOp = frontPassOp;
_frontDepthFailOp = frontDepthFailOp;
_frontCompareOp = frontCompareOp;
StencilTestEnable = stencilTestEnable;
_dirty |= DirtyFlags.StencilTestEnableAndStencilOp;
}
public void SetStencilTest(bool stencilTestEnable)
{
StencilTestEnable = stencilTestEnable;
_dirty |= DirtyFlags.StencilTestEnableAndStencilOp;
}
public void SetStencilMask(uint backCompareMask,
uint backWriteMask, uint backWriteMask,
uint backReference, uint backReference,
uint frontCompareMask, uint frontCompareMask,
@@ -174,180 +83,72 @@ namespace Ryujinx.Graphics.Vulkan
_frontCompareMask = frontCompareMask; _frontCompareMask = frontCompareMask;
_frontWriteMask = frontWriteMask; _frontWriteMask = frontWriteMask;
_frontReference = frontReference; _frontReference = frontReference;
_dirty |= DirtyFlags.Stencil; _dirty |= DirtyFlags.Stencil;
} }
public void SetViewport(int index, Viewport viewport) public void SetViewport(int index, Viewport viewport)
{ {
Viewports[index] = viewport; Viewports[index] = viewport;
_dirty |= DirtyFlags.Viewport; _dirty |= DirtyFlags.Viewport;
} }
public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount) public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount)
{
if (!Viewports.Equals(viewports) || ViewportsCount != viewportsCount)
{ {
Viewports = viewports; Viewports = viewports;
ViewportsCount = viewportsCount; ViewportsCount = viewportsCount;
if (ViewportsCount != 0) if (ViewportsCount != 0)
{ {
_dirty |= DirtyFlags.Viewport; _dirty |= DirtyFlags.Viewport;
} }
} }
public void SetFeedbackLoop(FeedbackLoopAspects aspects)
{
_feedbackLoopAspects = aspects;
_dirty |= DirtyFlags.FeedbackLoop;
} }
public void SetCullMode(CullModeFlags cullMode) public void ForceAllDirty()
{ {
CullMode = cullMode; _dirty = DirtyFlags.All;
_dirty |= DirtyFlags.CullMode;
}
public void SetFrontFace(FrontFace frontFace)
{
_frontFace = frontFace;
_dirty |= DirtyFlags.FrontFace;
}
public void SetLineWidth(float width)
{
_lineWidth = width;
_dirty |= DirtyFlags.LineWidth;
}
public void SetRasterizerDiscard(bool discard)
{
_discard = discard;
_dirty |= DirtyFlags.RasterDiscard;
}
public void SetPrimitiveRestartEnable(bool primitiveRestart)
{
_primitiveRestartEnable = primitiveRestart;
_dirty |= DirtyFlags.PrimitiveRestart;
}
public void SetPrimitiveTopology(PrimitiveTopology primitiveTopology)
{
Topology = primitiveTopology;
_dirty |= DirtyFlags.PrimitiveTopology;
}
public void SetLogicOp(LogicOp op)
{
_logicOp = op;
_dirty |= DirtyFlags.LogicOp;
}
public void SetPatchControlPoints(uint points)
{
_patchControlPoints = points;
_dirty |= DirtyFlags.PatchControlPoints;
}
public void ForceAllDirty(VulkanRenderer gd)
{
_dirty = DirtyFlags.Standard;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
_dirty |= DirtyFlags.Extended;
}
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2)
{
_dirty |= DirtyFlags.Extended2;
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
_dirty |= DirtyFlags.LogicOp;
}
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
_dirty |= DirtyFlags.PatchControlPoints;
}
}
if (!gd.IsMoltenVk)
{
_dirty |= DirtyFlags.LineWidth;
}
} }
public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer) public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
{ {
if (_dirty == DirtyFlags.None) Vk api = gd.Api;
if (_dirty.HasFlag(DirtyFlags.Blend))
{ {
return;
}
var api = gd.Api;
var extendedStateApi = gd.ExtendedDynamicStateApi;
var extendedState2Api = gd.ExtendedDynamicState2Api;
DirtyFlags dirtyFlags = _dirty;
while (dirtyFlags != DirtyFlags.None)
{
int bitIndex = BitOperations.TrailingZeroCount((uint)dirtyFlags);
DirtyFlags currentFlag = (DirtyFlags)(1 << bitIndex);
switch (currentFlag)
{
case DirtyFlags.Blend:
RecordBlend(api, commandBuffer); RecordBlend(api, commandBuffer);
break;
case DirtyFlags.DepthBias:
RecordDepthBias(api, commandBuffer);
break;
case DirtyFlags.Scissor:
RecordScissor(gd, commandBuffer);
break;
case DirtyFlags.Stencil:
RecordStencil(api, commandBuffer);
break;
case DirtyFlags.Viewport:
RecordViewport(gd, commandBuffer);
break;
case DirtyFlags.CullMode:
RecordCullMode(extendedStateApi, commandBuffer);
break;
case DirtyFlags.FrontFace:
RecordFrontFace(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthTestBool:
RecordDepthTestBool(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthTestCompareOp:
RecordDepthTestCompareOp(extendedStateApi, commandBuffer);
break;
case DirtyFlags.StencilTestEnableAndStencilOp:
RecordStencilTestAndOp(extendedStateApi, commandBuffer);
break;
case DirtyFlags.LineWidth:
RecordLineWidth(api, commandBuffer);
break;
case DirtyFlags.RasterDiscard:
RecordRasterizationDiscard(extendedState2Api, commandBuffer);
break;
case DirtyFlags.LogicOp:
RecordLogicOp(extendedState2Api, commandBuffer);
break;
case DirtyFlags.PatchControlPoints:
RecordPatchControlPoints(extendedState2Api, commandBuffer);
break;
case DirtyFlags.PrimitiveRestart:
RecordPrimitiveRestartEnable(gd, commandBuffer);
break;
case DirtyFlags.PrimitiveTopology:
RecordPrimitiveTopology(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthBiasEnable:
RecordDepthBiasEnable(extendedState2Api, commandBuffer);
break;
} }
dirtyFlags &= ~currentFlag; if (_dirty.HasFlag(DirtyFlags.DepthBias))
{
RecordDepthBias(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.Scissor))
{
RecordScissor(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.Stencil))
{
RecordStencilMasks(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.Viewport))
{
RecordViewport(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
} }
_dirty = DirtyFlags.None; _dirty = DirtyFlags.None;
@@ -363,27 +164,15 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor); api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor);
} }
private readonly void RecordDepthBiasEnable(ExtExtendedDynamicState2 gd, CommandBuffer commandBuffer) private void RecordScissor(Vk api, CommandBuffer commandBuffer)
{
gd.CmdSetDepthBiasEnable(commandBuffer, _depthBiasEnable);
}
private void RecordScissor(VulkanRenderer gd, CommandBuffer commandBuffer)
{ {
if (ScissorsCount != 0) if (ScissorsCount != 0)
{ {
if (gd.Capabilities.SupportsExtendedDynamicState) api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
{
gd.ExtendedDynamicStateApi.CmdSetScissorWithCount(commandBuffer, (uint)ScissorsCount, _scissors.AsSpan());
}
else
{
gd.Api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
}
} }
} }
private readonly void RecordStencil(Vk api, CommandBuffer commandBuffer) private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
{ {
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask); api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask);
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask); api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask);
@@ -393,107 +182,24 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference); api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference);
} }
private readonly void RecordStencilTestAndOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer) private void RecordViewport(Vk api, CommandBuffer commandBuffer)
{ {
api.CmdSetStencilTestEnable(commandBuffer, StencilTestEnable); if (ViewportsCount != 0)
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceBackBit, _backFailOp, _backPassOp, _backDepthFailOp, _backCompareOp);
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontFailOp, _frontPassOp, _frontDepthFailOp, _frontCompareOp);
}
private void RecordViewport(VulkanRenderer gd, CommandBuffer commandBuffer)
{ {
if (ViewportsCount == 0) api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
{ {
return; ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
}
if (gd.Capabilities.SupportsExtendedDynamicState) if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
{ {
gd.ExtendedDynamicStateApi.CmdSetViewportWithCount(commandBuffer, ViewportsCount, Viewports.AsSpan()); aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
}
else
{
gd.Api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
} }
private readonly void RecordCullMode(ExtExtendedDynamicState api, CommandBuffer commandBuffer) api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
{
api.CmdSetCullMode(commandBuffer, CullMode);
}
private readonly void RecordFrontFace(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetFrontFace(commandBuffer, _frontFace);
}
private readonly void RecordDepthTestBool(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetDepthTestEnable(commandBuffer, DepthTestEnable);
api.CmdSetDepthWriteEnable(commandBuffer, DepthWriteEnable);
}
private readonly void RecordDepthTestCompareOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetDepthCompareOp(commandBuffer, _depthCompareOp);
}
private readonly void RecordRasterizationDiscard(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetRasterizerDiscardEnable(commandBuffer, _discard);
}
private readonly void RecordPrimitiveRestartEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
bool primitiveRestartEnable = _primitiveRestartEnable;
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart ||
Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
primitiveRestartEnable &= topologySupportsRestart;
// Cannot disable primitiveRestartEnable for these Topologies on MacOS.
if (gd.IsMoltenVk)
{
primitiveRestartEnable = true;
}
gd.ExtendedDynamicState2Api.CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable);
}
private readonly void RecordPrimitiveTopology(ExtExtendedDynamicState extendedDynamicStateApi, CommandBuffer commandBuffer)
{
extendedDynamicStateApi.CmdSetPrimitiveTopology(commandBuffer, Topology);
}
private readonly void RecordLogicOp(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetLogicOp(commandBuffer, _logicOp);
}
private readonly void RecordPatchControlPoints(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetPatchControlPoints(commandBuffer, _patchControlPoints);
}
private readonly void RecordLineWidth(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetLineWidth(commandBuffer, _lineWidth);
} }
} }
} }
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
_activeBufferMirrors = []; _activeBufferMirrors = [];
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
IsMainPipeline = true;
} }
private void CopyPendingQuery() private void CopyPendingQuery()
@@ -8,16 +8,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
} }
public void SetRenderTarget(TextureView view, uint width, uint height, bool signalChange = true) public void SetRenderTarget(TextureView view, uint width, uint height)
{ {
CreateFramebuffer(view, width, height); CreateFramebuffer(view, width, height);
CreateRenderPass(); CreateRenderPass();
if (signalChange)
{
SignalStateChange(); SignalStateChange();
} }
}
private void CreateFramebuffer(TextureView view, uint width, uint height) private void CreateFramebuffer(TextureView view, uint width, uint height)
{ {
+376 -417
View File
@@ -7,236 +7,302 @@ namespace Ryujinx.Graphics.Vulkan
{ {
struct PipelineState : IDisposable struct PipelineState : IDisposable
{ {
private const int MaxDynamicStatesCount = 23; private const int MaxDynamicStatesCount = 9;
public PipelineUid Internal; public PipelineUid Internal;
public PolygonMode PolygonMode public float LineWidth
{ {
readonly get => (PolygonMode)((Internal.Id0 >> 0) & 0x3FFFFFFF); readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 0) & 0xFFFFFFFF));
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFC0000000) | ((ulong)value << 0); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0);
} }
public uint StagesCount public float DepthBiasClamp
{ {
readonly get => (byte)((Internal.Id0 >> 30) & 0xFF); readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 32) & 0xFFFFFFFF));
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32);
} }
public uint VertexAttributeDescriptionsCount public float DepthBiasConstantFactor
{ {
readonly get => (byte)((Internal.Id0 >> 38) & 0xFF); readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 0) & 0xFFFFFFFF));
set => Internal.Id0 = (Internal.Id0 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38); set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0);
} }
public uint VertexBindingDescriptionsCount public float DepthBiasSlopeFactor
{ {
readonly get => (byte)((Internal.Id0 >> 46) & 0xFF); readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 32) & 0xFFFFFFFF));
set => Internal.Id0 = (Internal.Id0 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46); set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32);
} }
public uint ViewportsCount public uint StencilFrontCompareMask
{
readonly get => (byte)((Internal.Id0 >> 54) & 0xFF);
set => Internal.Id0 = (Internal.Id0 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
}
public uint ScissorsCount
{
readonly get => (byte)((Internal.Id1 >> 0) & 0xFF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
}
public uint ColorBlendAttachmentStateCount
{
readonly get => (byte)((Internal.Id1 >> 8) & 0xFF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
}
public PrimitiveTopology Topology
{
readonly get => (PrimitiveTopology)((Internal.Id1 >> 16) & 0xF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
}
public LogicOp LogicOp
{
readonly get => (LogicOp)((Internal.Id1 >> 20) & 0xF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
}
public CompareOp DepthCompareOp
{
readonly get => (CompareOp)((Internal.Id1 >> 24) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
}
public StencilOp StencilFrontFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 27) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
}
public StencilOp StencilFrontPassOp
{
readonly get => (StencilOp)((Internal.Id1 >> 30) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
}
public StencilOp StencilFrontDepthFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 33) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
}
public CompareOp StencilFrontCompareOp
{
readonly get => (CompareOp)((Internal.Id1 >> 36) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
}
public StencilOp StencilBackFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 39) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
}
public StencilOp StencilBackPassOp
{
readonly get => (StencilOp)((Internal.Id1 >> 42) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
}
public StencilOp StencilBackDepthFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 45) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
}
public CompareOp StencilBackCompareOp
{
readonly get => (CompareOp)((Internal.Id1 >> 48) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
}
public CullModeFlags CullMode
{
readonly get => (CullModeFlags)((Internal.Id1 >> 51) & 0x3);
set => Internal.Id1 = (Internal.Id1 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
}
public bool PrimitiveRestartEnable
{
readonly get => ((Internal.Id1 >> 53) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
}
public bool DepthClampEnable
{
readonly get => ((Internal.Id1 >> 54) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
}
public bool RasterizerDiscardEnable
{
readonly get => ((Internal.Id1 >> 55) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
}
public FrontFace FrontFace
{
readonly get => (FrontFace)((Internal.Id1 >> 56) & 0x1);
set => Internal.Id1 = (Internal.Id1 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
}
public bool DepthBiasEnable
{
readonly get => ((Internal.Id1 >> 57) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
}
public bool DepthTestEnable
{
readonly get => ((Internal.Id1 >> 58) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
}
public bool DepthWriteEnable
{
readonly get => ((Internal.Id1 >> 59) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
}
public bool DepthBoundsTestEnable
{
readonly get => ((Internal.Id1 >> 60) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
}
public bool StencilTestEnable
{
readonly get => ((Internal.Id1 >> 61) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
}
public bool LogicOpEnable
{
readonly get => ((Internal.Id1 >> 62) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
}
public bool HasDepthStencil
{
readonly get => ((Internal.Id1 >> 63) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
}
public uint PatchControlPoints
{ {
readonly get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF); readonly get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF);
set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF00000000) | ((ulong)value << 0); set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
} }
public uint SamplesCount public uint StencilFrontWriteMask
{ {
readonly get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF); readonly get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF);
set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF) | ((ulong)value << 32); set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF) | ((ulong)value << 32);
} }
public uint StencilFrontReference
{
readonly get => (uint)((Internal.Id3 >> 0) & 0xFFFFFFFF);
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
}
public uint StencilBackCompareMask
{
readonly get => (uint)((Internal.Id3 >> 32) & 0xFFFFFFFF);
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF) | ((ulong)value << 32);
}
public uint StencilBackWriteMask
{
readonly get => (uint)((Internal.Id4 >> 0) & 0xFFFFFFFF);
set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
}
public uint StencilBackReference
{
readonly get => (uint)((Internal.Id4 >> 32) & 0xFFFFFFFF);
set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32);
}
public PolygonMode PolygonMode
{
readonly get => (PolygonMode)((Internal.Id5 >> 0) & 0x3FFFFFFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFFC0000000) | ((ulong)value << 0);
}
public uint StagesCount
{
readonly get => (byte)((Internal.Id5 >> 30) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30);
}
public uint VertexAttributeDescriptionsCount
{
readonly get => (byte)((Internal.Id5 >> 38) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38);
}
public uint VertexBindingDescriptionsCount
{
readonly get => (byte)((Internal.Id5 >> 46) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46);
}
public uint ViewportsCount
{
readonly get => (byte)((Internal.Id5 >> 54) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
}
public uint ScissorsCount
{
readonly get => (byte)((Internal.Id6 >> 0) & 0xFF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
}
public uint ColorBlendAttachmentStateCount
{
readonly get => (byte)((Internal.Id6 >> 8) & 0xFF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
}
public PrimitiveTopology Topology
{
readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
}
public LogicOp LogicOp
{
readonly get => (LogicOp)((Internal.Id6 >> 20) & 0xF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
}
public CompareOp DepthCompareOp
{
readonly get => (CompareOp)((Internal.Id6 >> 24) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
}
public StencilOp StencilFrontFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 27) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
}
public StencilOp StencilFrontPassOp
{
readonly get => (StencilOp)((Internal.Id6 >> 30) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
}
public StencilOp StencilFrontDepthFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 33) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
}
public CompareOp StencilFrontCompareOp
{
readonly get => (CompareOp)((Internal.Id6 >> 36) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
}
public StencilOp StencilBackFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 39) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
}
public StencilOp StencilBackPassOp
{
readonly get => (StencilOp)((Internal.Id6 >> 42) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
}
public StencilOp StencilBackDepthFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 45) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
}
public CompareOp StencilBackCompareOp
{
readonly get => (CompareOp)((Internal.Id6 >> 48) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
}
public CullModeFlags CullMode
{
readonly get => (CullModeFlags)((Internal.Id6 >> 51) & 0x3);
set => Internal.Id6 = (Internal.Id6 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
}
public bool PrimitiveRestartEnable
{
readonly get => ((Internal.Id6 >> 53) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
}
public bool DepthClampEnable
{
readonly get => ((Internal.Id6 >> 54) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
}
public bool RasterizerDiscardEnable
{
readonly get => ((Internal.Id6 >> 55) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
}
public FrontFace FrontFace
{
readonly get => (FrontFace)((Internal.Id6 >> 56) & 0x1);
set => Internal.Id6 = (Internal.Id6 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
}
public bool DepthBiasEnable
{
readonly get => ((Internal.Id6 >> 57) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
}
public bool DepthTestEnable
{
readonly get => ((Internal.Id6 >> 58) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
}
public bool DepthWriteEnable
{
readonly get => ((Internal.Id6 >> 59) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
}
public bool DepthBoundsTestEnable
{
readonly get => ((Internal.Id6 >> 60) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
}
public bool StencilTestEnable
{
readonly get => ((Internal.Id6 >> 61) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
}
public bool LogicOpEnable
{
readonly get => ((Internal.Id6 >> 62) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
}
public bool HasDepthStencil
{
readonly get => ((Internal.Id6 >> 63) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
}
public uint PatchControlPoints
{
readonly get => (uint)((Internal.Id7 >> 0) & 0xFFFFFFFF);
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
}
public uint SamplesCount
{
readonly get => (uint)((Internal.Id7 >> 32) & 0xFFFFFFFF);
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF) | ((ulong)value << 32);
}
public bool AlphaToCoverageEnable public bool AlphaToCoverageEnable
{ {
readonly get => ((Internal.Id3 >> 0) & 0x1) != 0UL; readonly get => ((Internal.Id8 >> 0) & 0x1) != 0UL;
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0);
} }
public bool AlphaToOneEnable public bool AlphaToOneEnable
{ {
readonly get => ((Internal.Id3 >> 1) & 0x1) != 0UL; readonly get => ((Internal.Id8 >> 1) & 0x1) != 0UL;
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1);
} }
public bool AdvancedBlendSrcPreMultiplied public bool AdvancedBlendSrcPreMultiplied
{ {
readonly get => ((Internal.Id3 >> 2) & 0x1) != 0UL; readonly get => ((Internal.Id8 >> 2) & 0x1) != 0UL;
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2);
} }
public bool AdvancedBlendDstPreMultiplied public bool AdvancedBlendDstPreMultiplied
{ {
readonly get => ((Internal.Id3 >> 3) & 0x1) != 0UL; readonly get => ((Internal.Id8 >> 3) & 0x1) != 0UL;
set => Internal.Id3 = (Internal.Id3 & 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.Id3 >> 4) & 0x3); readonly get => (BlendOverlapEXT)((Internal.Id8 >> 4) & 0x3);
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
} }
public bool DepthMode public bool DepthMode
{ {
readonly get => ((Internal.Id3 >> 6) & 0x1) != 0UL; readonly get => ((Internal.Id8 >> 6) & 0x1) != 0UL;
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
}
public FeedbackLoopAspects FeedbackLoopAspects
{
readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
} }
public bool HasTessellationControlShader; public bool HasTessellationControlShader;
@@ -246,11 +312,7 @@ namespace Ryujinx.Graphics.Vulkan
private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2; private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2;
private bool _supportsExtDynamicState; public void Initialize()
private PhysicalDeviceExtendedDynamicState2FeaturesEXT _supportsExtDynamicState2;
private uint _blendEnables;
public void Initialize(HardwareCapabilities capabilities)
{ {
HasTessellationControlShader = false; HasTessellationControlShader = false;
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages); Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
@@ -259,53 +321,9 @@ namespace Ryujinx.Graphics.Vulkan
AdvancedBlendDstPreMultiplied = true; AdvancedBlendDstPreMultiplied = true;
AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt; AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt;
LineWidth = 1f;
SamplesCount = 1;
DepthMode = true; DepthMode = true;
PolygonMode = PolygonMode.Fill;
DepthBoundsTestEnable = false;
_supportsExtDynamicState = capabilities.SupportsExtendedDynamicState;
_supportsExtDynamicState2 = capabilities.SupportsExtendedDynamicState2;
if (_supportsExtDynamicState)
{
StencilFrontFailOp = 0;
StencilFrontPassOp = 0;
StencilFrontDepthFailOp = 0;
StencilFrontCompareOp = 0;
StencilBackFailOp = 0;
StencilBackPassOp = 0;
StencilBackDepthFailOp = 0;
StencilBackCompareOp = 0;
ViewportsCount = 0;
ScissorsCount = 0;
CullMode = 0;
FrontFace = 0;
DepthTestEnable = false;
DepthWriteEnable = false;
DepthCompareOp = 0;
StencilTestEnable = false;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2)
{
PrimitiveRestartEnable = false;
DepthBiasEnable = false;
RasterizerDiscardEnable = false;
if (_supportsExtDynamicState2.ExtendedDynamicState2LogicOp)
{
LogicOp = 0;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
PatchControlPoints = 0;
}
}
} }
public unsafe Auto<DisposablePipeline> CreateComputePipeline( public unsafe Auto<DisposablePipeline> CreateComputePipeline(
@@ -323,6 +341,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.ComputePipelineCreateInfo, SType = StructureType.ComputePipelineCreateInfo,
Stage = Stages[0], Stage = Stages[0],
BasePipelineIndex = -1,
Layout = PipelineLayout, Layout = PipelineLayout,
}; };
@@ -358,74 +377,6 @@ namespace Ryujinx.Graphics.Vulkan
return pipeline; return pipeline;
} }
private void CheckCapability(VulkanRenderer gd)
{
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here.
LogicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
if (!_supportsExtDynamicState)
{
DepthWriteEnable = DepthWriteEnable && DepthTestEnable;
DepthCompareOp = DepthTestEnable ? DepthCompareOp : default;
}
if (!_supportsExtDynamicState2.ExtendedDynamicState2LogicOp)
{
LogicOp = LogicOpEnable ? LogicOp : default;
}
if (!_supportsExtDynamicState2.ExtendedDynamicState2)
{
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart ||
Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
PrimitiveRestartEnable &= topologySupportsRestart;
}
if (_supportsExtDynamicState)
{
Topology = Topology.ConvertToClass();
}
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology;
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{
_blendEnables = 0;
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
while (attachmentIntegerFormatMask != 0)
{
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
if (Internal.ColorBlendAttachmentState[i].BlendEnable)
{
_blendEnables |= 1u << i;
}
Internal.ColorBlendAttachmentState[i].BlendEnable = false;
attachmentIntegerFormatMask &= ~(1u << i);
}
}
}
public unsafe Auto<DisposablePipeline> CreateGraphicsPipeline( public unsafe Auto<DisposablePipeline> CreateGraphicsPipeline(
VulkanRenderer gd, VulkanRenderer gd,
Device device, Device device,
@@ -434,17 +385,6 @@ namespace Ryujinx.Graphics.Vulkan
RenderPass renderPass, RenderPass renderPass,
bool throwOnError = false) bool throwOnError = false)
{ {
CheckCapability(gd);
// Using patches topology without a tessellation shader is invalid.
// If we find such a case, return null pipeline to skip the draw.
if (Topology == PrimitiveTopology.PatchList && !HasTessellationControlShader)
{
program.AddGraphicsPipeline(ref Internal, null);
return null;
}
if (program.TryGetGraphicsPipeline(ref Internal, out var pipeline)) if (program.TryGetGraphicsPipeline(ref Internal, out var pipeline))
{ {
return pipeline; return pipeline;
@@ -454,7 +394,7 @@ namespace Ryujinx.Graphics.Vulkan
bool isMoltenVk = gd.IsMoltenVk; bool isMoltenVk = gd.IsMoltenVk;
if (isMoltenVk && !_supportsExtDynamicState) if (isMoltenVk)
{ {
UpdateVertexAttributeDescriptions(gd); UpdateVertexAttributeDescriptions(gd);
} }
@@ -468,30 +408,69 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PipelineVertexInputStateCreateInfo, SType = StructureType.PipelineVertexInputStateCreateInfo,
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
PVertexAttributeDescriptions = isMoltenVk && !_supportsExtDynamicState ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
VertexBindingDescriptionCount = VertexBindingDescriptionsCount, VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
PVertexBindingDescriptions = pVertexBindingDescriptions, PVertexBindingDescriptions = pVertexBindingDescriptions,
}; };
// Using patches topology without a tessellation shader is invalid.
// If we find such a case, return null pipeline to skip the draw.
if (Topology == PrimitiveTopology.PatchList && !HasTessellationControlShader)
{
program.AddGraphicsPipeline(ref Internal, null);
return null;
}
bool primitiveRestartEnable = PrimitiveRestartEnable;
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart || Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
primitiveRestartEnable &= topologySupportsRestart;
var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo
{ {
SType = StructureType.PipelineInputAssemblyStateCreateInfo, SType = StructureType.PipelineInputAssemblyStateCreateInfo,
Topology = Topology, PrimitiveRestartEnable = primitiveRestartEnable,
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
}; };
PipelineTessellationStateCreateInfo tessellationState; var tessellationState = new PipelineTessellationStateCreateInfo
{
SType = StructureType.PipelineTessellationStateCreateInfo,
PatchControlPoints = PatchControlPoints,
};
var rasterizationState = new PipelineRasterizationStateCreateInfo var rasterizationState = new PipelineRasterizationStateCreateInfo
{ {
SType = StructureType.PipelineRasterizationStateCreateInfo, SType = StructureType.PipelineRasterizationStateCreateInfo,
DepthClampEnable = DepthClampEnable, DepthClampEnable = DepthClampEnable,
// When widelines feature is not supported it must be 1.0f, this will be ignored if Line Width dynamic state is supported RasterizerDiscardEnable = RasterizerDiscardEnable,
LineWidth = 1.0f, PolygonMode = PolygonMode,
LineWidth = LineWidth,
CullMode = CullMode,
FrontFace = FrontFace,
DepthBiasEnable = DepthBiasEnable,
}; };
var viewportState = new PipelineViewportStateCreateInfo var viewportState = new PipelineViewportStateCreateInfo
{ {
SType = StructureType.PipelineViewportStateCreateInfo, SType = StructureType.PipelineViewportStateCreateInfo,
ViewportCount = ViewportsCount,
ScissorCount = ScissorsCount,
}; };
if (gd.Capabilities.SupportsDepthClipControl) if (gd.Capabilities.SupportsDepthClipControl)
@@ -515,20 +494,6 @@ namespace Ryujinx.Graphics.Vulkan
AlphaToOneEnable = AlphaToOneEnable, AlphaToOneEnable = AlphaToOneEnable,
}; };
var depthStencilState = new PipelineDepthStencilStateCreateInfo
{
SType = StructureType.PipelineDepthStencilStateCreateInfo,
DepthBoundsTestEnable = DepthBoundsTestEnable,
};
if (!_supportsExtDynamicState)
{
rasterizationState.CullMode = CullMode;
rasterizationState.FrontFace = FrontFace;
viewportState.ViewportCount = ViewportsCount;
viewportState.ScissorCount = ScissorsCount;
var stencilFront = new StencilOpState( var stencilFront = new StencilOpState(
StencilFrontFailOp, StencilFrontFailOp,
StencilFrontPassOp, StencilFrontPassOp,
@@ -541,49 +506,59 @@ namespace Ryujinx.Graphics.Vulkan
StencilBackDepthFailOp, StencilBackDepthFailOp,
StencilBackCompareOp); StencilBackCompareOp);
depthStencilState.Front = stencilFront; var depthStencilState = new PipelineDepthStencilStateCreateInfo
depthStencilState.Back = stencilBack;
depthStencilState.StencilTestEnable = StencilTestEnable;
depthStencilState.DepthTestEnable = DepthTestEnable;
depthStencilState.DepthWriteEnable = DepthWriteEnable;
depthStencilState.DepthCompareOp = DepthCompareOp;
}
if (!_supportsExtDynamicState2.ExtendedDynamicState2)
{ {
SType = StructureType.PipelineDepthStencilStateCreateInfo,
inputAssemblyState.PrimitiveRestartEnable = PrimitiveRestartEnable; DepthTestEnable = DepthTestEnable,
rasterizationState.DepthBiasEnable = DepthBiasEnable; DepthWriteEnable = DepthWriteEnable,
rasterizationState.RasterizerDiscardEnable = RasterizerDiscardEnable; DepthCompareOp = DepthCompareOp,
} DepthBoundsTestEnable = false,
StencilTestEnable = StencilTestEnable,
if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints) Front = stencilFront,
{ Back = stencilBack,
tessellationState = new PipelineTessellationStateCreateInfo
{
SType = StructureType.PipelineTessellationStateCreateInfo,
PatchControlPoints = PatchControlPoints,
}; };
uint blendEnables = 0;
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
while (attachmentIntegerFormatMask != 0)
{
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
if (Internal.ColorBlendAttachmentState[i].BlendEnable)
{
blendEnables |= 1u << i;
} }
Internal.ColorBlendAttachmentState[i].BlendEnable = false;
attachmentIntegerFormatMask &= ~(1u << i);
}
}
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here.
bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
var colorBlendState = new PipelineColorBlendStateCreateInfo var colorBlendState = new PipelineColorBlendStateCreateInfo
{ {
SType = StructureType.PipelineColorBlendStateCreateInfo, SType = StructureType.PipelineColorBlendStateCreateInfo,
LogicOpEnable = logicOpEnable,
LogicOp = LogicOp,
AttachmentCount = ColorBlendAttachmentStateCount, AttachmentCount = ColorBlendAttachmentStateCount,
PAttachments = pColorBlendAttachmentState, PAttachments = pColorBlendAttachmentState,
LogicOpEnable = LogicOpEnable,
}; };
if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp) PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState;
{
colorBlendState.LogicOp = LogicOp;
}
if (!AdvancedBlendSrcPreMultiplied || if (!AdvancedBlendSrcPreMultiplied ||
!AdvancedBlendDstPreMultiplied || !AdvancedBlendDstPreMultiplied ||
AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt) AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt)
{ {
PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT
{ {
SType = StructureType.PipelineColorBlendAdvancedStateCreateInfoExt, SType = StructureType.PipelineColorBlendAdvancedStateCreateInfoExt,
SrcPremultiplied = AdvancedBlendSrcPreMultiplied, SrcPremultiplied = AdvancedBlendSrcPreMultiplied,
@@ -594,75 +569,64 @@ namespace Ryujinx.Graphics.Vulkan
colorBlendState.PNext = &colorBlendAdvancedState; colorBlendState.PNext = &colorBlendAdvancedState;
} }
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount]; DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
uint dynamicStatesCount = 7; int dynamicStatesCount = 7;
dynamicStates[0] = DynamicState.Viewport; dynamicStates[0] = DynamicState.Viewport;
dynamicStates[1] = DynamicState.Scissor; dynamicStates[1] = DynamicState.Scissor;
dynamicStates[2] = DynamicState.StencilCompareMask; dynamicStates[2] = DynamicState.DepthBias;
dynamicStates[3] = DynamicState.StencilWriteMask; dynamicStates[3] = DynamicState.StencilCompareMask;
dynamicStates[4] = DynamicState.StencilReference; dynamicStates[4] = DynamicState.StencilWriteMask;
dynamicStates[5] = DynamicState.BlendConstants; dynamicStates[5] = DynamicState.StencilReference;
dynamicStates[6] = DynamicState.DepthBias; dynamicStates[6] = DynamicState.BlendConstants;
if (!isMoltenVk) if (supportsExtDynamicState)
{ {
//LineWidth dynamic state is only supported on macOS when using Metal Private API on newer version of MoltenVK
dynamicStates[dynamicStatesCount++] = DynamicState.LineWidth;
}
if (_supportsExtDynamicState)
{
if (gd.SupportsMTL31 || !gd.IsMoltenVk)
{
// Requires Metal 3.1 and new MoltenVK, however extended dynamic states extension is not
// available on older versions of MVK, so we can safely check only OS version.
dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt; dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
} }
dynamicStates[0] = DynamicState.ViewportWithCountExt;
dynamicStates[1] = DynamicState.ScissorWithCountExt;
dynamicStates[dynamicStatesCount++] = DynamicState.CullModeExt;
dynamicStates[dynamicStatesCount++] = DynamicState.FrontFaceExt;
dynamicStates[dynamicStatesCount++] = DynamicState.DepthTestEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.DepthWriteEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.DepthCompareOpExt; if (supportsFeedbackLoopDynamicState)
dynamicStates[dynamicStatesCount++] = DynamicState.StencilTestEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.StencilOpExt;
dynamicStates[dynamicStatesCount++] = DynamicState.PrimitiveTopologyExt;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2)
{ {
dynamicStates[dynamicStatesCount++] = DynamicState.DepthBiasEnableExt; dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.RasterizerDiscardEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.PrimitiveRestartEnableExt;
if (_supportsExtDynamicState2.ExtendedDynamicState2LogicOp)
{
dynamicStates[dynamicStatesCount++] = DynamicState.LogicOpExt;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
dynamicStates[dynamicStatesCount++] = DynamicState.PatchControlPointsExt;
}
} }
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
{ {
SType = StructureType.PipelineDynamicStateCreateInfo, SType = StructureType.PipelineDynamicStateCreateInfo,
DynamicStateCount = dynamicStatesCount, DynamicStateCount = (uint)dynamicStatesCount,
PDynamicStates = dynamicStates, PDynamicStates = dynamicStates,
}; };
PipelineCreateFlags flags = 0;
if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
{
FeedbackLoopAspects aspects = FeedbackLoopAspects;
if ((aspects & FeedbackLoopAspects.Color) != 0)
{
flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
}
if ((aspects & FeedbackLoopAspects.Depth) != 0)
{
flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
}
}
var pipelineCreateInfo = new GraphicsPipelineCreateInfo var pipelineCreateInfo = new GraphicsPipelineCreateInfo
{ {
SType = StructureType.GraphicsPipelineCreateInfo, SType = StructureType.GraphicsPipelineCreateInfo,
Flags = flags,
StageCount = StagesCount, StageCount = StagesCount,
PStages = Stages.Pointer, PStages = Stages.Pointer,
PVertexInputState = &vertexInputState, PVertexInputState = &vertexInputState,
PInputAssemblyState = &inputAssemblyState, PInputAssemblyState = &inputAssemblyState,
PTessellationState = &tessellationState,
PViewportState = &viewportState, PViewportState = &viewportState,
PRasterizationState = &rasterizationState, PRasterizationState = &rasterizationState,
PMultisampleState = &multisampleState, PMultisampleState = &multisampleState,
@@ -673,11 +637,6 @@ namespace Ryujinx.Graphics.Vulkan
RenderPass = renderPass, RenderPass = renderPass,
}; };
if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
pipelineCreateInfo.PTessellationState = &tessellationState;
}
Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle); Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle);
if (throwOnError) if (throwOnError)
@@ -690,21 +649,21 @@ namespace Ryujinx.Graphics.Vulkan
return null; return null;
} }
// Restore previous blend enable values if we changed it.
while (blendEnables != 0)
{
int i = BitOperations.TrailingZeroCount(blendEnables);
Internal.ColorBlendAttachmentState[i].BlendEnable = true;
blendEnables &= ~(1u << i);
}
} }
pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle)); pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
program.AddGraphicsPipeline(ref Internal, pipeline); program.AddGraphicsPipeline(ref Internal, pipeline);
// Restore previous blend enable values if we changed it.
while (_blendEnables != 0)
{
int i = BitOperations.TrailingZeroCount(_blendEnables);
Internal.ColorBlendAttachmentState[i].BlendEnable = true;
_blendEnables &= ~(1u << i);
}
return pipeline; return pipeline;
} }
+21 -8
View File
@@ -11,17 +11,23 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public ulong Id0; public ulong Id0;
public ulong Id1; public ulong Id1;
public ulong Id2; public ulong Id2;
public ulong Id3; public ulong Id3;
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 38) & 0xFF); public ulong Id4;
private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 46) & 0xFF); public ulong Id5;
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id1 >> 8) & 0xFF); public ulong Id6;
private readonly bool HasDepthStencil => ((Id1 >> 63) & 0x1) != 0UL;
public ulong Id7;
public ulong Id8;
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id5 >> 38) & 0xFF);
private readonly uint VertexBindingDescriptionsCount => (byte)((Id5 >> 46) & 0xFF);
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id6 >> 8) & 0xFF);
private readonly bool HasDepthStencil => ((Id6 >> 63) & 0x1) != 0UL;
public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions; public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions;
public Array32<VertexInputBindingDescription> VertexBindingDescriptions; public Array33<VertexInputBindingDescription> VertexBindingDescriptions;
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState; public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
public Array9<Format> AttachmentFormats; public Array9<Format> AttachmentFormats;
public uint AttachmentIntegerFormatMask; public uint AttachmentIntegerFormatMask;
@@ -34,7 +40,9 @@ namespace Ryujinx.Graphics.Vulkan
public bool Equals(ref PipelineUid other) public bool Equals(ref PipelineUid other)
{ {
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, Vector128<byte>>(ref Id7).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id7)))
{ {
return false; return false;
} }
@@ -72,7 +80,12 @@ namespace Ryujinx.Graphics.Vulkan
ulong hash64 = Id0 * 23 ^ ulong hash64 = Id0 * 23 ^
Id1 * 23 ^ Id1 * 23 ^
Id2 * 23 ^ Id2 * 23 ^
Id3 * 23; Id3 * 23 ^
Id4 * 23 ^
Id5 * 23 ^
Id6 * 23 ^
Id7 * 23 ^
Id8 * 23;
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
{ {
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using PrimitiveTopology = Silk.NET.Vulkan.PrimitiveTopology;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -539,7 +538,7 @@ namespace Ryujinx.Graphics.Vulkan
public void CreateBackgroundComputePipeline() public void CreateBackgroundComputePipeline()
{ {
PipelineState pipeline = new(); PipelineState pipeline = new();
pipeline.Initialize(_gd.Capabilities); pipeline.Initialize();
pipeline.Stages[0] = _shaders[0].GetInfo(); pipeline.Stages[0] = _shaders[0].GetInfo();
pipeline.StagesCount = 1; pipeline.StagesCount = 1;
+73 -3
View File
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using Format = Ryujinx.Graphics.GAL.Format; using Format = Ryujinx.Graphics.GAL.Format;
using VkBuffer = Silk.NET.Vulkan.Buffer; using VkBuffer = Silk.NET.Vulkan.Buffer;
using VkFormat = Silk.NET.Vulkan.Format; using VkFormat = Silk.NET.Vulkan.Format;
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
{ {
class TextureStorage : IDisposable class TextureStorage : IDisposable
{ {
private struct TextureSliceInfo
{
public int BindCount;
}
private const MemoryPropertyFlags DefaultImageMemoryFlags = private const MemoryPropertyFlags DefaultImageMemoryFlags =
MemoryPropertyFlags.DeviceLocalBit; MemoryPropertyFlags.DeviceLocalBit;
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Image _image; private readonly Image _image;
private readonly Auto<DisposableImage> _imageAuto; private readonly Auto<DisposableImage> _imageAuto;
private readonly Auto<MemoryAllocation> _allocationAuto; private readonly Auto<MemoryAllocation> _allocationAuto;
private readonly int _depthOrLayers;
private Auto<MemoryAllocation> _foreignAllocationAuto; private Auto<MemoryAllocation> _foreignAllocationAuto;
private Dictionary<Format, TextureStorage> _aliasedStorages; private Dictionary<Format, TextureStorage> _aliasedStorages;
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
private int _viewsCount; private int _viewsCount;
private readonly ulong _size; private readonly ulong _size;
private int _bindCount;
private readonly TextureSliceInfo[] _slices;
public VkFormat VkFormat { get; } public VkFormat VkFormat { get; }
public unsafe TextureStorage( public unsafe TextureStorage(
@@ -75,6 +85,7 @@ namespace Ryujinx.Graphics.Vulkan
var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1); var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
VkFormat = format; VkFormat = format;
_depthOrLayers = info.GetDepthOrLayers();
var type = info.Target.Convert(); var type = info.Target.Convert();
@@ -82,7 +93,7 @@ namespace Ryujinx.Graphics.Vulkan
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
var usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true); var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
@@ -150,6 +161,8 @@ namespace Ryujinx.Graphics.Vulkan
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General); InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
} }
_slices = new TextureSliceInfo[levels * _depthOrLayers];
} }
public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format) public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
@@ -294,7 +307,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage) public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
{ {
var usage = DefaultUsageFlags; var usage = DefaultUsageFlags;
@@ -307,11 +320,19 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit; usage |= ImageUsageFlags.ColorAttachmentBit;
} }
if ((format.IsImageCompatible() && isMsImageStorageSupported) || extendedUsage) bool isMsImageStorageSupported = capabilities.SupportsShaderStorageImageMultisample;
if (format.IsImageCompatible() && (isMsImageStorageSupported || !target.IsMultisample()))
{ {
usage |= ImageUsageFlags.StorageBit; usage |= ImageUsageFlags.StorageBit;
} }
if (capabilities.SupportsAttachmentFeedbackLoop &&
(usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
{
usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
}
return usage; return usage;
} }
@@ -512,6 +533,55 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public void AddBinding(TextureView view)
{
// Assumes a view only has a first level.
int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
int layers = view.Layers;
for (int i = 0; i < layers; i++)
{
ref TextureSliceInfo info = ref _slices[index++];
info.BindCount++;
}
_bindCount++;
}
public void ClearBindings()
{
if (_bindCount != 0)
{
Array.Clear(_slices, 0, _slices.Length);
_bindCount = 0;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsBound(TextureView view)
{
if (_bindCount != 0)
{
int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
int layers = view.Layers;
for (int i = 0; i < layers; i++)
{
ref TextureSliceInfo info = ref _slices[index++];
if (info.BindCount != 0)
{
return true;
}
}
}
return false;
}
public void IncrementViewsCount() public void IncrementViewsCount()
{ {
_viewsCount++; _viewsCount++;
+31 -2
View File
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto<DisposableImageView> _imageView2dArray; private readonly Auto<DisposableImageView> _imageView2dArray;
private Dictionary<Format, TextureView> _selfManagedViews; private Dictionary<Format, TextureView> _selfManagedViews;
private int _hazardUses;
private readonly TextureCreateInfo _info; private readonly TextureCreateInfo _info;
private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses; private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
@@ -62,8 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample();
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
var usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false); var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
var levels = (uint)info.Levels; var levels = (uint)info.Levels;
var layers = (uint)info.GetLayers(); var layers = (uint)info.GetLayers();
@@ -1037,6 +1038,34 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
{
Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
if (feedbackLoopHazards != null && Storage.IsBound(this))
{
feedbackLoopHazards.Add(this);
_hazardUses++;
}
}
public void ClearUsage(List<TextureView> feedbackLoopHazards)
{
if (_hazardUses != 0 && feedbackLoopHazards != null)
{
feedbackLoopHazards.Remove(this);
_hazardUses--;
}
}
public void DecrementHazardUses()
{
if (_hazardUses != 0)
{
_hazardUses--;
}
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd, VulkanRenderer gd,
Device device, Device device,
@@ -73,22 +73,16 @@ namespace Ryujinx.Graphics.Vulkan
_buffer = autoBuffer; _buffer = autoBuffer;
if (!gd.Capabilities.SupportsExtendedDynamicState)
{
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride; state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
} }
}
return; return;
} }
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size); autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
if (!gd.Capabilities.SupportsExtendedDynamicState)
{
// The original stride must be reapplied in case it was rewritten. // The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
}
if (_offset >= size) if (_offset >= size)
{ {
@@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_count != 0) if (_count != 0)
{ {
if (_gd.Capabilities.SupportsExtendedDynamicState && (_gd.SupportsMTL31 || !_gd.IsMoltenVk)) if (_gd.Capabilities.SupportsExtendedDynamicState)
{ {
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( _gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer, cbs.CommandBuffer,
@@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Vulkan
[ [
ExtConditionalRendering.ExtensionName, ExtConditionalRendering.ExtensionName,
ExtExtendedDynamicState.ExtensionName, ExtExtendedDynamicState.ExtensionName,
ExtExtendedDynamicState2.ExtensionName,
ExtTransformFeedback.ExtensionName, ExtTransformFeedback.ExtensionName,
KhrDrawIndirectCount.ExtensionName, KhrDrawIndirectCount.ExtensionName,
KhrPushDescriptor.ExtensionName, KhrPushDescriptor.ExtensionName,
@@ -45,7 +44,9 @@ namespace Ryujinx.Graphics.Vulkan
"VK_KHR_portability_subset", // As per spec, we should enable this if present. "VK_KHR_portability_subset", // As per spec, we should enable this if present.
"VK_EXT_4444_formats", "VK_EXT_4444_formats",
"VK_KHR_8bit_storage", "VK_KHR_8bit_storage",
"VK_KHR_maintenance2" "VK_KHR_maintenance2",
"VK_EXT_attachment_feedback_loop_layout",
"VK_EXT_attachment_feedback_loop_dynamic_state"
]; ];
private static readonly string[] _requiredExtensions = private static readonly string[] _requiredExtensions =
@@ -315,17 +316,6 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesCustomBorderColor; features2.PNext = &supportedFeaturesCustomBorderColor;
} }
PhysicalDeviceExtendedDynamicState2FeaturesEXT supportedFeaturesExtExtendedDynamicState2 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
features2.PNext = &supportedFeaturesExtExtendedDynamicState2;
}
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new() PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new()
{ {
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
@@ -371,6 +361,28 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesDepthClipControl; features2.PNext = &supportedFeaturesDepthClipControl;
} }
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
{
features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
}
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
{
features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
}
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new() PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
{ {
SType = StructureType.PhysicalDeviceVulkan12Features, SType = StructureType.PhysicalDeviceVulkan12Features,
@@ -406,7 +418,6 @@ namespace Ryujinx.Graphics.Vulkan
TessellationShader = supportedFeatures.TessellationShader, TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
RobustBufferAccess = useRobustBufferAccess, RobustBufferAccess = useRobustBufferAccess,
WideLines = supportedFeatures.WideLines,
SampleRateShading = supportedFeatures.SampleRateShading, SampleRateShading = supportedFeatures.SampleRateShading,
}; };
@@ -464,20 +475,6 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresExtendedDynamicState; pExtendedFeatures = &featuresExtendedDynamicState;
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
var featuresExtendedDynamicState2 = new PhysicalDeviceExtendedDynamicState2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = pExtendedFeatures,
ExtendedDynamicState2 = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2,
ExtendedDynamicState2LogicOp = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp,
ExtendedDynamicState2PatchControlPoints = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
};
pExtendedFeatures = &featuresExtendedDynamicState2;
}
var featuresVk11 = new PhysicalDeviceVulkan11Features var featuresVk11 = new PhysicalDeviceVulkan11Features
{ {
SType = StructureType.PhysicalDeviceVulkan11Features, SType = StructureType.PhysicalDeviceVulkan11Features,
@@ -560,6 +557,36 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresDepthClipControl; pExtendedFeatures = &featuresDepthClipControl;
} }
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
{
featuresAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
PNext = pExtendedFeatures,
AttachmentFeedbackLoopLayout = true,
};
pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
}
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
{
featuresDynamicAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
PNext = pExtendedFeatures,
AttachmentFeedbackLoopDynamicState = true,
};
pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
}
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
+34 -31
View File
@@ -38,10 +38,10 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrSwapchain SwapchainApi { get; private set; } internal KhrSwapchain SwapchainApi { get; private set; }
internal ExtConditionalRendering ConditionalRenderingApi { get; private set; } internal ExtConditionalRendering ConditionalRenderingApi { get; private set; }
internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; } internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; }
internal ExtExtendedDynamicState2 ExtendedDynamicState2Api { get; private set; }
internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
internal uint QueueFamilyIndex { get; private set; } internal uint QueueFamilyIndex { get; private set; }
internal Queue Queue { get; private set; } internal Queue Queue { get; private set; }
@@ -93,7 +93,6 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsIntelArc { get; private set; } internal bool IsIntelArc { get; private set; }
internal bool IsQualcommProprietary { get; private set; } internal bool IsQualcommProprietary { get; private set; }
internal bool IsMoltenVk { get; private set; } internal bool IsMoltenVk { get; private set; }
internal bool SupportsMTL31 { get; private set; }
internal bool IsTBDR { get; private set; } internal bool IsTBDR { get; private set; }
internal bool IsSharedMemory { get; private set; } internal bool IsSharedMemory { get; private set; }
@@ -122,8 +121,6 @@ namespace Ryujinx.Graphics.Vulkan
// Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
IsMoltenVk = true; IsMoltenVk = true;
SupportsMTL31 = OperatingSystem.IsMacOSVersionAtLeast(14);
} }
} }
@@ -141,11 +138,6 @@ namespace Ryujinx.Graphics.Vulkan
ExtendedDynamicStateApi = extendedDynamicStateApi; ExtendedDynamicStateApi = extendedDynamicStateApi;
} }
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState2 extendedDynamicState2Api))
{
ExtendedDynamicState2Api = extendedDynamicState2Api;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
{ {
PushDescriptorApi = pushDescriptorApi; PushDescriptorApi = pushDescriptorApi;
@@ -161,6 +153,11 @@ namespace Ryujinx.Graphics.Vulkan
DrawIndirectCountApi = drawIndirectCountApi; DrawIndirectCountApi = drawIndirectCountApi;
} }
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
{
DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
}
if (maxQueueCount >= 2) if (maxQueueCount >= 2)
{ {
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue); Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
@@ -235,11 +232,6 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
}; };
PhysicalDeviceExtendedDynamicState2FeaturesEXT featuresExtendedDynamicState2 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
};
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new() PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new()
{ {
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
@@ -260,6 +252,16 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
}; };
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
};
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
};
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new() PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
{ {
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr, SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
@@ -270,12 +272,6 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresPrimitiveTopologyListRestart; features2.PNext = &featuresPrimitiveTopologyListRestart;
} }
if (_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
featuresExtendedDynamicState2.PNext = features2.PNext;
features2.PNext = &featuresExtendedDynamicState2;
}
if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
featuresRobustness2.PNext = features2.PNext; featuresRobustness2.PNext = features2.PNext;
@@ -302,6 +298,22 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresDepthClipControl; features2.PNext = &featuresDepthClipControl;
} }
bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
if (supportsAttachmentFeedbackLoop)
{
featuresAttachmentFeedbackLoop.PNext = features2.PNext;
features2.PNext = &featuresAttachmentFeedbackLoop;
}
bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
if (supportsDynamicAttachmentFeedbackLoop)
{
featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
}
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
if (usePortability) if (usePortability)
@@ -393,10 +405,6 @@ namespace Ryujinx.Graphics.Vulkan
properties.Limits.FramebufferDepthSampleCounts & properties.Limits.FramebufferDepthSampleCounts &
properties.Limits.FramebufferStencilSampleCounts; properties.Limits.FramebufferStencilSampleCounts;
// Temporarily disable this, can be added back at a later date, make it easy to re-enable.
// Disabled because currently causing Device Lost error on NVIDIA.
featuresExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints = false;
Capabilities = new HardwareCapabilities( Capabilities = new HardwareCapabilities(
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
supportsCustomBorderColor, supportsCustomBorderColor,
@@ -413,8 +421,6 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.ShaderStorageImageMultisample, features2.Features.ShaderStorageImageMultisample,
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
featuresExtendedDynamicState2,
_physicalDevice.PhysicalDeviceProperties.Limits.MaxTessellationPatchSize,
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor || IsMoltenVk,
supportsPushDescriptors && !IsMoltenVk, supportsPushDescriptors && !IsMoltenVk,
@@ -430,7 +436,8 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
_physicalDevice.PhysicalDeviceFeatures.WideLines, supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
propertiesSubgroup.SubgroupSize, propertiesSubgroup.SubgroupSize,
supportedSampleCounts, supportedSampleCounts,
portabilityFlags, portabilityFlags,
@@ -764,10 +771,6 @@ namespace Ryujinx.Graphics.Vulkan
supportsViewportSwizzle: false, supportsViewportSwizzle: false,
supportsIndirectParameters: true, supportsIndirectParameters: true,
supportsDepthClipControl: Capabilities.SupportsDepthClipControl, supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
supportsExtendedDynamicState: Capabilities.SupportsExtendedDynamicState,
supportsExtendedDynamicState2: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2,
supportsLogicOpDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp,
supportsPatchControlPointsDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
uniformBufferSetIndex: PipelineBase.UniformSetIndex, uniformBufferSetIndex: PipelineBase.UniformSetIndex,
storageBufferSetIndex: PipelineBase.StorageSetIndex, storageBufferSetIndex: PipelineBase.StorageSetIndex,
textureSetIndex: PipelineBase.TextureSetIndex, textureSetIndex: PipelineBase.TextureSetIndex,
@@ -39,7 +39,7 @@ namespace Ryujinx.HLE.Generators
var type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type; var type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type;
var model = context.Compilation.GetSemanticModel(type.SyntaxTree); var model = context.Compilation.GetSemanticModel(type.SyntaxTree);
var typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol; var typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol;
var fullName = typeSymbol.ToString(); var fullName = typeSymbol?.ToString();
generator.EnterScope("if (parameter != null)"); generator.EnterScope("if (parameter != null)");
generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);"); generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);");
generator.LeaveScope(); generator.LeaveScope();
@@ -67,7 +67,7 @@ namespace Ryujinx.HLE.Generators
{ {
var typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode); var typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode);
return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); return typeSymbol?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
} }
public void Initialize(GeneratorInitializationContext context) public void Initialize(GeneratorInitializationContext context)
@@ -1054,8 +1054,6 @@ namespace Ryujinx.HLE.FileSystem
verified = verifyKeys(lines, titlePattern); verified = verifyKeys(lines, titlePattern);
break; break;
case "console.keys": case "console.keys":
verified = verifyKeys(lines, genericPattern);
break;
case "dev.keys": case "dev.keys":
verified = verifyKeys(lines, genericPattern); verified = verifyKeys(lines, genericPattern);
break; break;
@@ -2241,8 +2241,6 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
return new CastExpression("reinterpret_cast", to, from); return new CastExpression("reinterpret_cast", to, from);
case 'm': case 'm':
_position += 2;
return ParseBinaryExpression("%");
case 'M': case 'M':
_position += 2; _position += 2;
return ParseBinaryExpression("%"); return ParseBinaryExpression("%");
+2 -2
View File
@@ -175,7 +175,7 @@ namespace Ryujinx.HLE.HOS
if (StrEquals(RomfsDir, modDir.Name)) if (StrEquals(RomfsDir, modDir.Name))
{ {
var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path)); var modData = modMetadata.Mods.FirstOrDefault(x => modDir.Parent.FullName.Equals(x.Path));
var enabled = modData?.Enabled ?? true; var enabled = modData?.Enabled ?? true;
mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled)); mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled));
@@ -183,7 +183,7 @@ namespace Ryujinx.HLE.HOS
} }
else if (StrEquals(ExefsDir, modDir.Name)) else if (StrEquals(ExefsDir, modDir.Name))
{ {
var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path)); var modData = modMetadata.Mods.FirstOrDefault(x => modDir.Parent.FullName.Equals(x.Path));
var enabled = modData?.Enabled ?? true; var enabled = modData?.Enabled ?? true;
mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled)); mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled));
@@ -4,7 +4,6 @@ using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Account.Acc.Types; using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO; using System.IO;
namespace Ryujinx.HLE.HOS.Services.Account.Acc namespace Ryujinx.HLE.HOS.Services.Account.Acc
@@ -269,6 +269,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(29)] // 11.0.0+
// GetCacheStorageMax() -> (s32 cache_storage_index_max, s64 cache_storage_data_and_journal_size_max)
public ResultCode GetCacheStorageMax(ServiceCtx context)
{
ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
context.ResponseData.Write((Int32)nacp.CacheStorageIndexMax);
context.ResponseData.Write(0);
context.ResponseData.Write(nacp.CacheStorageDataAndJournalSizeMax);
return ResultCode.Success;
}
[CommandCmif(30)] [CommandCmif(30)]
// BeginBlockingHomeButtonShortAndLongPressed() // BeginBlockingHomeButtonShortAndLongPressed()
public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context) public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context)
@@ -7,7 +7,6 @@ using Ryujinx.HLE.HOS.Services.Mii.Types;
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -124,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{ {
targetPrevAddress = InvalidAddress; targetPrevAddress = InvalidAddress;
} }
node = node.Previous; node = node?.Previous;
_tree.Remove(prevAddress); _tree.Remove(prevAddress);
_list.Remove(_dictionary[prevAddress]); _list.Remove(_dictionary[prevAddress]);
_dictionary.Remove(prevAddress); _dictionary.Remove(prevAddress);
@@ -1,6 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -83,7 +83,6 @@ namespace Ryujinx.HLE.HOS.Services.Spl
} }
break; break;
case ConfigItem.SecurityEngineInterruptNumber: case ConfigItem.SecurityEngineInterruptNumber:
return SmcResult.NotImplemented;
case ConfigItem.FuseVersion: case ConfigItem.FuseVersion:
return SmcResult.NotImplemented; return SmcResult.NotImplemented;
case ConfigItem.HardwareType: case ConfigItem.HardwareType:
@@ -107,15 +106,10 @@ namespace Ryujinx.HLE.HOS.Services.Spl
configValue = 0; configValue = 0;
break; break;
case ConfigItem.KernelConfiguration: case ConfigItem.KernelConfiguration:
return SmcResult.NotImplemented;
case ConfigItem.IsChargerHiZModeEnabled: case ConfigItem.IsChargerHiZModeEnabled:
return SmcResult.NotImplemented;
case ConfigItem.QuestState: case ConfigItem.QuestState:
return SmcResult.NotImplemented;
case ConfigItem.RegulatorType: case ConfigItem.RegulatorType:
return SmcResult.NotImplemented;
case ConfigItem.DeviceUniqueKeyGeneration: case ConfigItem.DeviceUniqueKeyGeneration:
return SmcResult.NotImplemented;
case ConfigItem.Package2Hash: case ConfigItem.Package2Hash:
return SmcResult.NotImplemented; return SmcResult.NotImplemented;
default: default:
+1 -1
View File
@@ -23,7 +23,7 @@ namespace Ryujinx.HLE.Loaders.Mods
/// Adds a patch in the form of an RLE (Fill mode). /// Adds a patch in the form of an RLE (Fill mode).
/// </summary> /// </summary>
/// <param name="offset">Memory offset</param> /// <param name="offset">Memory offset</param>
/// <param name="length"The fill length</param> /// <param name="length">The fill length</param>
/// <param name="filler">The byte to fill</param> /// <param name="filler">The byte to fill</param>
public void AddFill(uint offset, int length, byte filler) public void AddFill(uint offset, int length, byte filler)
{ {
+1
View File
@@ -44,6 +44,7 @@ namespace Ryujinx.HLE.UI
/// <param name="value">The value associated to the <paramref name="kind"/>.</param> /// <param name="value">The value associated to the <paramref name="kind"/>.</param>
void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value); void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value);
/// <summary>
/// Displays a Message Dialog box specific to Error Applet and blocks until it is closed. /// Displays a Message Dialog box specific to Error Applet and blocks until it is closed.
/// </summary> /// </summary>
/// <returns>False when OK is pressed, True when another button (Details) is pressed.</returns> /// <returns>False when OK is pressed, True when another button (Details) is pressed.</returns>
+3
View File
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.Logging.Targets;
using Ryujinx.Common.SystemInterop; using Ryujinx.Common.SystemInterop;
@@ -462,6 +463,8 @@ namespace Ryujinx.Headless.SDL2
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
while (true) while (true)
{ {
LoadApplication(option); LoadApplication(option);
+1 -1
View File
@@ -418,7 +418,7 @@ namespace Ryujinx.Headless.SDL2
// Get screen touch position // Get screen touch position
if (!_enableMouse) if (!_enableMouse)
{ {
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL2MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat()); hasTouch = TouchScreenManager.Update(true, ((SDL2MouseDriver)_inputManager.MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
} }
if (!hasTouch) if (!hasTouch)
@@ -210,7 +210,7 @@ namespace Ryujinx.Horizon.Kernel.Generators
{ {
string name = methodParameter.Identifier.Text; string name = methodParameter.Identifier.Text;
string argName = GetPrefixedArgName(name); string argName = GetPrefixedArgName(name);
string typeName = methodParameter.Type.ToString(); string typeName = methodParameter.Type?.ToString();
string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type); string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type);
if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword)) if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword))
@@ -329,7 +329,7 @@ namespace Ryujinx.Horizon.Kernel.Generators
{ {
string name = methodParameter.Identifier.Text; string name = methodParameter.Identifier.Text;
string argName = GetPrefixedArgName(name); string argName = GetPrefixedArgName(name);
string typeName = methodParameter.Type.ToString(); string typeName = methodParameter.Type?.ToString();
string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type); string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type);
if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword)) if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword))
@@ -415,9 +415,9 @@ namespace Ryujinx.Horizon.Kernel.Generators
private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode) private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode)
{ {
TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode);
if (typeInfo.Type.ContainingNamespace == null) if (typeInfo.Type?.ContainingNamespace == null)
{ {
return typeInfo.Type.Name; return typeInfo.Type?.Name;
} }
return $"{typeInfo.Type.ContainingNamespace.ToDisplayString()}.{typeInfo.Type.Name}"; return $"{typeInfo.Type.ContainingNamespace.ToDisplayString()}.{typeInfo.Type.Name}";
@@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf; using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
namespace Ryujinx.Horizon.Sdk.Codec.Detail namespace Ryujinx.Horizon.Sdk.Codec.Detail
{ {
@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace Ryujinx.Horizon.Sdk.OsTypes namespace Ryujinx.Horizon.Sdk.OsTypes
@@ -1,7 +1,6 @@
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Memory namespace Ryujinx.Memory
-1
View File
@@ -1,5 +1,4 @@
using NUnit.Framework; using NUnit.Framework;
using System;
namespace Ryujinx.Tests.Cpu namespace Ryujinx.Tests.Cpu
{ {
@@ -795,10 +795,9 @@ namespace Ryujinx.UI.App.Common
{ {
ldnWebHost = DefaultLanPlayWebHost; ldnWebHost = DefaultLanPlayWebHost;
} }
IEnumerable<LdnGameData> ldnGameDataArray = Array.Empty<LdnGameData>();
using HttpClient httpClient = new HttpClient(); using HttpClient httpClient = new HttpClient();
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); IEnumerable<LdnGameData> ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
var evt = new LdnGameDataReceivedEventArgs var evt = new LdnGameDataReceivedEventArgs
{ {
LdnData = ldnGameDataArray LdnData = ldnGameDataArray
@@ -13,7 +13,6 @@ using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text.Json.Nodes;
namespace Ryujinx.UI.Common.Configuration namespace Ryujinx.UI.Common.Configuration
{ {
@@ -89,9 +88,9 @@ namespace Ryujinx.UI.Common.Configuration
} }
} }
// <summary>
/// Determines main window start-up position, size and state
/// <summary> /// <summary>
/// Determines main window start-up position, size and state
/// </summary>
public class WindowStartupSettings public class WindowStartupSettings
{ {
public ReactiveObject<int> WindowSizeWidth { get; private set; } public ReactiveObject<int> WindowSizeWidth { get; private set; }
@@ -878,7 +877,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableHardwareAcceleration.Value = true; EnableHardwareAcceleration.Value = true;
HideCursor.Value = HideCursorMode.OnIdle; HideCursor.Value = HideCursorMode.OnIdle;
Graphics.VSyncMode.Value = VSyncMode.Switch; Graphics.VSyncMode.Value = VSyncMode.Switch;
Graphics.CustomVSyncInterval.Value = 60; Graphics.CustomVSyncInterval.Value = 120;
Graphics.EnableCustomVSyncInterval.Value = false; Graphics.EnableCustomVSyncInterval.Value = false;
Graphics.EnableShaderCache.Value = true; Graphics.EnableShaderCache.Value = true;
Graphics.EnableTextureRecompression.Value = false; Graphics.EnableTextureRecompression.Value = false;
@@ -1617,7 +1616,7 @@ namespace Ryujinx.UI.Common.Configuration
CustomVSyncIntervalDecrement = Key.Unbound, CustomVSyncIntervalDecrement = Key.Unbound,
}; };
configurationFileFormat.CustomVSyncInterval = 60; configurationFileFormat.CustomVSyncInterval = 120;
configurationFileUpdated = true; configurationFileUpdated = true;
} }
@@ -99,16 +99,13 @@ namespace Ryujinx.UI.Common.Helper
Dictionary<ulong, ContentMetaData> updates = Dictionary<ulong, ContentMetaData> updates =
pfs.GetContentData(ContentMetaType.Patch, vfs, checkLevel); pfs.GetContentData(ContentMetaType.Patch, vfs, checkLevel);
Nca patchNca = null;
Nca controlNca = null;
if (!updates.TryGetValue(applicationIdBase, out ContentMetaData content)) if (!updates.TryGetValue(applicationIdBase, out ContentMetaData content))
{ {
continue; continue;
} }
patchNca = content.GetNcaByType(vfs.KeySet, ContentType.Program); Nca patchNca = content.GetNcaByType(vfs.KeySet, ContentType.Program);
controlNca = content.GetNcaByType(vfs.KeySet, ContentType.Control); Nca controlNca = content.GetNcaByType(vfs.KeySet, ContentType.Control);
if (controlNca == null || patchNca == null) if (controlNca == null || patchNca == null)
{ {
+1 -1
View File
@@ -144,7 +144,7 @@ namespace Ryujinx.Ava
public static ThemeVariant DetectSystemTheme() public static ThemeVariant DetectSystemTheme()
{ {
if (Application.Current is App app) if (Current is App app)
{ {
var colorValues = app.PlatformSettings.GetColorValues(); var colorValues = app.PlatformSettings.GetColorValues();
+1 -5
View File
@@ -414,11 +414,7 @@ namespace Ryujinx.Ava
string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
string directory = AppDataManager.Mode switch string directory = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
{
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"),
};
string path = Path.Combine(directory, filename); string path = Path.Combine(directory, filename);
+2 -1
View File
@@ -548,8 +548,9 @@
"AboutDiscordUrlTooltipMessage": "انقر لفتح دعوة إلى خادم ريوجينكس في ديكسورد في متصفحك الافتراضي.", "AboutDiscordUrlTooltipMessage": "انقر لفتح دعوة إلى خادم ريوجينكس في ديكسورد في متصفحك الافتراضي.",
"AboutTwitterUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في تويتر في متصفحك الافتراضي.", "AboutTwitterUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في تويتر في متصفحك الافتراضي.",
"AboutRyujinxAboutTitle": "حول:", "AboutRyujinxAboutTitle": "حول:",
"AboutRyujinxAboutContent": "ريوجينكس هو محاكي لجهاز نينتندو سويتش™.\nمن فضلك ادعمنا على باتريون.\nاحصل على آخر الأخبار على تويتر أو ديسكورد.\nيمكن للمطورين المهتمين بالمساهمة معرفة المزيد على غيت هاب أو ديسكورد.", "AboutRyujinxAboutContent": "ريوجينكس هو محاكي لجهاز نينتندو سويتش™.\n\nيمكن للمطورين المهتمين بالمساهمة معرفة المزيد على غيت هاب.",
"AboutRyujinxMaintainersTitle": "تتم صيانته بواسطة:", "AboutRyujinxMaintainersTitle": "تتم صيانته بواسطة:",
"AboutRyujinxFormerMaintainersTitle": "تم صيانتها سابقًا بواسطة:",
"AboutRyujinxMaintainersContentTooltipMessage": "انقر لفتح صفحة المساهمين في متصفحك الافتراضي.", "AboutRyujinxMaintainersContentTooltipMessage": "انقر لفتح صفحة المساهمين في متصفحك الافتراضي.",
"AboutRyujinxSupprtersTitle": "مدعوم على باتريون بواسطة:", "AboutRyujinxSupprtersTitle": "مدعوم على باتريون بواسطة:",
"AmiiboSeriesLabel": "مجموعة أميبو", "AmiiboSeriesLabel": "مجموعة أميبو",
+2 -1
View File
@@ -549,8 +549,9 @@
"AboutDiscordUrlTooltipMessage": "Klicke hier, um eine Einladung zum Ryujinx Discord Server im Standardbrowser zu öffnen.", "AboutDiscordUrlTooltipMessage": "Klicke hier, um eine Einladung zum Ryujinx Discord Server im Standardbrowser zu öffnen.",
"AboutTwitterUrlTooltipMessage": "Klicke hier, um die Ryujinx Twitter Seite im Standardbrowser zu öffnen.", "AboutTwitterUrlTooltipMessage": "Klicke hier, um die Ryujinx Twitter Seite im Standardbrowser zu öffnen.",
"AboutRyujinxAboutTitle": "Über:", "AboutRyujinxAboutTitle": "Über:",
"AboutRyujinxAboutContent": "Ryujinx ist ein Nintendo Switch™ Emulator.\nBitte unterstütze uns auf Patreon.\nAuf Twitter oder Discord erfährst du alle Neuigkeiten.\nEntwickler, die an einer Mitarbeit interessiert sind, können auf GitHub oder Discord mehr erfahren.", "AboutRyujinxAboutContent": "Ryujinx ist ein Nintendo Switch™ Emulator.\n\nEntwickler, die an einer Mitarbeit interessiert sind, können auf GitHub mehr erfahren.",
"AboutRyujinxMaintainersTitle": "Entwickelt von:", "AboutRyujinxMaintainersTitle": "Entwickelt von:",
"AboutRyujinxFormerMaintainersTitle": "Früher Entwickelt von:",
"AboutRyujinxMaintainersContentTooltipMessage": "Klicke hier, um die Liste der Mitwirkenden im Standardbrowser zu öffnen.", "AboutRyujinxMaintainersContentTooltipMessage": "Klicke hier, um die Liste der Mitwirkenden im Standardbrowser zu öffnen.",
"AboutRyujinxSupprtersTitle": "Unterstützt auf Patreon von:", "AboutRyujinxSupprtersTitle": "Unterstützt auf Patreon von:",
"AmiiboSeriesLabel": "Amiibo-Serie", "AmiiboSeriesLabel": "Amiibo-Serie",
+2 -1
View File
@@ -548,8 +548,9 @@
"AboutDiscordUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε μία πρόσκληση στον διακομιστή Ryujinx Discord στο προεπιλεγμένο πρόγραμμα περιήγησης.", "AboutDiscordUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε μία πρόσκληση στον διακομιστή Ryujinx Discord στο προεπιλεγμένο πρόγραμμα περιήγησης.",
"AboutTwitterUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx Twitter στο προεπιλεγμένο πρόγραμμα περιήγησης.", "AboutTwitterUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx Twitter στο προεπιλεγμένο πρόγραμμα περιήγησης.",
"AboutRyujinxAboutTitle": "Σχετικά με:", "AboutRyujinxAboutTitle": "Σχετικά με:",
"AboutRyujinxAboutContent": "Το Ryujinx είναι ένας εξομοιωτής για το Nintendo Switch™.\nΥποστηρίξτε μας στο Patreon.\nΛάβετε όλα τα τελευταία νέα στο Twitter ή στο Discord.\nΟι προγραμματιστές που ενδιαφέρονται να συνεισφέρουν μπορούν να μάθουν περισσότερα στο GitHub ή στο Discord μας.", "AboutRyujinxAboutContent": "Το Ryujinx είναι ένας εξομοιωτής για το Nintendo Switch™.\n\nΟι προγραμματιστές που ενδιαφέρονται να συνεισφέρουν μπορούν να μάθουν περισσότερα στον GitHub μας.",
"AboutRyujinxMaintainersTitle": "Συντηρείται από:", "AboutRyujinxMaintainersTitle": "Συντηρείται από:",
"AboutRyujinxFormerMaintainersTitle": "Προηγουμένως διατηρείται από:",
"AboutRyujinxMaintainersContentTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Συνεισφέροντες στο προεπιλεγμένο πρόγραμμα περιήγησης.", "AboutRyujinxMaintainersContentTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Συνεισφέροντες στο προεπιλεγμένο πρόγραμμα περιήγησης.",
"AboutRyujinxSupprtersTitle": "Υποστηρίζεται στο Patreon από:", "AboutRyujinxSupprtersTitle": "Υποστηρίζεται στο Patreon από:",
"AmiiboSeriesLabel": "Σειρά Amiibo", "AmiiboSeriesLabel": "Σειρά Amiibo",
+4 -1
View File
@@ -15,6 +15,7 @@
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder", "MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder", "MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
"MenuBarFileOpenEmuFolder": "Open Ryujinx Folder", "MenuBarFileOpenEmuFolder": "Open Ryujinx Folder",
"MenuBarFileOpenScreenshotsFolder": "Open Screenshots Folder",
"MenuBarFileOpenLogsFolder": "Open Logs Folder", "MenuBarFileOpenLogsFolder": "Open Logs Folder",
"MenuBarFileExit": "_Exit", "MenuBarFileExit": "_Exit",
"MenuBarOptions": "_Options", "MenuBarOptions": "_Options",
@@ -574,8 +575,9 @@
"AboutDiscordUrlTooltipMessage": "Click to open an invite to the Ryujinx Discord server in your default browser.", "AboutDiscordUrlTooltipMessage": "Click to open an invite to the Ryujinx Discord server in your default browser.",
"AboutTwitterUrlTooltipMessage": "Click to open the Ryujinx Twitter page in your default browser.", "AboutTwitterUrlTooltipMessage": "Click to open the Ryujinx Twitter page in your default browser.",
"AboutRyujinxAboutTitle": "About:", "AboutRyujinxAboutTitle": "About:",
"AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nPlease support us on Patreon.\nGet all the latest news on our Twitter or Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.", "AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\n\nDevelopers interested in contributing can find out more on our GitHub.",
"AboutRyujinxMaintainersTitle": "Maintained By:", "AboutRyujinxMaintainersTitle": "Maintained By:",
"AboutRyujinxFormerMaintainersTitle": "Formerly Maintained By:",
"AboutRyujinxMaintainersContentTooltipMessage": "Click to open the Contributors page in your default browser.", "AboutRyujinxMaintainersContentTooltipMessage": "Click to open the Contributors page in your default browser.",
"AboutRyujinxSupprtersTitle": "Supported on Patreon By:", "AboutRyujinxSupprtersTitle": "Supported on Patreon By:",
"AmiiboSeriesLabel": "Amiibo Series", "AmiiboSeriesLabel": "Amiibo Series",
@@ -655,6 +657,7 @@
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from", "LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from", "LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
"OpenRyujinxFolderTooltip": "Open Ryujinx filesystem folder", "OpenRyujinxFolderTooltip": "Open Ryujinx filesystem folder",
"OpenScreenshotFolderTooltip": "Open Ryujinx screenshots folder",
"OpenRyujinxLogsTooltip": "Opens the folder where logs are written to", "OpenRyujinxLogsTooltip": "Opens the folder where logs are written to",
"ExitTooltip": "Exit Ryujinx", "ExitTooltip": "Exit Ryujinx",
"OpenSettingsTooltip": "Open settings window", "OpenSettingsTooltip": "Open settings window",
+2 -1
View File
@@ -548,8 +548,9 @@
"AboutDiscordUrlTooltipMessage": "Haz clic para recibir una invitación al Discord de Ryujinx en tu navegador predeterminado.", "AboutDiscordUrlTooltipMessage": "Haz clic para recibir una invitación al Discord de Ryujinx en tu navegador predeterminado.",
"AboutTwitterUrlTooltipMessage": "Haz clic para abrir el Twitter de Ryujinx en tu navegador predeterminado.", "AboutTwitterUrlTooltipMessage": "Haz clic para abrir el Twitter de Ryujinx en tu navegador predeterminado.",
"AboutRyujinxAboutTitle": "Acerca de:", "AboutRyujinxAboutTitle": "Acerca de:",
"AboutRyujinxAboutContent": "Ryujinx es un emulador para Nintendo Switch™.\nPor favor, apóyanos en Patreon.\nEncuentra las noticias más recientes en nuestro Twitter o Discord.\nDesarrolladores interesados en contribuir pueden encontrar más información en GitHub o Discord.", "AboutRyujinxAboutContent": "Ryujinx es un emulador para Nintendo Switch™.\n\nDesarrolladores interesados en contribuir pueden encontrar más información en GitHub.",
"AboutRyujinxMaintainersTitle": "Mantenido por:", "AboutRyujinxMaintainersTitle": "Mantenido por:",
"AboutRyujinxFormerMaintainersTitle": "Anteriormente Mantenido por:",
"AboutRyujinxMaintainersContentTooltipMessage": "Haz clic para abrir la página de contribuidores en tu navegador predeterminado.", "AboutRyujinxMaintainersContentTooltipMessage": "Haz clic para abrir la página de contribuidores en tu navegador predeterminado.",
"AboutRyujinxSupprtersTitle": "Apoyado en Patreon Por:", "AboutRyujinxSupprtersTitle": "Apoyado en Patreon Por:",
"AmiiboSeriesLabel": "Serie de Amiibo", "AmiiboSeriesLabel": "Serie de Amiibo",
+2 -1
View File
@@ -550,8 +550,9 @@
"AboutDiscordUrlTooltipMessage": "Cliquez pour ouvrir une invitation au serveur Discord de Ryujinx dans votre navigateur par défaut.", "AboutDiscordUrlTooltipMessage": "Cliquez pour ouvrir une invitation au serveur Discord de Ryujinx dans votre navigateur par défaut.",
"AboutTwitterUrlTooltipMessage": "Cliquez pour ouvrir la page Twitter de Ryujinx dans votre navigateur par défaut.", "AboutTwitterUrlTooltipMessage": "Cliquez pour ouvrir la page Twitter de Ryujinx dans votre navigateur par défaut.",
"AboutRyujinxAboutTitle": "À propos :", "AboutRyujinxAboutTitle": "À propos :",
"AboutRyujinxAboutContent": "Ryujinx est un émulateur pour la Nintendo Switch™.\nMerci de nous soutenir sur Patreon.\nObtenez toutes les dernières actualités sur notre Twitter ou notre Discord.\nLes développeurs intéressés à contribuer peuvent en savoir plus sur notre GitHub ou notre Discord.", "AboutRyujinxAboutContent": "Ryujinx est un émulateur pour la Nintendo Switch™.\n\nLes développeurs intéressés à contribuer peuvent en savoir plus sur notre GitHub.",
"AboutRyujinxMaintainersTitle": "Maintenu par :", "AboutRyujinxMaintainersTitle": "Maintenu par :",
"AboutRyujinxFormerMaintainersTitle": "Auparavant Maintenu par :",
"AboutRyujinxMaintainersContentTooltipMessage": "Cliquez pour ouvrir la page Contributeurs dans votre navigateur par défaut.", "AboutRyujinxMaintainersContentTooltipMessage": "Cliquez pour ouvrir la page Contributeurs dans votre navigateur par défaut.",
"AboutRyujinxSupprtersTitle": "Supporté sur Patreon par :", "AboutRyujinxSupprtersTitle": "Supporté sur Patreon par :",
"AmiiboSeriesLabel": "Séries Amiibo", "AmiiboSeriesLabel": "Séries Amiibo",
+2 -1
View File
@@ -548,8 +548,9 @@
"AboutDiscordUrlTooltipMessage": "לחץ כדי לפתוח הזמנה לשרת הדיסקורד של ריוג'ינקס בדפדפן ברירת המחדל שלך.", "AboutDiscordUrlTooltipMessage": "לחץ כדי לפתוח הזמנה לשרת הדיסקורד של ריוג'ינקס בדפדפן ברירת המחדל שלך.",
"AboutTwitterUrlTooltipMessage": "לחץ כדי לפתוח את דף הטוויטר של Ryujinx בדפדפן ברירת המחדל שלך.", "AboutTwitterUrlTooltipMessage": "לחץ כדי לפתוח את דף הטוויטר של Ryujinx בדפדפן ברירת המחדל שלך.",
"AboutRyujinxAboutTitle": "אודות:", "AboutRyujinxAboutTitle": "אודות:",
"AboutRyujinxAboutContent": "ריוג'ינקס הוא אמולטור עבור הנינטנדו סוויץ' (כל הזכויות שמורות).\nבבקשה תתמכו בנו בפטראון.\nקבל את כל החדשות האחרונות בטוויטר או בדיסקורד שלנו.\nמפתחים המעוניינים לתרום יכולים לקבל מידע נוסף ב-גיטהאב או ב-דיסקורד שלנו.", "AboutRyujinxAboutContent": "ריוג'ינקס הוא אמולטור עבור הנינטנדו סוויץ' (כל הזכויות שמורות).\n\nמפתחים המעוניינים לתרום יכולים לקבל מידע נוסף ב-גיטהאב או ב-דיסקורד שלנו.",
"AboutRyujinxMaintainersTitle": "מתוחזק על ידי:", "AboutRyujinxMaintainersTitle": "מתוחזק על ידי:",
"AboutRyujinxFormerMaintainersTitle": "תוחזק בעבר על ידי:",
"AboutRyujinxMaintainersContentTooltipMessage": "לחץ כדי לפתוח את דף התורמים בדפדפן ברירת המחדל שלך.", "AboutRyujinxMaintainersContentTooltipMessage": "לחץ כדי לפתוח את דף התורמים בדפדפן ברירת המחדל שלך.",
"AboutRyujinxSupprtersTitle": "תמוך באמצעות Patreon", "AboutRyujinxSupprtersTitle": "תמוך באמצעות Patreon",
"AmiiboSeriesLabel": "סדרת אמיבו", "AmiiboSeriesLabel": "סדרת אמיבו",
+2 -1
View File
@@ -548,8 +548,9 @@
"AboutDiscordUrlTooltipMessage": "Clicca per aprire un invito al server Discord di Ryujinx nel tuo browser predefinito.", "AboutDiscordUrlTooltipMessage": "Clicca per aprire un invito al server Discord di Ryujinx nel tuo browser predefinito.",
"AboutTwitterUrlTooltipMessage": "Clicca per aprire la pagina Twitter di Ryujinx nel tuo browser predefinito.", "AboutTwitterUrlTooltipMessage": "Clicca per aprire la pagina Twitter di Ryujinx nel tuo browser predefinito.",
"AboutRyujinxAboutTitle": "Informazioni:", "AboutRyujinxAboutTitle": "Informazioni:",
"AboutRyujinxAboutContent": "Ryujinx è un emulatore per la console Nintendo Switch™.\nSostienici su Patreon.\nRicevi tutte le ultime notizie sul nostro Twitter o su Discord.\nGli sviluppatori interessati a contribuire possono trovare più informazioni sul nostro GitHub o Discord.", "AboutRyujinxAboutContent": "Ryujinx è un emulatore per la console Nintendo Switch™.\n\nGli sviluppatori interessati a contribuire possono trovare più informazioni sul nostro GitHub.",
"AboutRyujinxMaintainersTitle": "Mantenuto da:", "AboutRyujinxMaintainersTitle": "Mantenuto da:",
"AboutRyujinxFormerMaintainersTitle": "Precedentemente Mantenuto da:",
"AboutRyujinxMaintainersContentTooltipMessage": "Clicca per aprire la pagina dei contributori nel tuo browser predefinito.", "AboutRyujinxMaintainersContentTooltipMessage": "Clicca per aprire la pagina dei contributori nel tuo browser predefinito.",
"AboutRyujinxSupprtersTitle": "Supportato su Patreon da:", "AboutRyujinxSupprtersTitle": "Supportato su Patreon da:",
"AmiiboSeriesLabel": "Serie Amiibo", "AmiiboSeriesLabel": "Serie Amiibo",
+2 -1
View File
@@ -548,8 +548,9 @@
"AboutDiscordUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Discord サーバを開きます.", "AboutDiscordUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Discord サーバを開きます.",
"AboutTwitterUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Twitter ページを開きます.", "AboutTwitterUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Twitter ページを開きます.",
"AboutRyujinxAboutTitle": "Ryujinx について:", "AboutRyujinxAboutTitle": "Ryujinx について:",
"AboutRyujinxAboutContent": "Ryujinx は Nintendo Switch™ のエミュレータです.\nPatreon で私達の活動を支援してください.\n最新の情報は Twitter または Discord から取得できます.\n貢献したい開発者の方は GitHub または Discord で詳細をご確認ください.", "AboutRyujinxAboutContent": "Ryujinx は Nintendo Switch™ のエミュレータです.\n\n貢献したい開発者の方は GitHub で詳細をご確認ください.",
"AboutRyujinxMaintainersTitle": "開発者:", "AboutRyujinxMaintainersTitle": "開発者:",
"AboutRyujinxFormerMaintainersTitle": "以前の開発者:",
"AboutRyujinxMaintainersContentTooltipMessage": "クリックするとデフォルトのブラウザで 貢献者のページを開きます.", "AboutRyujinxMaintainersContentTooltipMessage": "クリックするとデフォルトのブラウザで 貢献者のページを開きます.",
"AboutRyujinxSupprtersTitle": "Patreon での支援者:", "AboutRyujinxSupprtersTitle": "Patreon での支援者:",
"AmiiboSeriesLabel": "Amiibo シリーズ", "AmiiboSeriesLabel": "Amiibo シリーズ",
+4 -1
View File
@@ -14,6 +14,7 @@
"MenuBarFileLoadDlcFromFolder": "폴더에서 DLC 불러오기", "MenuBarFileLoadDlcFromFolder": "폴더에서 DLC 불러오기",
"MenuBarFileLoadTitleUpdatesFromFolder": "폴더에서 타이틀 업데이트 불러오기", "MenuBarFileLoadTitleUpdatesFromFolder": "폴더에서 타이틀 업데이트 불러오기",
"MenuBarFileOpenEmuFolder": "Ryujinx 폴더 열기", "MenuBarFileOpenEmuFolder": "Ryujinx 폴더 열기",
"MenuBarFileOpenScreenshotsFolder": "스크린샷 폴더 열기",
"MenuBarFileOpenLogsFolder": "로그 폴더 열기", "MenuBarFileOpenLogsFolder": "로그 폴더 열기",
"MenuBarFileExit": "_종료", "MenuBarFileExit": "_종료",
"MenuBarOptions": "옵션(_O)", "MenuBarOptions": "옵션(_O)",
@@ -549,8 +550,9 @@
"AboutDiscordUrlTooltipMessage": "기본 브라우저에서 Ryujinx 디스코드 서버에 대한 초대를 열려면 클릭하세요.", "AboutDiscordUrlTooltipMessage": "기본 브라우저에서 Ryujinx 디스코드 서버에 대한 초대를 열려면 클릭하세요.",
"AboutTwitterUrlTooltipMessage": "기본 브라우저에서 Ryujinx 트위터 페이지를 열려면 클릭하세요.", "AboutTwitterUrlTooltipMessage": "기본 브라우저에서 Ryujinx 트위터 페이지를 열려면 클릭하세요.",
"AboutRyujinxAboutTitle": "정보 :", "AboutRyujinxAboutTitle": "정보 :",
"AboutRyujinxAboutContent": "Ryujinx는 닌텐도 스위치™용 에뮬레이터입니다.\nPatreon에서 지원해 주세요.\n트위터나 디스코드에서 최신 소식을 받아보세요.\n기여에 참여하고자 하는 개발자는 GitHub 또는 디스코드에서 자세한 내용을 확인할 수 있습니다.", "AboutRyujinxAboutContent": "Ryujinx는 닌텐도 스위치™용 에뮬레이터입니다.\n\n기여에 참여하고자 하는 개발자는 GitHub 자세한 내용을 확인할 수 있습니다.",
"AboutRyujinxMaintainersTitle": "유지 관리 :", "AboutRyujinxMaintainersTitle": "유지 관리 :",
"AboutRyujinxFormerMaintainersTitle": "이전 유지 보수 :",
"AboutRyujinxMaintainersContentTooltipMessage": "기본 브라우저에서 기여자 페이지를 열려면 클릭하세요.", "AboutRyujinxMaintainersContentTooltipMessage": "기본 브라우저에서 기여자 페이지를 열려면 클릭하세요.",
"AboutRyujinxSupprtersTitle": "Patreon에서 후원:", "AboutRyujinxSupprtersTitle": "Patreon에서 후원:",
"AmiiboSeriesLabel": "Amiibo 시리즈", "AmiiboSeriesLabel": "Amiibo 시리즈",
@@ -624,6 +626,7 @@
"LoadApplicationFileTooltip": "파일 탐색기를 열어 불러올 스위치 호환 파일 선택", "LoadApplicationFileTooltip": "파일 탐색기를 열어 불러올 스위치 호환 파일 선택",
"LoadApplicationFolderTooltip": "파일 탐색기를 열어 불러올 스위치 호환 압축 해제 응용 프로그램 선택", "LoadApplicationFolderTooltip": "파일 탐색기를 열어 불러올 스위치 호환 압축 해제 응용 프로그램 선택",
"OpenRyujinxFolderTooltip": "Ryujinx 파일 시스템 폴더 열기", "OpenRyujinxFolderTooltip": "Ryujinx 파일 시스템 폴더 열기",
"OpenScreenshotFolderTooltip": "Ryujinx 스크린샷 폴더 열기",
"OpenRyujinxLogsTooltip": "로그가 기록된 폴더 열기", "OpenRyujinxLogsTooltip": "로그가 기록된 폴더 열기",
"ExitTooltip": "Ryujinx 종료", "ExitTooltip": "Ryujinx 종료",
"OpenSettingsTooltip": "설정 창 열기", "OpenSettingsTooltip": "설정 창 열기",

Some files were not shown because too many files have changed in this diff Show More