diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index f78184c3bc..5433f3402e 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -697,7 +697,7 @@ namespace Ryujinx.Graphics.Vulkan var oldStencilTestEnable = _supportExtDynamic ? DynamicState.StencilTestEnable : _newState.StencilTestEnable; var oldDepthTestEnable = _supportExtDynamic ? DynamicState.DepthTestEnable : _newState.DepthTestEnable; var oldDepthWriteEnable = _supportExtDynamic ? DynamicState.DepthWriteEnable : _newState.DepthWriteEnable; - var oldTopology = _newState.Topology; + var oldTopology = Gd.SupportsUnrestrictedDynamicTopology ? DynamicState.Topology : _newState.Topology; var oldViewports = DynamicState.Viewports; var oldViewportsCount = _supportExtDynamic ? DynamicState.ViewportsCount : _newState.ViewportsCount; @@ -740,7 +740,14 @@ namespace Ryujinx.Graphics.Vulkan _newState.ViewportsCount = oldViewportsCount; } - _newState.Topology = oldTopology; + if (Gd.SupportsUnrestrictedDynamicTopology) + { + DynamicState.SetPrimitiveTopology(oldTopology); + } + else + { + _newState.Topology = oldTopology; + } DynamicState.SetViewports(ref oldViewports, oldViewportsCount); @@ -1128,7 +1135,14 @@ namespace Ryujinx.Graphics.Vulkan var vkTopology = Gd.TopologyRemap(topology).Convert(); - _newState.Topology = vkTopology; + if (Gd.SupportsUnrestrictedDynamicTopology) + { + DynamicState.SetPrimitiveTopology(vkTopology); + } + else + { + _newState.Topology = vkTopology; + } SignalStateChange(); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index 61d9a65020..ecf8013bec 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -65,6 +65,8 @@ namespace Ryujinx.Graphics.Vulkan private bool _primitiveRestartEnable; + public PrimitiveTopology Topology; + [Flags] private enum DirtyFlags { @@ -89,10 +91,11 @@ namespace Ryujinx.Graphics.Vulkan PatchControlPoints = 1 << 17, DepthMode = 1 << 18, PrimitiveRestart = 1 << 19, + PrimitiveTopology = 1 << 20, Standard = Blend | DepthBias | Scissor | Stencil | Viewport | LineWidth, Extended = CullMode | FrontFace | DepthTestBool | DepthTestCompareOp | StencilTestEnable, Extended2 = RasterDiscard | LogicOp | PatchControlPoints | PrimitiveRestart, - Extended3 = DepthClampEnable | LogicOpEnable | AlphaToCover | AlphaToOne | DepthMode, + Extended3 = DepthClampEnable | LogicOpEnable | AlphaToCover | AlphaToOne | DepthMode | PrimitiveTopology, } private DirtyFlags _dirty; @@ -218,6 +221,12 @@ namespace Ryujinx.Graphics.Vulkan _dirty |= DirtyFlags.PrimitiveRestart; } + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + Topology = topology; + _dirty |= DirtyFlags.PrimitiveTopology; + } + public void SetLogicOp(LogicOp op) { _logicOp = op; @@ -318,6 +327,11 @@ namespace Ryujinx.Graphics.Vulkan { _dirty &= ~DirtyFlags.DepthMode; } + + if (!gd.SupportsUnrestrictedDynamicTopology) + { + _dirty &= ~DirtyFlags.PrimitiveTopology; + } } public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer) @@ -387,6 +401,11 @@ namespace Ryujinx.Graphics.Vulkan RecordPrimitiveRestartEnable(gd, commandBuffer); } + if (_dirty.HasFlag(DirtyFlags.PrimitiveTopology)) + { + RecordPrimitiveRestartEnable(gd, commandBuffer); + } + if (_dirty.HasFlag(DirtyFlags.LogicOp)) { RecordLogicOp(gd, commandBuffer); @@ -422,6 +441,11 @@ namespace Ryujinx.Graphics.Vulkan RecordDepthMode(gd, commandBuffer); } + if (_dirty.HasFlag(DirtyFlags.PrimitiveTopology)) + { + RecordPrimitiveTopology(gd, commandBuffer); + } + _dirty = DirtyFlags.None; } @@ -538,6 +562,11 @@ namespace Ryujinx.Graphics.Vulkan gd.ExtendedDynamicState2Api.CmdSetPrimitiveRestartEnable(commandBuffer, _primitiveRestartEnable); } + private readonly void RecordPrimitiveTopology(VulkanRenderer gd, CommandBuffer commandBuffer) + { + gd.ExtendedDynamicStateApi.CmdSetPrimitiveTopology(commandBuffer, Topology); + } + private readonly void RecordLogicOp(VulkanRenderer gd, CommandBuffer commandBuffer) { if (gd.ExtendedDynamicState3Features.ExtendedDynamicState3LogicOpEnable && !_logicOpEnable) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 50e3926ca7..f8b71ed15c 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -435,9 +435,13 @@ namespace Ryujinx.Graphics.Vulkan var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo { SType = StructureType.PipelineInputAssemblyStateCreateInfo, - Topology = Topology, }; + if (!gd.SupportsUnrestrictedDynamicTopology) + { + inputAssemblyState.Topology = Topology; + } + var tessellationState = new PipelineTessellationStateCreateInfo { SType = StructureType.PipelineTessellationStateCreateInfo, @@ -633,6 +637,11 @@ namespace Ryujinx.Graphics.Vulkan { additionalDynamicStatesCount++; } + + if (gd.SupportsUnrestrictedDynamicTopology) + { + additionalDynamicStatesCount++; + } } int dynamicStatesCount = baseDynamicStatesCount + additionalDynamicStatesCount; @@ -710,6 +719,11 @@ namespace Ryujinx.Graphics.Vulkan { dynamicStates[currentIndex++] = DynamicState.DepthClipNegativeOneToOneExt; } + + if (gd.SupportsUnrestrictedDynamicTopology) + { + dynamicStates[currentIndex++] = DynamicState.PrimitiveTopology; + } } var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 69d0759511..5379367967 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -104,6 +104,8 @@ namespace Ryujinx.Graphics.Vulkan public PhysicalDeviceExtendedDynamicState3FeaturesEXT ExtendedDynamicState3Features; + public bool SupportsUnrestrictedDynamicTopology; + public event EventHandler ScreenCaptured; @@ -211,6 +213,17 @@ namespace Ryujinx.Graphics.Vulkan properties2.PNext = &propertiesTransformFeedback; } + PhysicalDeviceExtendedDynamicState3PropertiesEXT propertiesExtendedDynamicState3 = new() + { + SType = StructureType.PhysicalDeviceExtendedDynamicState3PropertiesExt, + }; + + if (_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState3.ExtensionName)) + { + propertiesExtendedDynamicState3.PNext = properties2.PNext; + properties2.PNext = &propertiesExtendedDynamicState3; + } + PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new() { SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr, @@ -425,6 +438,8 @@ namespace Ryujinx.Graphics.Vulkan properties.Limits.SubTexelPrecisionBits, minResourceAlignment); + SupportsUnrestrictedDynamicTopology = propertiesExtendedDynamicState3.DynamicPrimitiveTopologyUnrestricted; + IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device);