diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 12b305728d..153eef8b01 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -709,8 +709,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { DepthTestDescriptor descriptor = new( _state.State.DepthTestEnable, - _state.State.DepthWriteEnable && _state.State.DepthTestEnable, - _state.State.DepthTestEnable ? _state.State.DepthTestFunc : default); + _state.State.DepthWriteEnable, + _state.State.DepthTestFunc); _pipeline.DepthTest = descriptor; _context.Renderer.Pipeline.SetDepthTest(descriptor); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index d5a1e9855e..7bf4a346aa 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; @@ -675,9 +676,9 @@ namespace Ryujinx.Graphics.Vulkan if (_supportExtDynamic) { - if (oldTopologyClass != _newState.Topology) + if (oldTopologyClass != _newState.Topology.ConvertToClass()) { - _newState.Topology = oldTopologyClass; + _newState.Topology = oldTopology; } DynamicState.SetCullMode(oldCullMode); @@ -983,7 +984,7 @@ namespace Ryujinx.Graphics.Vulkan } else { - _newState.LogicOp = logicOpEnable ? op.Convert() : default; + _newState.LogicOp = op.Convert(); } SignalStateChange(); @@ -1049,9 +1050,9 @@ namespace Ryujinx.Graphics.Vulkan { var newTopologyClass = vkTopology.ConvertToClass(); - if ((_newState.Topology != newTopologyClass)) + if ((_newState.Topology.ConvertToClass() != newTopologyClass)) { - _newState.Topology = newTopologyClass; + _newState.Topology = vkTopology; } DynamicState.SetPrimitiveTopology(vkTopology); @@ -1401,7 +1402,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription( (uint)binding, - _supportExtDynamic && !Gd.IsMoltenVk ? default : (uint)vertexBuffer.Stride, + (uint)vertexBuffer.Stride, inputRate); int vbSize = vertexBuffer.Buffer.Size; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 870a3fd11f..fcc27c6676 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.PolygonMode = PolygonMode.Fill; // Not implemented. - pipeline.Topology = extendedDynamicState ? gd.TopologyRemap(state.Topology).Convert().ConvertToClass() : gd.TopologyRemap(state.Topology).Convert(); + pipeline.Topology = gd.TopologyRemap(state.Topology).Convert(); if (!extendedDynamicState) { @@ -219,6 +219,11 @@ namespace Ryujinx.Graphics.Vulkan (state.DepthBiasFactor != 0 && state.DepthBiasUnits != 0)); } + if (!extendedDynamicState2.ExtendedDynamicState2LogicOp) + { + pipeline.LogicOp = state.LogicOp.Convert(); + } + if (!extendedDynamicState2.ExtendedDynamicState2PatchControlPoints) { pipeline.PatchControlPoints = state.PatchControlPoints; @@ -271,7 +276,7 @@ namespace Ryujinx.Graphics.Vulkan // TODO: Support divisor > 1 pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription( (uint)i + 1, - extendedDynamicState && !gd.IsMoltenVk ? default : (uint)alignedStride, + (uint)alignedStride, inputRate); } } @@ -334,14 +339,6 @@ namespace Ryujinx.Graphics.Vulkan pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask; pipeline.Internal.LogicOpsAllowed = attachmentCount == 0 || !allFormatsFloatOrSrgb; - if (!extendedDynamicState2.ExtendedDynamicState2LogicOp) - { - bool logicOpEnable = state.LogicOpEnable && - (gd.Vendor == Vendor.Nvidia || pipeline.Internal.LogicOpsAllowed); - - pipeline.LogicOp = logicOpEnable ? state.LogicOp.Convert() : default; - } - return pipeline; } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index b25cdccd54..ad9d2d657d 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -256,6 +256,8 @@ namespace Ryujinx.Graphics.Vulkan private bool _supportsExtDynamicState; private PhysicalDeviceExtendedDynamicState2FeaturesEXT _supportsExtDynamicState2; private bool _supportsFeedBackLoopDynamicState; + private uint _blendEnables; + public void Initialize(HardwareCapabilities capabilities) @@ -297,7 +299,6 @@ namespace Ryujinx.Graphics.Vulkan DepthWriteEnable = false; DepthCompareOp = 0; StencilTestEnable = false; - } if (_supportsExtDynamicState2.ExtendedDynamicState2) @@ -368,6 +369,74 @@ namespace Ryujinx.Graphics.Vulkan 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 CreateGraphicsPipeline( VulkanRenderer gd, Device device, @@ -376,6 +445,8 @@ namespace Ryujinx.Graphics.Vulkan RenderPass renderPass, 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) @@ -413,12 +484,10 @@ namespace Ryujinx.Graphics.Vulkan PVertexBindingDescriptions = pVertexBindingDescriptions, }; - var topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology; - var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo { SType = StructureType.PipelineInputAssemblyStateCreateInfo, - Topology = topology, + Topology = Topology, }; PipelineTessellationStateCreateInfo tessellationState; @@ -497,27 +566,8 @@ namespace Ryujinx.Graphics.Vulkan if (!_supportsExtDynamicState2.ExtendedDynamicState2) { - 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; - - inputAssemblyState.PrimitiveRestartEnable = primitiveRestartEnable; + inputAssemblyState.PrimitiveRestartEnable = PrimitiveRestartEnable; rasterizationState.DepthBiasEnable = DepthBiasEnable; rasterizationState.RasterizerDiscardEnable = RasterizerDiscardEnable; } @@ -531,37 +581,12 @@ namespace Ryujinx.Graphics.Vulkan }; } - uint blendEnables = 0; - - if (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 { SType = StructureType.PipelineColorBlendStateCreateInfo, AttachmentCount = ColorBlendAttachmentStateCount, PAttachments = pColorBlendAttachmentState, - LogicOpEnable = logicOpEnable, + LogicOpEnable = LogicOpEnable, }; if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp) @@ -650,27 +675,9 @@ namespace Ryujinx.Graphics.Vulkan PDynamicStates = dynamicStates, }; - PipelineCreateFlags flags = 0; - - if (gd.Capabilities.SupportsAttachmentFeedbackLoop && !_supportsFeedBackLoopDynamicState) - { - FeedbackLoopAspects aspects = FeedbackLoopAspects; - - if ((aspects & FeedbackLoopAspects.Color) != 0) - { - flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt; - } - - if ((aspects & FeedbackLoopAspects.Depth) != 0) - { - flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt; - } - } - var pipelineCreateInfo = new GraphicsPipelineCreateInfo { SType = StructureType.GraphicsPipelineCreateInfo, - Flags = flags, StageCount = StagesCount, PStages = Stages.Pointer, PVertexInputState = &vertexInputState, @@ -685,6 +692,21 @@ namespace Ryujinx.Graphics.Vulkan RenderPass = renderPass, }; + if (gd.Capabilities.SupportsAttachmentFeedbackLoop && !_supportsFeedBackLoopDynamicState) + { + FeedbackLoopAspects aspects = FeedbackLoopAspects; + + if ((aspects & FeedbackLoopAspects.Color) != 0) + { + pipelineCreateInfo.Flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt; + } + + if ((aspects & FeedbackLoopAspects.Depth) != 0) + { + pipelineCreateInfo.Flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt; + } + } + if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints) { pipelineCreateInfo.PTessellationState = &tessellationState; @@ -702,21 +724,21 @@ namespace Ryujinx.Graphics.Vulkan 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(new DisposablePipeline(gd.Api, device, pipelineHandle)); 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; }