Explorar o código

Vulkan: Implement Dynamic States 2

Fernando Sahmkow %!s(int64=3) %!d(string=hai) anos
pai
achega
c897c55e3c

+ 30 - 16
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp

@@ -48,23 +48,16 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
 }
 } // Anonymous namespace
 
-void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
-                                 bool has_extended_dynamic_state, bool has_dynamic_vertex_input) {
+void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
     const Maxwell& regs = maxwell3d.regs;
     const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
-    const std::array enabled_lut{
-        regs.polygon_offset_point_enable,
-        regs.polygon_offset_line_enable,
-        regs.polygon_offset_fill_enable,
-    };
-    const u32 topology_index = static_cast<u32>(topology_);
 
     raw1 = 0;
-    extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0);
-    dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 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_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);
-    primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
-    depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
     depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
                                     Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
                                 regs.viewport_clip_control.geometry_clip ==
@@ -84,7 +77,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
     msaa_mode.Assign(regs.anti_alias_samples_mode);
 
     raw2 = 0;
-    rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
+
     const auto test_func =
         regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
     alpha_test_func.Assign(PackComparisonOp(test_func));
@@ -106,7 +99,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
     point_size = Common::BitCast<u32>(regs.point_size);
 
     if (maxwell3d.dirty.flags[Dirty::VertexInput]) {
-        if (has_dynamic_vertex_input) {
+        if (features.has_dynamic_vertex_input) {
             // Dirty flag will be reset by the command buffer update
             static constexpr std::array LUT{
                 0u, // Invalid
@@ -158,9 +151,17 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
             return static_cast<u16>(viewport.swizzle.raw);
         });
     }
+    dynamic_state.raw1 = 0;
+    dynamic_state.raw2 = 0;
     if (!extended_dynamic_state) {
         dynamic_state.Refresh(regs);
     }
+    if (!extended_dynamic_state_2) {
+        dynamic_state.Refresh2(regs, topology);
+    }
+    if (!extended_dynamic_state_3) {
+        dynamic_state.Refresh3(regs);
+    }
     if (xfb_enabled) {
         RefreshXfbState(xfb_state, regs);
     }
@@ -212,8 +213,6 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
         packed_front_face = 1 - packed_front_face;
     }
 
-    raw1 = 0;
-    raw2 = 0;
     front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail));
     front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail));
     front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass));
@@ -242,6 +241,21 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
     });
 }
 
+void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology_) {
+    const std::array enabled_lut{
+        regs.polygon_offset_point_enable,
+        regs.polygon_offset_line_enable,
+        regs.polygon_offset_fill_enable,
+    };
+    const u32 topology_index = static_cast<u32>(topology_);
+
+    rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
+    primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
+    depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
+}
+
+void FixedPipelineState::DynamicState::Refresh3(const Maxwell&) {}
+
 size_t FixedPipelineState::Hash() const noexcept {
     const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
     return static_cast<size_t>(hash);

+ 29 - 14
src/video_core/renderer_vulkan/fixed_pipeline_state.h

@@ -17,6 +17,14 @@ namespace Vulkan {
 
 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 
+struct DynamicFeatures {
+    bool has_extended_dynamic_state;
+    bool has_extended_dynamic_state_2;
+    bool has_extended_dynamic_state_2_extra;
+    bool has_extended_dynamic_state_3;
+    bool has_dynamic_vertex_input;
+};
+
 struct FixedPipelineState {
     static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
     static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
@@ -133,6 +141,14 @@ struct FixedPipelineState {
     struct DynamicState {
         union {
             u32 raw1;
+            BitField<0, 2, u32> cull_face;
+            BitField<2, 1, u32> cull_enable;
+            BitField<3, 1, u32> primitive_restart_enable;
+            BitField<4, 1, u32> depth_bias_enable;
+            BitField<5, 1, u32> rasterize_enable;
+        };
+        union {
+            u32 raw2;
             StencilFace<0> front;
             StencilFace<12> back;
             BitField<24, 1, u32> stencil_enable;
@@ -142,15 +158,12 @@ struct FixedPipelineState {
             BitField<28, 1, u32> front_face;
             BitField<29, 3, u32> depth_test_func;
         };
-        union {
-            u32 raw2;
-            BitField<0, 2, u32> cull_face;
-            BitField<2, 1, u32> cull_enable;
-        };
         // Vertex stride is a 12 bits value, we have 4 bits to spare per element
         std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
 
         void Refresh(const Maxwell& regs);
+        void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology);
+        void Refresh3(const Maxwell& regs);
 
         Maxwell::ComparisonOp DepthTestFunc() const noexcept {
             return UnpackComparisonOp(depth_test_func);
@@ -168,10 +181,10 @@ struct FixedPipelineState {
     union {
         u32 raw1;
         BitField<0, 1, u32> extended_dynamic_state;
-        BitField<1, 1, u32> dynamic_vertex_input;
-        BitField<2, 1, u32> xfb_enabled;
-        BitField<3, 1, u32> primitive_restart_enable;
-        BitField<4, 1, u32> depth_bias_enable;
+        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;
@@ -186,7 +199,6 @@ struct FixedPipelineState {
     };
     union {
         u32 raw2;
-        BitField<0, 1, u32> rasterize_enable;
         BitField<1, 3, u32> alpha_test_func;
         BitField<4, 1, u32> early_z;
         BitField<5, 1, u32> depth_enabled;
@@ -215,8 +227,7 @@ struct FixedPipelineState {
     DynamicState dynamic_state;
     VideoCommon::TransformFeedbackState xfb_state;
 
-    void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state,
-                 bool has_dynamic_vertex_input);
+    void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features);
 
     size_t Hash() const noexcept;
 
@@ -231,14 +242,18 @@ struct FixedPipelineState {
             // When transform feedback is enabled, use the whole struct
             return sizeof(*this);
         }
-        if (dynamic_vertex_input) {
+        if (dynamic_vertex_input && extended_dynamic_state_2) {
             // Exclude dynamic state and attributes
             return offsetof(FixedPipelineState, attributes);
         }
-        if (extended_dynamic_state) {
+        if (extended_dynamic_state_2) {
             // Exclude dynamic state
             return offsetof(FixedPipelineState, dynamic_state);
         }
+        if (extended_dynamic_state) {
+            // Exclude dynamic state
+            return offsetof(FixedPipelineState, dynamic_state.raw2);
+        }
         // Default
         return offsetof(FixedPipelineState, xfb_state);
     }

+ 15 - 4
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp

@@ -525,6 +525,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
     if (!key.state.extended_dynamic_state) {
         dynamic = key.state.dynamic_state;
     }
+    if (!key.state.extended_dynamic_state_2) {
+        dynamic.raw2 = key.state.dynamic_state.raw2;
+    }
     static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
     static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
     static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
@@ -625,7 +628,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
         .pNext = nullptr,
         .flags = 0,
         .topology = input_assembly_topology,
-        .primitiveRestartEnable = key.state.primitive_restart_enable != 0 &&
+        .primitiveRestartEnable = key.state.dynamic_state.primitive_restart_enable != 0 &&
                                   ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
                                     device.IsTopologyListPrimitiveRestartSupported()) ||
                                    SupportsPrimitiveRestart(input_assembly_topology) ||
@@ -674,13 +677,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
         .depthClampEnable =
             static_cast<VkBool32>(key.state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
         .rasterizerDiscardEnable =
-            static_cast<VkBool32>(key.state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
+            static_cast<VkBool32>(dynamic.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
         .polygonMode =
             MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode)),
         .cullMode = static_cast<VkCullModeFlags>(
             dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
         .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
-        .depthBiasEnable = key.state.depth_bias_enable,
+        .depthBiasEnable = (dynamic.depth_bias_enable == 0 ? VK_TRUE : VK_FALSE),
         .depthBiasConstantFactor = 0.0f,
         .depthBiasClamp = 0.0f,
         .depthBiasSlopeFactor = 0.0f,
@@ -788,7 +791,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
         .pAttachments = cb_attachments.data(),
         .blendConstants = {},
     };
-    static_vector<VkDynamicState, 19> dynamic_states{
+    static_vector<VkDynamicState, 22> 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,
@@ -811,6 +814,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
             dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
         }
         dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
+        if (key.state.extended_dynamic_state_2) {
+            static constexpr std::array extended2{
+                VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
+                VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
+                VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
+            };
+            dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
+        }
     }
     const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
         .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,

+ 16 - 6
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@@ -351,6 +351,14 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
         LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
                     device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
     }
+
+    dynamic_features = DynamicFeatures{
+        .has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
+        .has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
+        .has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
+        .has_extended_dynamic_state_3 = device.IsExtExtendedDynamicState3Supported(),
+        .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
+    };
 }
 
 PipelineCache::~PipelineCache() = default;
@@ -362,8 +370,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
         current_pipeline = nullptr;
         return nullptr;
     }
-    graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(),
-                               device.IsExtVertexInputDynamicStateSupported());
+    graphics_key.state.Refresh(*maxwell3d, dynamic_features);
 
     if (current_pipeline) {
         GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
@@ -439,14 +446,17 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
         });
         ++state.total;
     }};
-    const bool extended_dynamic_state = device.IsExtExtendedDynamicStateSupported();
-    const bool dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported();
     const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
         GraphicsPipelineCacheKey key;
         file.read(reinterpret_cast<char*>(&key), sizeof(key));
 
-        if ((key.state.extended_dynamic_state != 0) != extended_dynamic_state ||
-            (key.state.dynamic_vertex_input != 0) != dynamic_vertex_input) {
+        if ((key.state.extended_dynamic_state != 0) !=
+                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_3 != 0) !=
+                dynamic_features.has_extended_dynamic_state_3 ||
+            (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
             return;
         }
         workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable {

+ 1 - 0
src/video_core/renderer_vulkan/vk_pipeline_cache.h

@@ -160,6 +160,7 @@ private:
 
     Common::ThreadWorker workers;
     Common::ThreadWorker serialization_thread;
+    DynamicFeatures dynamic_features;
 };
 
 } // namespace Vulkan

+ 66 - 4
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@@ -680,16 +680,26 @@ void RasterizerVulkan::UpdateDynamicStates() {
     UpdateLineWidth(regs);
     if (device.IsExtExtendedDynamicStateSupported()) {
         UpdateCullMode(regs);
-        UpdateDepthBoundsTestEnable(regs);
-        UpdateDepthTestEnable(regs);
-        UpdateDepthWriteEnable(regs);
+
         UpdateDepthCompareOp(regs);
         UpdateFrontFace(regs);
         UpdateStencilOp(regs);
-        UpdateStencilTestEnable(regs);
+
         if (device.IsExtVertexInputDynamicStateSupported()) {
             UpdateVertexInput(regs);
         }
+
+        if (state_tracker.TouchStateEnable()) {
+            UpdateDepthBoundsTestEnable(regs);
+            UpdateDepthTestEnable(regs);
+            UpdateDepthWriteEnable(regs);
+            UpdateStencilTestEnable(regs);
+            if (device.IsExtExtendedDynamicState2Supported()) {
+                UpdatePrimitiveRestartEnable(regs);
+                UpdateRasterizerDiscardEnable(regs);
+                UpdateDepthBiasEnable(regs);
+            }
+        }
     }
 }
 
@@ -909,6 +919,58 @@ void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& r
     });
 }
 
+void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchPrimitiveRestartEnable()) {
+        return;
+    }
+    scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetPrimitiveRestartEnableEXT(enable);
+    });
+}
+
+void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchRasterizerDiscardEnable()) {
+        return;
+    }
+    scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0);
+    });
+}
+
+void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchDepthBiasEnable()) {
+        return;
+    }
+    constexpr size_t POINT = 0;
+    constexpr size_t LINE = 1;
+    constexpr size_t POLYGON = 2;
+    constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
+        POINT,   // Points
+        LINE,    // Lines
+        LINE,    // LineLoop
+        LINE,    // LineStrip
+        POLYGON, // Triangles
+        POLYGON, // TriangleStrip
+        POLYGON, // TriangleFan
+        POLYGON, // Quads
+        POLYGON, // QuadStrip
+        POLYGON, // Polygon
+        LINE,    // LinesAdjacency
+        LINE,    // LineStripAdjacency
+        POLYGON, // TrianglesAdjacency
+        POLYGON, // TriangleStripAdjacency
+        POLYGON, // Patches
+    };
+    const std::array enabled_lut{
+        regs.polygon_offset_point_enable,
+        regs.polygon_offset_line_enable,
+        regs.polygon_offset_fill_enable,
+    };
+    const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
+    const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
+    scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable); });
+}
+
 void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchDepthCompareOp()) {
         return;

+ 3 - 0
src/video_core/renderer_vulkan/vk_rasterizer.h

@@ -139,6 +139,9 @@ private:
     void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);

+ 36 - 22
src/video_core/renderer_vulkan/vk_state_tracker.cpp

@@ -27,10 +27,27 @@ using Flags = Maxwell3D::DirtyState::Flags;
 
 Flags MakeInvalidationFlags() {
     static constexpr int INVALIDATION_FLAGS[]{
-        Viewports,         Scissors,       DepthBias, BlendConstants,    DepthBounds,
-        StencilProperties, LineWidth,      CullMode,  DepthBoundsEnable, DepthTestEnable,
-        DepthWriteEnable,  DepthCompareOp, FrontFace, StencilOp,         StencilTestEnable,
-        VertexBuffers,     VertexInput,
+        Viewports,
+        Scissors,
+        DepthBias,
+        BlendConstants,
+        DepthBounds,
+        StencilProperties,
+        LineWidth,
+        CullMode,
+        DepthBoundsEnable,
+        DepthTestEnable,
+        DepthWriteEnable,
+        DepthCompareOp,
+        FrontFace,
+        StencilOp,
+        StencilTestEnable,
+        VertexBuffers,
+        VertexInput,
+        StateEnable,
+        PrimitiveRestartEnable,
+        RasterizerDiscardEnable,
+        DepthBiasEnable,
     };
     Flags flags{};
     for (const int flag : INVALIDATION_FLAGS) {
@@ -96,16 +113,20 @@ void SetupDirtyCullMode(Tables& tables) {
     table[OFF(gl_cull_test_enabled)] = CullMode;
 }
 
-void SetupDirtyDepthBoundsEnable(Tables& tables) {
-    tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
-}
-
-void SetupDirtyDepthTestEnable(Tables& tables) {
-    tables[0][OFF(depth_test_enable)] = DepthTestEnable;
-}
-
-void SetupDirtyDepthWriteEnable(Tables& tables) {
-    tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
+void SetupDirtyStateEnable(Tables& tables) {
+    const auto setup = [&](size_t position, u8 flag) {
+        tables[0][position] = flag;
+        tables[1][position] = StateEnable;
+    };
+    setup(OFF(depth_bounds_enable), DepthBoundsEnable);
+    setup(OFF(depth_test_enable), DepthTestEnable);
+    setup(OFF(depth_write_enabled), DepthWriteEnable);
+    setup(OFF(stencil_enable), StencilTestEnable);
+    setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable);
+    setup(OFF(rasterize_enable), RasterizerDiscardEnable);
+    setup(OFF(polygon_offset_point_enable), DepthBiasEnable);
+    setup(OFF(polygon_offset_line_enable), DepthBiasEnable);
+    setup(OFF(polygon_offset_fill_enable), DepthBiasEnable);
 }
 
 void SetupDirtyDepthCompareOp(Tables& tables) {
@@ -133,10 +154,6 @@ void SetupDirtyStencilOp(Tables& tables) {
     tables[1][OFF(stencil_two_side_enable)] = StencilOp;
 }
 
-void SetupDirtyStencilTestEnable(Tables& tables) {
-    tables[0][OFF(stencil_enable)] = StencilTestEnable;
-}
-
 void SetupDirtyBlending(Tables& tables) {
     tables[0][OFF(color_mask_common)] = Blending;
     tables[0][OFF(blend_per_target_enabled)] = Blending;
@@ -185,13 +202,10 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
     SetupDirtyStencilProperties(tables);
     SetupDirtyLineWidth(tables);
     SetupDirtyCullMode(tables);
-    SetupDirtyDepthBoundsEnable(tables);
-    SetupDirtyDepthTestEnable(tables);
-    SetupDirtyDepthWriteEnable(tables);
+    SetupDirtyStateEnable(tables);
     SetupDirtyDepthCompareOp(tables);
     SetupDirtyFrontFace(tables);
     SetupDirtyStencilOp(tables);
-    SetupDirtyStencilTestEnable(tables);
     SetupDirtyBlending(tables);
     SetupDirtyViewportSwizzles(tables);
     SetupDirtyVertexAttributes(tables);

+ 20 - 0
src/video_core/renderer_vulkan/vk_state_tracker.h

@@ -45,6 +45,10 @@ enum : u8 {
     FrontFace,
     StencilOp,
     StencilTestEnable,
+    PrimitiveRestartEnable,
+    RasterizerDiscardEnable,
+    DepthBiasEnable,
+    StateEnable,
 
     Blending,
     ViewportSwizzles,
@@ -111,6 +115,10 @@ public:
         return Exchange(Dirty::CullMode, false);
     }
 
+    bool TouchStateEnable() {
+        return Exchange(Dirty::StateEnable, false);
+    }
+
     bool TouchDepthBoundsTestEnable() {
         return Exchange(Dirty::DepthBoundsEnable, false);
     }
@@ -123,6 +131,18 @@ public:
         return Exchange(Dirty::DepthWriteEnable, false);
     }
 
+    bool TouchPrimitiveRestartEnable() {
+        return Exchange(Dirty::PrimitiveRestartEnable, false);
+    }
+
+    bool TouchRasterizerDiscardEnable() {
+        return Exchange(Dirty::RasterizerDiscardEnable, false);
+    }
+
+    bool TouchDepthBiasEnable() {
+        return Exchange(Dirty::DepthBiasEnable, false);
+    }
+
     bool TouchDepthCompareOp() {
         return Exchange(Dirty::DepthCompareOp, false);
     }

+ 64 - 0
src/video_core/vulkan_common/vulkan_device.cpp

@@ -569,6 +569,34 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
         LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
     }
 
+    VkPhysicalDeviceExtendedDynamicState2FeaturesEXT dynamic_state2;
+    if (ext_extended_dynamic_state2) {
+        dynamic_state2 = {
+            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT,
+            .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 {
+        LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 2");
+    }
+
+    VkPhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state3;
+    if (ext_extended_dynamic_state3) {
+        dynamic_state3 = {
+            .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
+            .pNext = nullptr,
+            .extendedDynamicState3ColorBlendEnable = VK_TRUE,
+            .extendedDynamicState3ColorBlendEquation = VK_TRUE,
+        };
+        SetNext(next, dynamic_state3);
+    } else {
+        LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 3");
+    }
+
     VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
     if (ext_line_rasterization) {
         line_raster = {
@@ -1091,6 +1119,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
     bool has_ext_transform_feedback{};
     bool has_ext_custom_border_color{};
     bool has_ext_extended_dynamic_state{};
+    bool has_ext_extended_dynamic_state2{};
+    bool has_ext_extended_dynamic_state3{};
     bool has_ext_shader_atomic_int64{};
     bool has_ext_provoking_vertex{};
     bool has_ext_vertex_input_dynamic_state{};
@@ -1135,6 +1165,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
         test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
         test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
         test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
+        test(has_ext_extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME,
+             false);
+        test(has_ext_extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
+             false);
         test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true);
         test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false);
         test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
@@ -1284,6 +1318,36 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
             ext_extended_dynamic_state = true;
         }
     }
+    if (has_ext_extended_dynamic_state2) {
+        VkPhysicalDeviceExtendedDynamicState2FeaturesEXT extended_dynamic_state2;
+        extended_dynamic_state2.sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT;
+        extended_dynamic_state2.pNext = nullptr;
+        features.pNext = &extended_dynamic_state2;
+        physical.GetFeatures2(features);
+
+        if (extended_dynamic_state2.extendedDynamicState2) {
+            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;
+        }
+    }
+    if (has_ext_extended_dynamic_state3) {
+        VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extended_dynamic_state3;
+        extended_dynamic_state3.sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT;
+        extended_dynamic_state3.pNext = nullptr;
+        features.pNext = &extended_dynamic_state3;
+        physical.GetFeatures2(features);
+
+        if (extended_dynamic_state3.extendedDynamicState3ColorBlendEnable &&
+            extended_dynamic_state3.extendedDynamicState3ColorBlendEquation) {
+            extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
+            ext_extended_dynamic_state3 = true;
+        }
+    }
     if (has_ext_line_rasterization) {
         VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
         line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;

+ 17 - 0
src/video_core/vulkan_common/vulkan_device.h

@@ -286,6 +286,20 @@ public:
         return ext_extended_dynamic_state;
     }
 
+    /// Returns true if the device supports VK_EXT_extended_dynamic_state2.
+    bool IsExtExtendedDynamicState2Supported() const {
+        return ext_extended_dynamic_state2;
+    }
+
+    bool IsExtExtendedDynamicState2ExtrasSupported() const {
+        return ext_extended_dynamic_state2_extra;
+    }
+
+    /// Returns true if the device supports VK_EXT_extended_dynamic_state3.
+    bool IsExtExtendedDynamicState3Supported() const {
+        return ext_extended_dynamic_state3;
+    }
+
     /// Returns true if the device supports VK_EXT_line_rasterization.
     bool IsExtLineRasterizationSupported() const {
         return ext_line_rasterization;
@@ -468,6 +482,9 @@ private:
     bool ext_transform_feedback{};          ///< Support for VK_EXT_transform_feedback.
     bool ext_custom_border_color{};         ///< Support for VK_EXT_custom_border_color.
     bool ext_extended_dynamic_state{};      ///< Support for VK_EXT_extended_dynamic_state.
+    bool ext_extended_dynamic_state2{};      ///< Support for VK_EXT_extended_dynamic_state2.
+    bool ext_extended_dynamic_state2_extra{}; ///< Support for VK_EXT_extended_dynamic_state2.
+    bool ext_extended_dynamic_state3{};      ///< Support for VK_EXT_extended_dynamic_state3.
     bool ext_line_rasterization{};          ///< Support for VK_EXT_line_rasterization.
     bool ext_vertex_input_dynamic_state{};  ///< Support for VK_EXT_vertex_input_dynamic_state.
     bool ext_shader_stencil_export{};       ///< Support for VK_EXT_shader_stencil_export.

+ 3 - 0
src/video_core/vulkan_common/vulkan_wrapper.cpp

@@ -122,6 +122,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
     X(vkCmdSetDepthCompareOpEXT);
     X(vkCmdSetDepthTestEnableEXT);
     X(vkCmdSetDepthWriteEnableEXT);
+    X(vkCmdSetPrimitiveRestartEnableEXT);
+    X(vkCmdSetRasterizerDiscardEnableEXT);
+    X(vkCmdSetDepthBiasEnableEXT);
     X(vkCmdSetFrontFaceEXT);
     X(vkCmdSetLineWidth);
     X(vkCmdSetPrimitiveTopologyEXT);

+ 15 - 0
src/video_core/vulkan_common/vulkan_wrapper.h

@@ -234,6 +234,9 @@ struct DeviceDispatch : InstanceDispatch {
     PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{};
     PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{};
     PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
+    PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{};
+    PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{};
+    PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
     PFN_vkCmdSetEvent vkCmdSetEvent{};
     PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
     PFN_vkCmdSetLineWidth vkCmdSetLineWidth{};
@@ -1219,6 +1222,18 @@ public:
         dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
     }
 
+    void SetPrimitiveRestartEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
+    void SetRasterizerDiscardEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
+    void SetDepthBiasEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
     void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
         dld->vkCmdSetFrontFaceEXT(handle, front_face);
     }