Implement VK_EXT_extended_dynamic_state3

This commit is contained in:
sunshineinabox 2024-05-17 16:32:05 -07:00
parent 66b6b46716
commit 19db5a0ce2
7 changed files with 342 additions and 50 deletions

View file

@ -39,9 +39,9 @@
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />

View file

@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsExtendedDynamicState2;
public readonly bool SupportsExtendedDynamicState3;
public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors;
@ -72,6 +73,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsConditionalRendering,
bool supportsExtendedDynamicState,
bool supportsExtendedDynamicState2,
bool supportsExtendedDynamicState3,
bool supportsMultiView,
bool supportsNullDescriptors,
bool supportsPushDescriptors,
@ -111,6 +113,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
SupportsExtendedDynamicState3 = supportsExtendedDynamicState3;
SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors;

View file

@ -884,7 +884,15 @@ namespace Ryujinx.Graphics.Vulkan
public void SetDepthClamp(bool clamp)
{
_newState.DepthClampEnable = clamp;
if (Gd.ExtendedDynamicState3Features.ExtendedDynamicState3DepthClampEnable)
{
DynamicState.SetDepthClampEnable(clamp);
}
else
{
_newState.DepthClampEnable = clamp;
}
SignalStateChange();
}
@ -989,7 +997,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetLogicOpState(bool enable, LogicalOp op)
{
if (_supportExtDynamic2 && Gd.ExtendedLogicOp)
if (Gd.ExtendedDynamicState2Features.ExtendedDynamicState2LogicOp)
{
DynamicState.SetLogicOp(op.Convert());
}
@ -998,21 +1006,53 @@ namespace Ryujinx.Graphics.Vulkan
_newState.LogicOp = op.Convert();
}
_newState.LogicOpEnable = enable;
if (Gd.ExtendedDynamicState3Features.ExtendedDynamicState3LogicOpEnable)
{
DynamicState.SetLogicOpEnable(enable);
}
else
{
_newState.LogicOpEnable = enable;
}
SignalStateChange();
}
public void SetMultisampleState(MultisampleDescriptor multisample)
{
_newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable;
_newState.AlphaToOneEnable = multisample.AlphaToOneEnable;
if (Gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToCoverageEnable)
{
DynamicState.SetAlphaToCoverEnable(multisample.AlphaToCoverageEnable);
}
else
{
_newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable;
}
if (Gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToOneEnable)
{
DynamicState.SetAlphaToOneEnable(multisample.AlphaToOneEnable);
}
else
{
_newState.AlphaToOneEnable = multisample.AlphaToOneEnable;
}
SignalStateChange();
}
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
{
_newState.PatchControlPoints = (uint)vertices;
if (Gd.ExtendedDynamicState2Features.ExtendedDynamicState2PatchControlPoints)
{
DynamicState.SetPatchControlPoints((uint)vertices);
}
else
{
_newState.PatchControlPoints = (uint)vertices;
}
SignalStateChange();
// TODO: Default levels (likely needs emulation on shaders?)

View file

@ -52,6 +52,15 @@ namespace Ryujinx.Graphics.Vulkan
private LogicOp _logicOp;
private uint _patchControlPoints;
private bool _logicOpEnable;
private bool _depthClampEnable;
private bool _alphaToCoverEnable;
private bool _alphaToOneEnable;
[Flags]
private enum DirtyFlags
{
@ -69,9 +78,14 @@ namespace Ryujinx.Graphics.Vulkan
LineWidth = 1 << 10,
RasterDiscard = 1 << 11,
LogicOp = 1 << 12,
DepthClampEnable = 1 << 13,
LogicOpEnalbe = 1 << 14,
AlphaToCover = 1 << 15,
AlphaToOne = 1 << 16,
PatchControlPoints = 1 << 17,
Standard = Blend | DepthBias | Scissor | Stencil | Viewport | LineWidth,
Extended = CullMode | FrontFace | DepthTestBool | DepthTestCompareOp | StencilTestEnable,
Extended2 = RasterDiscard | LogicOp,
Extended2 = RasterDiscard | LogicOp | PatchControlPoints,
}
private DirtyFlags _dirty;
@ -219,6 +233,41 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.LogicOp;
}
public void SetPatchControlPoints(uint points)
{
_patchControlPoints = points;
_dirty |= DirtyFlags.PatchControlPoints;
}
public void SetLogicOpEnable(bool logicOpEnable)
{
_logicOpEnable = logicOpEnable;
_dirty |= DirtyFlags.LogicOpEnalbe;
}
public void SetDepthClampEnable(bool depthClampEnable)
{
_depthClampEnable = depthClampEnable;
_dirty |= DirtyFlags.DepthClampEnable;
}
public void SetAlphaToCoverEnable(bool alphaToCoverEnable)
{
_alphaToCoverEnable = alphaToCoverEnable;
_dirty |= DirtyFlags.AlphaToCover;
}
public void SetAlphaToOneEnable(bool alphaToOneEnable)
{
_alphaToOneEnable = alphaToOneEnable;
_dirty |= DirtyFlags.AlphaToOne;
}
public void ForceAllDirty(VulkanRenderer gd)
{
_dirty = DirtyFlags.Standard;
@ -238,10 +287,35 @@ namespace Ryujinx.Graphics.Vulkan
_dirty &= ~DirtyFlags.LineWidth;
}
if (!gd.ExtendedLogicOp)
if (!gd.ExtendedDynamicState2Features.ExtendedDynamicState2LogicOp)
{
_dirty &= ~DirtyFlags.LogicOp;
}
if (!gd.ExtendedDynamicState2Features.ExtendedDynamicState2PatchControlPoints)
{
_dirty &= ~DirtyFlags.LogicOp;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToCoverageEnable)
{
_dirty = DirtyFlags.AlphaToCover;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToOneEnable)
{
_dirty = DirtyFlags.AlphaToOne;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3DepthClampEnable)
{
_dirty = DirtyFlags.DepthClampEnable;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3LogicOpEnable)
{
_dirty = DirtyFlags.LogicOpEnalbe;
}
}
public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
@ -306,11 +380,36 @@ namespace Ryujinx.Graphics.Vulkan
RecordRasterizationDiscard(gd, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.RasterDiscard))
if (_dirty.HasFlag(DirtyFlags.LogicOp))
{
RecordLogicOp(gd, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.PatchControlPoints))
{
RecordPatchControlPoints(gd, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.LogicOpEnalbe))
{
RecordLogicOpEnable(gd, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.DepthClampEnable))
{
RecordDepthClampEnable(gd, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.AlphaToCover))
{
RecordAlphaToCoverEnable(gd, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.AlphaToOne))
{
RecordAlphaToOneEnable(gd, commandBuffer);
}
_dirty = DirtyFlags.None;
}
@ -421,6 +520,31 @@ namespace Ryujinx.Graphics.Vulkan
gd.ExtendedDynamicState2Api.CmdSetLogicOp(commandBuffer, _logicOp);
}
private readonly void RecordLogicOpEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
gd.ExtendedDynamicState3Api.CmdSetLogicOpEnable(commandBuffer, _logicOpEnable);
}
private readonly void RecordDepthClampEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
gd.ExtendedDynamicState3Api.CmdSetDepthClampEnable(commandBuffer, _depthClampEnable);
}
private readonly void RecordAlphaToCoverEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
gd.ExtendedDynamicState3Api.CmdSetAlphaToCoverageEnable(commandBuffer, _alphaToCoverEnable);
}
private readonly void RecordAlphaToOneEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
gd.ExtendedDynamicState3Api.CmdSetAlphaToOneEnable(commandBuffer, _alphaToOneEnable);
}
private readonly void RecordPatchControlPoints(VulkanRenderer gd, CommandBuffer commandBuffer)
{
gd.ExtendedDynamicState2Api.CmdSetPatchControlPoints(commandBuffer, _patchControlPoints);
}
private readonly void RecordLineWidth(Vk api, CommandBuffer commandBuffer)
{
if (!OperatingSystem.IsMacOS())

View file

@ -404,7 +404,7 @@ namespace Ryujinx.Graphics.Vulkan
{
UpdateVertexAttributeDescriptions(gd);
}
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
bool supportsExtDynamicState2 = gd.Capabilities.SupportsExtendedDynamicState2;
@ -457,7 +457,7 @@ namespace Ryujinx.Graphics.Vulkan
{
primitiveRestartEnable = true;
}
var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo
{
SType = StructureType.PipelineInputAssemblyStateCreateInfo,
@ -494,12 +494,12 @@ namespace Ryujinx.Graphics.Vulkan
rasterizationState.DepthBiasEnable = DepthBiasEnable;
rasterizationState.RasterizerDiscardEnable = RasterizerDiscardEnable;
}
var viewportState = new PipelineViewportStateCreateInfo
{
SType = StructureType.PipelineViewportStateCreateInfo,
};
if (!supportsExtDynamicState)
{
viewportState.ViewportCount = ViewportsCount;
@ -534,7 +534,7 @@ namespace Ryujinx.Graphics.Vulkan
MinDepthBounds = MinDepthBounds,
MaxDepthBounds = MaxDepthBounds,
};
if (!supportsExtDynamicState)
{
var stencilFront = new StencilOpState(
@ -613,9 +613,54 @@ namespace Ryujinx.Graphics.Vulkan
colorBlendState.PNext = &colorBlendAdvancedState;
}
int dynamicStatesCount = supportsExtDynamicState ? (isMoltenVk ? 18 : 19) : (isMoltenVk ? 7 : 8);
int baseDynamicStatesCount = 7;
int additionalDynamicStatesCount = 0;
if (!isMoltenVk)
{
baseDynamicStatesCount++;
}
if (supportsExtDynamicState)
{
additionalDynamicStatesCount += isMoltenVk ? 10 : 11;
}
if (supportsExtDynamicState2)
{
additionalDynamicStatesCount += 2;
if (gd.ExtendedDynamicState2Features.ExtendedDynamicState2LogicOp)
{
additionalDynamicStatesCount++;
}
if (gd.ExtendedDynamicState2Features.ExtendedDynamicState2PatchControlPoints)
{
additionalDynamicStatesCount++;
}
}
if (supportsExtDynamicState3)
{
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3DepthClampEnable)
{
additionalDynamicStatesCount++;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3LogicOpEnable)
{
additionalDynamicStatesCount++;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToCoverageEnable)
{
additionalDynamicStatesCount++;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToOneEnable)
{
additionalDynamicStatesCount++;
}
}
int dynamicStatesCount = baseDynamicStatesCount + additionalDynamicStatesCount;
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
dynamicStates[0] = DynamicState.Viewport;
@ -625,33 +670,64 @@ namespace Ryujinx.Graphics.Vulkan
dynamicStates[4] = DynamicState.StencilWriteMask;
dynamicStates[5] = DynamicState.StencilReference;
dynamicStates[6] = DynamicState.BlendConstants;
if(!isMoltenVk)
int currentIndex = 7;
if (!isMoltenVk)
{
dynamicStates[7] = DynamicState.LineWidth;
dynamicStates[currentIndex++] = DynamicState.LineWidth;
}
if (supportsExtDynamicState)
{
int index = (isMoltenVk ? 7 : 8);
if (!isMoltenVk) {
dynamicStates[index++] = DynamicState.VertexInputBindingStrideExt;
if (!isMoltenVk)
{
dynamicStates[currentIndex++] = DynamicState.VertexInputBindingStrideExt;
}
dynamicStates[index++] = DynamicState.CullModeExt;
dynamicStates[index++] = DynamicState.FrontFaceExt;
dynamicStates[index++] = DynamicState.DepthTestEnableExt;
dynamicStates[index++] = DynamicState.DepthWriteEnableExt;
dynamicStates[index++] = DynamicState.DepthCompareOpExt;
dynamicStates[index++] = DynamicState.StencilTestEnableExt;
dynamicStates[index++] = DynamicState.ViewportWithCountExt;
dynamicStates[index++] = DynamicState.ScissorWithCountExt;
dynamicStates[index] = DynamicState.StencilOpExt;
dynamicStates[currentIndex++] = DynamicState.CullModeExt;
dynamicStates[currentIndex++] = DynamicState.FrontFaceExt;
dynamicStates[currentIndex++] = DynamicState.DepthTestEnableExt;
dynamicStates[currentIndex++] = DynamicState.DepthWriteEnableExt;
dynamicStates[currentIndex++] = DynamicState.DepthCompareOpExt;
dynamicStates[currentIndex++] = DynamicState.StencilTestEnableExt;
dynamicStates[currentIndex++] = DynamicState.ViewportWithCountExt;
dynamicStates[currentIndex++] = DynamicState.ScissorWithCountExt;
dynamicStates[currentIndex++] = DynamicState.StencilOpExt;
}
if (supportsExtDynamicState2)
{
dynamicStates[16] = DynamicState.DepthBiasEnableExt;
dynamicStates[17] = DynamicState.RasterizerDiscardEnableExt;
dynamicStates[currentIndex++] = DynamicState.DepthBiasEnableExt;
dynamicStates[currentIndex++] = DynamicState.RasterizerDiscardEnableExt;
if (gd.ExtendedDynamicState2Features.ExtendedDynamicState2LogicOp)
{
dynamicStates[currentIndex++] = DynamicState.LogicOpExt;
}
if (gd.ExtendedDynamicState2Features.ExtendedDynamicState2PatchControlPoints)
{
dynamicStates[currentIndex++] = DynamicState.PatchControlPointsExt;
}
}
if (supportsExtDynamicState3)
{
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3DepthClampEnable)
{
dynamicStates[currentIndex++] = DynamicState.DepthClampEnableExt;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3LogicOpEnable)
{
dynamicStates[currentIndex++] = DynamicState.LogicOpEnableExt;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToCoverageEnable)
{
dynamicStates[currentIndex++] = DynamicState.AlphaToCoverageEnableExt;
}
if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3AlphaToOneEnable)
{
dynamicStates[currentIndex++] = DynamicState.AlphaToOneEnableExt;
}
}
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo

View file

@ -263,7 +263,7 @@ namespace Ryujinx.Graphics.Vulkan
return InvalidIndex;
}
internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount, out bool extendedLogicOp)
internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount, out PhysicalDeviceExtendedDynamicState2FeaturesEXT extendedDynamicState2Features, out PhysicalDeviceExtendedDynamicState3FeaturesEXT extendedDynamicState3Features)
{
if (queueCount > QueuesCount)
{
@ -322,6 +322,17 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesExtExtendedDynamicState2;
}
PhysicalDeviceExtendedDynamicState3FeaturesEXT supportedFeaturesExtExtendedDynamicState3 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState3FeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState3.ExtensionName))
{
features2.PNext = &supportedFeaturesExtExtendedDynamicState3;
}
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new()
{
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
@ -451,18 +462,45 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresExtendedDynamicState;
var featuresExtendedDynamicState2 = new PhysicalDeviceExtendedDynamicState2FeaturesEXT()
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = pExtendedFeatures,
ExtendedDynamicState2 = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName),
ExtendedDynamicState2LogicOp = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp,
ExtendedDynamicState2PatchControlPoints = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
};
var featuresExtendedDynamicState2 = new PhysicalDeviceExtendedDynamicState2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = pExtendedFeatures,
ExtendedDynamicState2 =
physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName),
ExtendedDynamicState2LogicOp =
supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp,
ExtendedDynamicState2PatchControlPoints = supportedFeaturesExtExtendedDynamicState2
.ExtendedDynamicState2PatchControlPoints,
};
extendedLogicOp = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp;
pExtendedFeatures = &featuresExtendedDynamicState2;
}
pExtendedFeatures = &featuresExtendedDynamicState2;
extendedDynamicState2Features = supportedFeaturesExtExtendedDynamicState2;
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState3.ExtensionName))
{
var featuresExtendedDynamicState3 = new PhysicalDeviceExtendedDynamicState3FeaturesEXT()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState3FeaturesExt,
PNext = pExtendedFeatures,
ExtendedDynamicState3LogicOpEnable = supportedFeaturesExtExtendedDynamicState3.ExtendedDynamicState3LogicOpEnable,
ExtendedDynamicState3AlphaToCoverageEnable = supportedFeaturesExtExtendedDynamicState3.ExtendedDynamicState3AlphaToCoverageEnable,
ExtendedDynamicState3AlphaToOneEnable = supportedFeaturesExtExtendedDynamicState3.ExtendedDynamicState3AlphaToOneEnable,
ExtendedDynamicState3DepthClampEnable = supportedFeaturesExtExtendedDynamicState3.ExtendedDynamicState3DepthClampEnable,
};
pExtendedFeatures = &featuresExtendedDynamicState3;
}
//Seems to be a error in Silk.Net bidings investigate further later
supportedFeaturesExtExtendedDynamicState3.ExtendedDynamicState3DepthClampEnable = false;
extendedDynamicState3Features = supportedFeaturesExtExtendedDynamicState3;
var featuresVk11 = new PhysicalDeviceVulkan11Features
{

View file

@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
internal ExtConditionalRendering ConditionalRenderingApi { get; private set; }
internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; }
internal ExtExtendedDynamicState2 ExtendedDynamicState2Api { get; private set; }
internal ExtExtendedDynamicState3 ExtendedDynamicState3Api { get; private set; }
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
@ -99,7 +100,10 @@ namespace Ryujinx.Graphics.Vulkan
public bool PreferThreading => true;
public bool ExtendedLogicOp;
public PhysicalDeviceExtendedDynamicState2FeaturesEXT ExtendedDynamicState2Features;
public PhysicalDeviceExtendedDynamicState3FeaturesEXT ExtendedDynamicState3Features;
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
@ -141,6 +145,11 @@ namespace Ryujinx.Graphics.Vulkan
ExtendedDynamicState2Api = extendedDynamicState2Api;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState3 extendedDynamicState3Api))
{
ExtendedDynamicState3Api = extendedDynamicState3Api;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
{
PushDescriptorApi = pushDescriptorApi;
@ -392,6 +401,7 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState3.ExtensionName),
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk,
supportsPushDescriptors && !IsMoltenVk,
@ -456,9 +466,10 @@ namespace Ryujinx.Graphics.Vulkan
var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(Api, _physicalDevice, _surface, out uint maxQueueCount);
_device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount, out bool extendedLogicOp);
_device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount, out PhysicalDeviceExtendedDynamicState2FeaturesEXT extendedDynamicState2Features, out PhysicalDeviceExtendedDynamicState3FeaturesEXT extendedDynamicState3Features);
ExtendedLogicOp = extendedLogicOp;
ExtendedDynamicState2Features = extendedDynamicState2Features;
ExtendedDynamicState3Features = extendedDynamicState3Features;
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
{