diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 6318008814..bf22a120a5 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -84,8 +84,8 @@ namespace Ryujinx.Graphics.Vulkan private bool _tfEnabled; private bool _tfActive; - private bool _supportExtDynamic; - private bool _supportExtDynamic2; + private readonly bool _supportExtDynamic; + private readonly bool _supportExtDynamic2; private readonly PipelineColorBlendAttachmentState[] _storedBlend; @@ -126,7 +126,7 @@ namespace Ryujinx.Graphics.Vulkan _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets]; _supportExtDynamic = gd.Capabilities.SupportsExtendedDynamicState; - + _supportExtDynamic2 = gd.Capabilities.SupportsExtendedDynamicState2; @@ -714,7 +714,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthTestEnable = false; _newState.DepthWriteEnable = false; } - + SignalStateChange(); Gd.HelperShader.DrawTexture( @@ -739,7 +739,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthWriteEnable = oldDepthWriteEnable; _newState.ViewportsCount = oldViewportsCount; } - + _newState.Topology = oldTopology; DynamicState.SetViewports(ref oldViewports, oldViewportsCount); @@ -878,7 +878,7 @@ namespace Ryujinx.Graphics.Vulkan { _newState.DepthBiasEnable = enables != 0; } - + SignalStateChange(); } @@ -911,7 +911,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthWriteEnable = depthTest.WriteEnable; _newState.DepthCompareOp = depthTest.Func.Convert(); } - + SignalStateChange(); } @@ -983,14 +983,22 @@ namespace Ryujinx.Graphics.Vulkan { DynamicState.SetLineWidth(Gd.Capabilities.SupportsWideLines ? width : 1.0f); } - + SignalStateChange(); } public void SetLogicOpState(bool enable, LogicalOp op) { + if (_supportExtDynamic2 && Gd.ExtendedLogicOp) + { + DynamicState.SetLogicOp(op.Convert()); + } + else + { + _newState.LogicOp = op.Convert(); + } + _newState.LogicOpEnable = enable; - _newState.LogicOp = op.Convert(); SignalStateChange(); } @@ -1032,9 +1040,9 @@ namespace Ryujinx.Graphics.Vulkan _topology = topology; var vkTopology = Gd.TopologyRemap(topology).Convert(); - + _newState.Topology = vkTopology; - + SignalStateChange(); } @@ -1195,7 +1203,7 @@ namespace Ryujinx.Graphics.Vulkan stencilTest.FrontDpPass.Convert(), stencilTest.FrontDpFail.Convert(), stencilTest.FrontFunc.Convert()); - + DynamicState.SetStencilTest(stencilTest.TestEnable); } else @@ -1210,7 +1218,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert(); _newState.StencilTestEnable = stencilTest.TestEnable; } - + DynamicState.SetStencilMask((uint)stencilTest.BackFuncMask, (uint)stencilTest.BackMask, (uint)stencilTest.BackFuncRef, @@ -1454,11 +1462,11 @@ namespace Ryujinx.Graphics.Vulkan Clamp(viewport.DepthNear), Clamp(viewport.DepthFar))); } - + if (!_supportExtDynamic) { _newState.ViewportsCount = (uint)count; - } + } SignalStateChange(); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 9bc16b4de8..e8fbea9992 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -164,9 +164,9 @@ namespace Ryujinx.Graphics.Vulkan pipeline.DepthBoundsTestEnable = false; // Not implemented. pipeline.DepthClampEnable = state.DepthClampEnable; - + pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne; - + pipeline.HasDepthStencil = state.DepthStencilEnable; pipeline.LogicOpEnable = state.LogicOpEnable; pipeline.LogicOp = state.LogicOp.Convert(); @@ -188,11 +188,11 @@ namespace Ryujinx.Graphics.Vulkan pipeline.DepthTestEnable = state.DepthTest.TestEnable; pipeline.DepthWriteEnable = state.DepthTest.WriteEnable; pipeline.DepthCompareOp = state.DepthTest.Func.Convert(); - + pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None; - + pipeline.FrontFace = state.FrontFace.Convert(); - + if (gd.Capabilities.SupportsMultiView) { pipeline.ScissorsCount = Constants.MaxViewports; @@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.ScissorsCount = 1; pipeline.ViewportsCount = 1; } - + pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert(); pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert(); pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert(); @@ -213,12 +213,12 @@ namespace Ryujinx.Graphics.Vulkan pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert(); pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert(); pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert(); - + pipeline.StencilTestEnable = state.StencilTest.TestEnable; } - + pipeline.Topology = gd.TopologyRemap(state.Topology).Convert(); - + int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index efee04b10c..783f2bf710 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan private uint _frontCompareMask; private uint _frontWriteMask; private uint _frontReference; - + private bool _opToo; private StencilOp _backfailop; private StencilOp _backpassop; @@ -33,13 +33,13 @@ namespace Ryujinx.Graphics.Vulkan private CompareOp _frontcompareop; private float _linewidth; - + public bool _stencilTestEnable; public bool _depthtestEnable; public bool _depthwriteEnable; private CompareOp _depthCompareOp; - + private Array4 _blendConstants; public uint ViewportsCount; @@ -49,7 +49,9 @@ namespace Ryujinx.Graphics.Vulkan public FrontFace FrontFace; private bool _discard; - + + private LogicOp _logicOp; + [Flags] private enum DirtyFlags { @@ -61,14 +63,15 @@ namespace Ryujinx.Graphics.Vulkan Viewport = 1 << 4, CullMode = 1 << 5, FrontFace = 1 << 6, - DepthTestBool = 1 << 7, + DepthTestBool = 1 << 7, DepthTestCompareOp = 1 << 8, StencilTestEnable = 1 << 9, LineWidth = 1 << 10, RasterDiscard = 1 << 11, + LogicOp = 1 << 12, Standard = Blend | DepthBias | Scissor | Stencil | Viewport | LineWidth, Extended = CullMode | FrontFace | DepthTestBool | DepthTestCompareOp | StencilTestEnable, - Extended2 = RasterDiscard, + Extended2 = RasterDiscard | LogicOp, } private DirtyFlags _dirty; @@ -107,14 +110,14 @@ namespace Ryujinx.Graphics.Vulkan _dirty |= DirtyFlags.DepthTestBool; } - + public void SetDepthTestCompareOp(CompareOp depthTestOp) { _depthCompareOp = depthTestOp; _dirty |= DirtyFlags.DepthTestCompareOp; } - + public void SetStencilOp(StencilOp backFailOp, StencilOp backPassOp, StencilOp backDepthFailOp, @@ -133,10 +136,10 @@ namespace Ryujinx.Graphics.Vulkan _frontpassop = frontPassOp; _frontdepthfailop = frontDepthFailOp; _frontcompareop = frontCompareOp; - + _opToo = true; } - + public void SetStencilMask( uint backCompareMask, uint backWriteMask, @@ -154,11 +157,11 @@ namespace Ryujinx.Graphics.Vulkan _dirty |= DirtyFlags.Stencil; } - + public void SetStencilTest(bool stencilTestEnable) { _stencilTestEnable = stencilTestEnable; - + _dirty |= DirtyFlags.StencilTestEnable; } @@ -180,35 +183,42 @@ namespace Ryujinx.Graphics.Vulkan _dirty |= DirtyFlags.Viewport; } } - + public void SetCullMode(CullModeFlags cullMode) { CullMode = cullMode; - + _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 SetLogicOp(LogicOp op) + { + _logicOp = op; + + _dirty |= DirtyFlags.LogicOp; + } + public void ForceAllDirty(VulkanRenderer gd) { _dirty = DirtyFlags.Standard; @@ -227,6 +237,11 @@ namespace Ryujinx.Graphics.Vulkan { _dirty &= ~DirtyFlags.LineWidth; } + + if (!gd.ExtendedLogicOp) + { + _dirty &= ~DirtyFlags.LogicOp; + } } public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer) @@ -255,42 +270,47 @@ namespace Ryujinx.Graphics.Vulkan { RecordViewport(gd, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.CullMode)) { RecordCullMode(gd.ExtendedDynamicStateApi, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.FrontFace)) { RecordFrontFace(gd.ExtendedDynamicStateApi, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.DepthTestBool)) { RecordDepthTestBool(gd.ExtendedDynamicStateApi, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.DepthTestCompareOp)) { RecordDepthTestCompareOp(gd.ExtendedDynamicStateApi, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.StencilTestEnable)) { RecordStencilTestEnable(gd.ExtendedDynamicStateApi, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.LineWidth)) { RecordLineWidth(gd.Api, commandBuffer); } - + if (_dirty.HasFlag(DirtyFlags.RasterDiscard)) { RecordRasterizationDiscard(gd, commandBuffer); } - + + if (_dirty.HasFlag(DirtyFlags.RasterDiscard)) + { + RecordLogicOp(gd, commandBuffer); + } + _dirty = DirtyFlags.None; } @@ -302,7 +322,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly void RecordDepthBias(VulkanRenderer gd, CommandBuffer commandBuffer) { gd.Api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor); - + if (gd.Capabilities.SupportsExtendedDynamicState2) { gd.ExtendedDynamicState2Api.CmdSetDepthBiasEnable(commandBuffer, _depthBiasEnable); @@ -326,7 +346,7 @@ namespace Ryujinx.Graphics.Vulkan } } } - + private readonly void RecordStencil(VulkanRenderer gd, CommandBuffer commandBuffer) { if (_opToo) @@ -336,7 +356,7 @@ namespace Ryujinx.Graphics.Vulkan gd.ExtendedDynamicStateApi.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontfailop, _frontpassop, _frontdepthfailop, _frontcompareop); } - + gd.Api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask); gd.Api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask); gd.Api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceBackBit, _backReference); @@ -344,12 +364,12 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontWriteMask); gd.Api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference); } - + private readonly void RecordStencilTestEnable(ExtExtendedDynamicState api, CommandBuffer commandBuffer) { api.CmdSetStencilTestEnable(commandBuffer, _stencilTestEnable); } - + private void RecordViewport(VulkanRenderer gd, CommandBuffer commandBuffer) { if (ViewportsCount == 0) @@ -369,34 +389,39 @@ namespace Ryujinx.Graphics.Vulkan Viewports.AsSpan()); } } - - private void RecordCullMode(ExtExtendedDynamicState api, CommandBuffer commandBuffer) + + private readonly void RecordCullMode(ExtExtendedDynamicState api, CommandBuffer commandBuffer) { api.CmdSetCullMode(commandBuffer, CullMode); } - - private void RecordFrontFace(ExtExtendedDynamicState api, CommandBuffer commandBuffer) + + private readonly void RecordFrontFace(ExtExtendedDynamicState api, CommandBuffer commandBuffer) { api.CmdSetFrontFace(commandBuffer, FrontFace); } - - private void RecordDepthTestBool(ExtExtendedDynamicState api, CommandBuffer commandBuffer) + + private readonly void RecordDepthTestBool(ExtExtendedDynamicState api, CommandBuffer commandBuffer) { api.CmdSetDepthTestEnable(commandBuffer, _depthtestEnable); api.CmdSetDepthWriteEnable(commandBuffer, _depthwriteEnable); } - - private void RecordDepthTestCompareOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer) + + private readonly void RecordDepthTestCompareOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer) { api.CmdSetDepthCompareOp(commandBuffer, _depthCompareOp); } - - private void RecordRasterizationDiscard(VulkanRenderer gd, CommandBuffer commandBuffer) + + private readonly void RecordRasterizationDiscard(VulkanRenderer gd, CommandBuffer commandBuffer) { gd.ExtendedDynamicState2Api.CmdSetRasterizerDiscardEnable(commandBuffer, _discard); } - - private void RecordLineWidth(Vk api, CommandBuffer commandBuffer) + + private readonly void RecordLogicOp(VulkanRenderer gd, CommandBuffer commandBuffer) + { + gd.ExtendedDynamicState2Api.CmdSetLogicOp(commandBuffer, _logicOp); + } + + private readonly void RecordLineWidth(Vk api, CommandBuffer commandBuffer) { if (!OperatingSystem.IsMacOS()) { diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 94bab99e62..7c6f6a83ff 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -584,19 +584,19 @@ namespace Ryujinx.Graphics.Vulkan } } - // 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, - LogicOpEnable = logicOpEnable, - LogicOp = LogicOp, + LogicOpEnable = LogicOpEnable, AttachmentCount = ColorBlendAttachmentStateCount, PAttachments = pColorBlendAttachmentState, }; - + + if (!(supportsExtDynamicState2 && gd.ExtendedLogicOp)) + { + colorBlendState.LogicOp = LogicOp; + } + PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState; if (!AdvancedBlendSrcPreMultiplied || diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index d6b4c7a87b..6b4ca1d3d2 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -263,7 +263,7 @@ namespace Ryujinx.Graphics.Vulkan return InvalidIndex; } - internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount) + internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount, out bool extendedLogicOp) { if (queueCount > QueuesCount) { @@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.PhysicalDeviceFeatures2, }; - + PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new() { SType = StructureType.PhysicalDeviceVulkan11Features, @@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan { features2.PNext = &supportedFeaturesCustomBorderColor; } - + PhysicalDeviceExtendedDynamicState2FeaturesEXT supportedFeaturesExtExtendedDynamicState2 = new() { SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt, @@ -450,7 +450,7 @@ namespace Ryujinx.Graphics.Vulkan }; pExtendedFeatures = &featuresExtendedDynamicState; - + var featuresExtendedDynamicState2 = new PhysicalDeviceExtendedDynamicState2FeaturesEXT() { SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt, @@ -460,6 +460,8 @@ namespace Ryujinx.Graphics.Vulkan ExtendedDynamicState2PatchControlPoints = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints, }; + extendedLogicOp = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp; + pExtendedFeatures = &featuresExtendedDynamicState2; var featuresVk11 = new PhysicalDeviceVulkan11Features diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index c75c4a39f5..1807711d67 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -99,6 +99,8 @@ namespace Ryujinx.Graphics.Vulkan public bool PreferThreading => true; + public bool ExtendedLogicOp; + public event EventHandler ScreenCaptured; public VulkanRenderer(Vk api, Func surfaceFunc, Func requiredExtensionsFunc, string preferredGpuId) @@ -133,7 +135,7 @@ namespace Ryujinx.Graphics.Vulkan { ExtendedDynamicStateApi = extendedDynamicStateApi; } - + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState2 extendedDynamicState2Api)) { ExtendedDynamicState2Api = extendedDynamicState2Api; @@ -454,7 +456,9 @@ namespace Ryujinx.Graphics.Vulkan var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(Api, _physicalDevice, _surface, out uint maxQueueCount); - _device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount); + _device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount, out bool extendedLogicOp); + + ExtendedLogicOp = extendedLogicOp; if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi)) {