diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index b07f5927b3..fe6730bd13 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -127,6 +127,13 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe depth_bounds_min = static_cast(regs.depth_bounds[0]); depth_bounds_max = static_cast(regs.depth_bounds[1]); + depth_bias = std::bit_cast(regs.depth_bias); + depth_bias_clamp = std::bit_cast(regs.depth_bias_clamp); + slope_scale_depth_bias = std::bit_cast(regs.slope_scale_depth_bias); + + line_width_smooth = std::bit_cast(regs.line_width_smooth); + line_width_aliased = std::bit_cast(regs.line_width_aliased); + line_stipple_factor = regs.line_stipple_params.factor; line_stipple_pattern = regs.line_stipple_params.pattern; diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 030c62a883..efe4e57960 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -236,6 +236,13 @@ struct FixedPipelineState { u32 depth_bounds_min; u32 depth_bounds_max; + u32 depth_bias; + u32 depth_bias_clamp; + u32 slope_scale_depth_bias; + + u32 line_width_smooth; + u32 line_width_aliased; + u32 line_stipple_factor; u32 line_stipple_pattern; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index bc24124563..f3b8ae490d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include @@ -707,24 +708,68 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), .depthBiasEnable = (dynamic.depth_bias_enable != 0 ? VK_TRUE : VK_FALSE), - .depthBiasConstantFactor = 0.0f, - .depthBiasClamp = 0.0f, - .depthBiasSlopeFactor = 0.0f, - .lineWidth = 1.0f, - // TODO(alekpop): Transfer from regs + .depthBiasConstantFactor = std::bit_cast(key.state.depth_bias) / 2.0f, + .depthBiasClamp = std::bit_cast(key.state.depth_bias_clamp), + .depthBiasSlopeFactor = std::bit_cast(key.state.slope_scale_depth_bias), + .lineWidth = key.state.smooth_lines != 0 + ? std::bit_cast(key.state.line_width_smooth) + : std::bit_cast(key.state.line_width_aliased), }; - const bool smooth_lines_supported = - device.IsExtLineRasterizationSupported() && device.SupportsSmoothLines(); - const bool stippled_lines_supported = - device.IsExtLineRasterizationSupported() && device.SupportsStippledRectangularLines(); + const bool line_rasterization_supported = device.IsExtLineRasterizationSupported(); + const bool any_stippled_lines_supported = + line_rasterization_supported && + (device.SupportsStippledRectangularLines() || device.SupportsStippledBresenhamLines() || + device.SupportsStippledSmoothLines()); + const bool line_stipple_dynamic_state_supported = + IsLine(input_assembly_topology) && any_stippled_lines_supported; + const bool supports_rectangular_lines = + line_rasterization_supported && device.SupportsRectangularLines(); + const bool supports_bresenham_lines = + line_rasterization_supported && device.SupportsBresenhamLines(); + const bool supports_smooth_lines = line_rasterization_supported && device.SupportsSmoothLines(); + + VkLineRasterizationModeEXT line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT; + if (line_rasterization_supported) { + if (key.state.smooth_lines != 0) { + if (supports_smooth_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } else if (supports_rectangular_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } else if (supports_bresenham_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } + } else { + if (supports_rectangular_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } else if (supports_bresenham_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } else if (supports_smooth_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } + } + } + + const bool stippled_lines_supported = [&]() { + if (!line_rasterization_supported || !dynamic.line_stipple_enable) { + return false; + } + switch (line_rasterization_mode) { + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT: + return device.SupportsStippledRectangularLines(); + case VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT: + return device.SupportsStippledBresenhamLines(); + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT: + return device.SupportsStippledSmoothLines(); + default: + return false; + } + }(); + VkPipelineRasterizationLineStateCreateInfoEXT line_state{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT, .pNext = nullptr, - .lineRasterizationMode = key.state.smooth_lines != 0 && smooth_lines_supported - ? VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT - : VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT, - .stippledLineEnable = - (dynamic.line_stipple_enable && stippled_lines_supported) ? VK_TRUE : VK_FALSE, + .lineRasterizationMode = line_rasterization_mode, + .stippledLineEnable = stippled_lines_supported ? VK_TRUE : VK_FALSE, .lineStippleFactor = key.state.line_stipple_factor, .lineStipplePattern = static_cast(key.state.line_stipple_pattern), }; @@ -840,12 +885,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{ + static_vector dynamic_states{ VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_LINE_WIDTH, }; + if (line_stipple_dynamic_state_supported) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_EXT); + } if (key.state.extended_dynamic_state) { static constexpr std::array extended{ VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a722edbc6c..5afb526de3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -173,6 +173,55 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, } return params; } + +bool IsLineRasterizationTopology(const Device& device, Maxwell::PrimitiveTopology topology) { + const VkPrimitiveTopology vk_topology = MaxwellToVK::PrimitiveTopology(device, topology); + return vk_topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || + vk_topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; +} + +VkLineRasterizationModeEXT SelectLineRasterizationMode(const Device& device, bool smooth_lines) { + const bool supports_rectangular_lines = device.SupportsRectangularLines(); + const bool supports_bresenham_lines = device.SupportsBresenhamLines(); + const bool supports_smooth_lines = device.SupportsSmoothLines(); + + if (smooth_lines) { + if (supports_smooth_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } + if (supports_rectangular_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } + if (supports_bresenham_lines) { + return VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } + } else { + if (supports_rectangular_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } + if (supports_bresenham_lines) { + return VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } + if (supports_smooth_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } + } + + return VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT; +} + +bool SupportsStippleForMode(const Device& device, VkLineRasterizationModeEXT mode) { + switch (mode) { + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT: + return device.SupportsStippledRectangularLines(); + case VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT: + return device.SupportsStippledBresenhamLines(); + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT: + return device.SupportsStippledSmoothLines(); + default: + return false; + } +} } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -1026,6 +1075,7 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateDepthBounds(regs); UpdateStencilFaces(regs); UpdateLineWidth(regs); + UpdateLineStipple(regs); // EDS1: CullMode, DepthCompare, FrontFace, StencilOp, DepthBoundsTest, DepthTest, DepthWrite, StencilTest if (device.IsExtExtendedDynamicStateSupported()) { @@ -1370,6 +1420,33 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); } +void RasterizerVulkan::UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchLineStipple()) { + return; + } + if (!device.IsExtLineRasterizationSupported()) { + return; + } + + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (!IsLineRasterizationTopology(device, topology)) { + return; + } + + const VkLineRasterizationModeEXT mode = + SelectLineRasterizationMode(device, regs.line_anti_alias_enable != 0); + + if (regs.line_stipple_enable == 0 || !SupportsStippleForMode(device, mode)) { + return; + } + + scheduler.Record( + [factor = regs.line_stipple_params.factor, + pattern = static_cast(regs.line_stipple_params.pattern)](vk::CommandBuffer cmdbuf) { + cmdbuf.SetLineStippleEXT(factor, pattern); + }); +} + void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchCullMode()) { return; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index c8f50cd99b..bdea6510c0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -168,6 +168,7 @@ private: void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBoundsTestEnable(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 c17bc5900b..19133d3255 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -40,6 +40,7 @@ Flags MakeInvalidationFlags() { StencilWriteMask, StencilCompare, LineWidth, + LineStipple, CullMode, DepthBoundsEnable, DepthTestEnable, @@ -119,6 +120,13 @@ void SetupDirtyStencilProperties(Tables& tables) { void SetupDirtyLineWidth(Tables& tables) { tables[0][OFF(line_width_smooth)] = LineWidth; tables[0][OFF(line_width_aliased)] = LineWidth; + tables[0][OFF(line_anti_alias_enable)] = LineWidth; +} + +void SetupDirtyLineStipple(Tables& tables) { + tables[0][OFF(line_stipple_enable)] = LineStipple; + FillBlock(tables[0], OFF(line_stipple_params), NUM(line_stipple_params), LineStipple); + tables[1][OFF(line_anti_alias_enable)] = LineStipple; } void SetupDirtyCullMode(Tables& tables) { @@ -226,6 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { SetupDirtyDepthBounds(tables); SetupDirtyStencilProperties(tables); SetupDirtyLineWidth(tables); + SetupDirtyLineStipple(tables); SetupDirtyCullMode(tables); SetupDirtyStateEnable(tables); SetupDirtyDepthCompareOp(tables); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 8f8db9f828..7282bc1f44 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -42,6 +42,7 @@ enum : u8 { StencilWriteMask, StencilCompare, LineWidth, + LineStipple, CullMode, DepthBoundsEnable, @@ -177,6 +178,10 @@ public: return Exchange(Dirty::LineWidth, false); } + bool TouchLineStipple() const { + return Exchange(Dirty::LineStipple, false); + } + bool TouchCullMode() { return Exchange(Dirty::CullMode, false); } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 5e30773bf1..1ea17f82d8 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1229,6 +1229,14 @@ void Device::RemoveUnsuitableExtensions() { features.provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); + // VK_EXT_line_rasterization + extensions.line_rasterization = features.line_rasterization.rectangularLines || + features.line_rasterization.bresenhamLines || + features.line_rasterization.smoothLines; + RemoveExtensionFeatureIfUnsuitable(extensions.line_rasterization, + features.line_rasterization, + VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); + // VK_EXT_conditional_rendering extensions.conditional_rendering = features.conditional_rendering.conditionalRendering; RemoveExtensionFeatureIfUnsuitable(extensions.conditional_rendering, diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 8050390dc7..884f8c3b12 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -435,7 +435,7 @@ public: return extensions.viewport_array2; } - /// Returns true if the device supporst VK_EXT_DESCRIPTOR_INDEXING. + /// Returns true if the device supporst VK_EXT_descriptor_indexing. bool isExtDescriptorIndexingSupported() const { return extensions.descriptor_indexing; } @@ -631,19 +631,32 @@ public: } bool SupportsRectangularLines() const { - return features.line_rasterization.rectangularLines != VK_FALSE; + return features.line_rasterization.rectangularLines; + } + + bool SupportsBresenhamLines() const { + return features.line_rasterization.bresenhamLines; } bool SupportsSmoothLines() const { - return features.line_rasterization.smoothLines != VK_FALSE; + return features.line_rasterization.smoothLines; } bool SupportsStippledRectangularLines() const { - return features.line_rasterization.stippledRectangularLines != VK_FALSE; + return features.line_rasterization.stippledRectangularLines; } + bool SupportsStippledBresenhamLines() const { + return features.line_rasterization.stippledBresenhamLines; + } + + bool SupportsStippledSmoothLines() const { + return features.line_rasterization.stippledSmoothLines; + } + + /// Returns true if the device supports AlphaToOne. bool SupportsAlphaToOne() const { - return features.features.alphaToOne != VK_FALSE; + return features.features.alphaToOne; } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index fce0103eae..a5b3a1c917 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -152,6 +152,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetFrontFaceEXT); X(vkCmdSetLogicOpEXT); X(vkCmdSetPatchControlPointsEXT); + X(vkCmdSetLineStippleEXT); X(vkCmdSetLineWidth); X(vkCmdSetPrimitiveTopologyEXT); X(vkCmdSetStencilOpEXT);