From f800e485c9bcd98e08128db974540e7ba0324128 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 5 Dec 2022 17:14:34 +0100
Subject: [PATCH] Vulkan Implement Dynamic State 2 LogicOp and PatchVertices

---
 src/video_core/engines/maxwell_3d.cpp         |  1 +
 .../renderer_vulkan/fixed_pipeline_state.cpp  | 16 ++++++----
 .../renderer_vulkan/fixed_pipeline_state.h    | 30 ++++++++++---------
 .../renderer_vulkan/vk_graphics_pipeline.cpp  |  9 ++++--
 .../renderer_vulkan/vk_pipeline_cache.cpp     |  2 ++
 .../renderer_vulkan/vk_rasterizer.cpp         | 15 +++++++++-
 .../renderer_vulkan/vk_rasterizer.h           |  1 +
 .../renderer_vulkan/vk_state_tracker.cpp      |  6 ++++
 .../renderer_vulkan/vk_state_tracker.h        |  5 ++++
 .../vulkan_common/vulkan_device.cpp           |  5 +---
 .../vulkan_common/vulkan_wrapper.cpp          |  2 ++
 src/video_core/vulkan_common/vulkan_wrapper.h | 10 +++++++
 12 files changed, 75 insertions(+), 27 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index d44a5cabfc..7f406e1714 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -124,6 +124,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
     regs.gl_front_face = Maxwell3D::Regs::FrontFace::ClockWise;
     regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill;
     regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill;
+    regs.logic_op.op = Maxwell3D::Regs::LogicOp::Op::Clear;
 
     shadow_state = regs;
 }
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index f13ff09c69..b1623b882e 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -55,6 +55,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
     raw1 = 0;
     extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
     extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
+    extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
     extended_dynamic_state_3.Assign(features.has_extended_dynamic_state_3 ? 1 : 0);
     dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
     xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
@@ -66,13 +67,12 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
                                     Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
     ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
     polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
-    patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
     tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
     tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
     tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
                                   Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
     logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
-    logic_op.Assign(PackLogicOp(regs.logic_op.op));
+    patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
     topology.Assign(topology_);
     msaa_mode.Assign(regs.anti_alias_samples_mode);
 
@@ -156,8 +156,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
     if (!extended_dynamic_state) {
         dynamic_state.Refresh(regs);
     }
-    if (!extended_dynamic_state_2) {
-        dynamic_state.Refresh2(regs, topology);
+    if (!extended_dynamic_state_2_extra) {
+        dynamic_state.Refresh2(regs, topology, extended_dynamic_state_2);
     }
     if (!extended_dynamic_state_3) {
         dynamic_state.Refresh3(regs);
@@ -241,7 +241,13 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
     });
 }
 
-void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology_) {
+void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology_, bool base_feautures_supported) {
+    logic_op.Assign(PackLogicOp(regs.logic_op.op));
+
+    if (base_feautures_supported) {
+        return;
+    }
+
     const std::array enabled_lut{
         regs.polygon_offset_point_enable,
         regs.polygon_offset_line_enable,
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index ac2ec3edc0..88680e4482 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -146,6 +146,7 @@ struct FixedPipelineState {
             BitField<3, 1, u32> primitive_restart_enable;
             BitField<4, 1, u32> depth_bias_enable;
             BitField<5, 1, u32> rasterize_enable;
+            BitField<6, 4, u32> logic_op;
         };
         union {
             u32 raw2;
@@ -162,7 +163,7 @@ struct FixedPipelineState {
         std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
 
         void Refresh(const Maxwell& regs);
-        void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology);
+        void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, bool base_feautures_supported);
         void Refresh3(const Maxwell& regs);
 
         Maxwell::ComparisonOp DepthTestFunc() const noexcept {
@@ -182,18 +183,19 @@ struct FixedPipelineState {
         u32 raw1;
         BitField<0, 1, u32> extended_dynamic_state;
         BitField<1, 1, u32> extended_dynamic_state_2;
-        BitField<2, 1, u32> extended_dynamic_state_3;
-        BitField<3, 1, u32> dynamic_vertex_input;
-        BitField<4, 1, u32> xfb_enabled;
-        BitField<5, 1, u32> depth_clamp_disabled;
-        BitField<6, 1, u32> ndc_minus_one_to_one;
-        BitField<7, 2, u32> polygon_mode;
-        BitField<9, 5, u32> patch_control_points_minus_one;
-        BitField<14, 2, u32> tessellation_primitive;
-        BitField<16, 2, u32> tessellation_spacing;
-        BitField<18, 1, u32> tessellation_clockwise;
-        BitField<19, 1, u32> logic_op_enable;
-        BitField<20, 4, u32> logic_op;
+        BitField<2, 1, u32> extended_dynamic_state_2_extra;
+        BitField<3, 1, u32> extended_dynamic_state_3;
+        BitField<4, 1, u32> dynamic_vertex_input;
+        BitField<5, 1, u32> xfb_enabled;
+        BitField<6, 1, u32> depth_clamp_disabled;
+        BitField<7, 1, u32> ndc_minus_one_to_one;
+        BitField<8, 2, u32> polygon_mode;
+        BitField<10, 2, u32> tessellation_primitive;
+        BitField<12, 2, u32> tessellation_spacing;
+        BitField<14, 1, u32> tessellation_clockwise;
+        BitField<15, 1, u32> logic_op_enable;
+        BitField<16, 5, u32> patch_control_points_minus_one;
+
         BitField<24, 4, Maxwell::PrimitiveTopology> topology;
         BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
     };
@@ -246,7 +248,7 @@ struct FixedPipelineState {
             // Exclude dynamic state and attributes
             return offsetof(FixedPipelineState, attributes);
         }
-        if (extended_dynamic_state_2) {
+        if (extended_dynamic_state_2_extra) {
             // Exclude dynamic state
             return offsetof(FixedPipelineState, dynamic_state);
         }
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index d21d5aaf4c..ce82a9c65c 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -628,7 +628,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
         .pNext = nullptr,
         .flags = 0,
         .topology = input_assembly_topology,
-        .primitiveRestartEnable = key.state.dynamic_state.primitive_restart_enable != 0 &&
+        .primitiveRestartEnable = dynamic.primitive_restart_enable != 0 &&
                                   ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
                                     device.IsTopologyListPrimitiveRestartSupported()) ||
                                    SupportsPrimitiveRestart(input_assembly_topology) ||
@@ -786,12 +786,12 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
         .pNext = nullptr,
         .flags = 0,
         .logicOpEnable = key.state.logic_op_enable != 0,
-        .logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()),
+        .logicOp = static_cast<VkLogicOp>(dynamic.logic_op.Value()),
         .attachmentCount = static_cast<u32>(cb_attachments.size()),
         .pAttachments = cb_attachments.data(),
         .blendConstants = {},
     };
-    static_vector<VkDynamicState, 22> dynamic_states{
+    static_vector<VkDynamicState, 23> dynamic_states{
         VK_DYNAMIC_STATE_VIEWPORT,           VK_DYNAMIC_STATE_SCISSOR,
         VK_DYNAMIC_STATE_DEPTH_BIAS,         VK_DYNAMIC_STATE_BLEND_CONSTANTS,
         VK_DYNAMIC_STATE_DEPTH_BOUNDS,       VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
@@ -822,6 +822,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
             };
             dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
         }
+        if (key.state.extended_dynamic_state_2_extra) {
+            dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
+        }
     }
     const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
         .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 1292b6bdff..ee1ad744f9 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -454,6 +454,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
                 dynamic_features.has_extended_dynamic_state ||
             (key.state.extended_dynamic_state_2 != 0) !=
                 dynamic_features.has_extended_dynamic_state_2 ||
+            (key.state.extended_dynamic_state_2_extra != 0) !=
+                dynamic_features.has_extended_dynamic_state_2_extra ||
             (key.state.extended_dynamic_state_3 != 0) !=
                 dynamic_features.has_extended_dynamic_state_3 ||
             (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index f52cebc222..3cf6b796b3 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -680,7 +680,6 @@ void RasterizerVulkan::UpdateDynamicStates() {
     UpdateLineWidth(regs);
     if (device.IsExtExtendedDynamicStateSupported()) {
         UpdateCullMode(regs);
-
         UpdateDepthCompareOp(regs);
         UpdateFrontFace(regs);
         UpdateStencilOp(regs);
@@ -700,6 +699,9 @@ void RasterizerVulkan::UpdateDynamicStates() {
                 UpdateDepthBiasEnable(regs);
             }
         }
+        if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
+            UpdateLogicOp(regs);
+        }
     }
 }
 
@@ -1028,6 +1030,17 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
     }
 }
 
+void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!regs.logic_op.enable) {
+        return;
+    }
+    if (!state_tracker.TouchLogicOp()) {
+        return;
+    }
+    auto op = static_cast<VkLogicOp>(static_cast<u32>(regs.logic_op.op) - 0x1500);
+    scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); });
+}
+
 void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchStencilTestEnable()) {
         return;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index c09415a6ad..67d35eff7a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -145,6 +145,7 @@ private:
     void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs);
 
     void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
 
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 339679ff92..1f8528e3eb 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -48,6 +48,7 @@ Flags MakeInvalidationFlags() {
         PrimitiveRestartEnable,
         RasterizerDiscardEnable,
         DepthBiasEnable,
+        LogicOp,
     };
     Flags flags{};
     for (const int flag : INVALIDATION_FLAGS) {
@@ -162,6 +163,10 @@ void SetupDirtyBlending(Tables& tables) {
     FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending);
 }
 
+void SetupDirtySpecialOps(Tables& tables) {
+    tables[0][OFF(logic_op.op)] = LogicOp;
+}
+
 void SetupDirtyViewportSwizzles(Tables& tables) {
     static constexpr size_t swizzle_offset = 6;
     for (size_t index = 0; index < Regs::NumViewports; ++index) {
@@ -210,6 +215,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
     SetupDirtyViewportSwizzles(tables);
     SetupDirtyVertexAttributes(tables);
     SetupDirtyVertexBindings(tables);
+    SetupDirtySpecialOps(tables);
 }
 
 void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 583bfe1350..6050f5d268 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -49,6 +49,7 @@ enum : u8 {
     RasterizerDiscardEnable,
     DepthBiasEnable,
     StateEnable,
+    LogicOp,
 
     Blending,
     ViewportSwizzles,
@@ -159,6 +160,10 @@ public:
         return Exchange(Dirty::StencilTestEnable, false);
     }
 
+    bool TouchLogicOp() {
+        return Exchange(Dirty::LogicOp, false);
+    }
+
     bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
         const bool has_changed = current_topology != new_topology;
         current_topology = new_topology;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 9a420a2934..7294fcfe37 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -576,8 +576,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
             .pNext = nullptr,
             .extendedDynamicState2 = VK_TRUE,
             .extendedDynamicState2LogicOp = ext_extended_dynamic_state2_extra ? VK_TRUE : VK_FALSE,
-            .extendedDynamicState2PatchControlPoints =
-                ext_extended_dynamic_state2_extra ? VK_TRUE : VK_FALSE,
         };
         SetNext(next, dynamic_state2);
     } else {
@@ -1330,8 +1328,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
             extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
             ext_extended_dynamic_state2 = true;
             ext_extended_dynamic_state2_extra =
-                extended_dynamic_state2.extendedDynamicState2LogicOp &&
-                extended_dynamic_state2.extendedDynamicState2PatchControlPoints;
+                extended_dynamic_state2.extendedDynamicState2LogicOp;
         }
     }
     if (has_ext_extended_dynamic_state3) {
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 4dde325ffb..8745cf80fc 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -126,6 +126,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
     X(vkCmdSetRasterizerDiscardEnableEXT);
     X(vkCmdSetDepthBiasEnableEXT);
     X(vkCmdSetFrontFaceEXT);
+    X(vkCmdSetLogicOpEXT);
+    X(vkCmdSetPatchControlPointsEXT);
     X(vkCmdSetLineWidth);
     X(vkCmdSetPrimitiveTopologyEXT);
     X(vkCmdSetStencilOpEXT);
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 0d3f71460a..c4b7051fca 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -239,6 +239,8 @@ struct DeviceDispatch : InstanceDispatch {
     PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
     PFN_vkCmdSetEvent vkCmdSetEvent{};
     PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
+    PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{};
+    PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT{};
     PFN_vkCmdSetLineWidth vkCmdSetLineWidth{};
     PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
     PFN_vkCmdSetScissor vkCmdSetScissor{};
@@ -1238,6 +1240,14 @@ public:
         dld->vkCmdSetFrontFaceEXT(handle, front_face);
     }
 
+    void SetLogicOpEXT(VkLogicOp logic_op) const noexcept {
+        dld->vkCmdSetLogicOpEXT(handle, logic_op);
+    }
+
+    void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept {
+        dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points);
+    }
+
     void SetLineWidth(float line_width) const noexcept {
         dld->vkCmdSetLineWidth(handle, line_width);
     }