[vulkan] Rework line rasterization handle

This commit is contained in:
CamilleLaVey 2026-02-15 20:32:00 -04:00
parent 7fdbc33aaf
commit 57aab352b4
10 changed files with 196 additions and 20 deletions

View File

@ -127,6 +127,13 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
depth_bounds_min = static_cast<u32>(regs.depth_bounds[0]);
depth_bounds_max = static_cast<u32>(regs.depth_bounds[1]);
depth_bias = std::bit_cast<u32>(regs.depth_bias);
depth_bias_clamp = std::bit_cast<u32>(regs.depth_bias_clamp);
slope_scale_depth_bias = std::bit_cast<u32>(regs.slope_scale_depth_bias);
line_width_smooth = std::bit_cast<u32>(regs.line_width_smooth);
line_width_aliased = std::bit_cast<u32>(regs.line_width_aliased);
line_stipple_factor = regs.line_stipple_params.factor;
line_stipple_pattern = regs.line_stipple_params.pattern;

View File

@ -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;

View File

@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <bit>
#include <iostream>
#include <span>
@ -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<float>(key.state.depth_bias) / 2.0f,
.depthBiasClamp = std::bit_cast<float>(key.state.depth_bias_clamp),
.depthBiasSlopeFactor = std::bit_cast<float>(key.state.slope_scale_depth_bias),
.lineWidth = key.state.smooth_lines != 0
? std::bit_cast<float>(key.state.line_width_smooth)
: std::bit_cast<float>(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<uint16_t>(key.state.line_stipple_pattern),
};
@ -840,12 +885,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(),
.blendConstants = {}
};
static_vector<VkDynamicState, 34> dynamic_states{
static_vector<VkDynamicState, 35> 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,

View File

@ -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<u16>(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;

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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);