layers: Validate VK_EXT_line_rasterization
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp
index 5d8c2ea..420feb3 100644
--- a/layers/core_validation.cpp
+++ b/layers/core_validation.cpp
@@ -774,6 +774,15 @@
         result |= ValidateStatus(pCB, CBSTATUS_INDEX_BUFFER_BOUND, VK_DEBUG_REPORT_ERROR_BIT_EXT,
                                  "Index buffer object not bound to this command buffer when Indexed Draw attempted", msg_code);
     }
+    if (pPipe->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_LINE_LIST ||
+        pPipe->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) {
+        const auto *line_state =
+            lvl_find_in_chain<VkPipelineRasterizationLineStateCreateInfoEXT>(pPipe->graphicsPipelineCI.pRasterizationState->pNext);
+        if (line_state && line_state->stippledLineEnable) {
+            result |= ValidateStatus(pCB, CBSTATUS_LINE_STIPPLE_SET, VK_DEBUG_REPORT_ERROR_BIT_EXT,
+                                     "Dynamic line stipple state not set for this command buffer", msg_code);
+        }
+    }
 
     return result;
 }
@@ -2247,6 +2256,9 @@
                 case VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV:
                     flags &= ~CBSTATUS_SHADING_RATE_PALETTE_SET;
                     break;
+                case VK_DYNAMIC_STATE_LINE_STIPPLE_EXT:
+                    flags &= ~CBSTATUS_LINE_STIPPLE_SET;
+                    break;
                 default:
                     break;
             }
@@ -7229,6 +7241,28 @@
     cb_state->status |= CBSTATUS_LINE_WIDTH_SET;
 }
 
+bool CoreChecks::PreCallValidateCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor,
+                                                     uint16_t lineStipplePattern) {
+    const CMD_BUFFER_STATE *cb_state = GetCBState(commandBuffer);
+    assert(cb_state);
+    bool skip = ValidateCmdQueueFlags(cb_state, "vkCmdSetLineStippleEXT()", VK_QUEUE_GRAPHICS_BIT,
+                                      "VUID-vkCmdSetLineStippleEXT-commandBuffer-cmdpool");
+    skip |= ValidateCmd(cb_state, CMD_SETLINESTIPPLEEXT, "vkCmdSetLineStippleEXT()");
+
+    if (cb_state->static_status & CBSTATUS_LINE_STIPPLE_SET) {
+        skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT,
+                        HandleToUint64(commandBuffer), "VUID-vkCmdSetLineStippleEXT-None-02775",
+                        "vkCmdSetLineStippleEXT called but pipeline was created without VK_DYNAMIC_STATE_LINE_STIPPLE_EXT flag.");
+    }
+    return skip;
+}
+
+void ValidationStateTracker::PreCallRecordCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor,
+                                                               uint16_t lineStipplePattern) {
+    CMD_BUFFER_STATE *cb_state = GetCBState(commandBuffer);
+    cb_state->status |= CBSTATUS_LINE_STIPPLE_SET;
+}
+
 bool CoreChecks::PreCallValidateCmdSetDepthBias(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp,
                                                 float depthBiasSlopeFactor) {
     const CMD_BUFFER_STATE *cb_state = GetCBState(commandBuffer);
diff --git a/layers/core_validation.h b/layers/core_validation.h
index 5399282..0e2eadf 100644
--- a/layers/core_validation.h
+++ b/layers/core_validation.h
@@ -685,6 +685,7 @@
     void PreCallRecordCmdSetExclusiveScissorNV(VkCommandBuffer commandBuffer, uint32_t firstExclusiveScissor,
                                                uint32_t exclusiveScissorCount, const VkRect2D* pExclusiveScissors);
     void PreCallRecordCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth);
+    void PreCallRecordCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern);
     void PreCallRecordCmdSetScissor(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount,
                                     const VkRect2D* pScissors);
     void PreCallRecordCmdSetStencilCompareMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask);
@@ -1722,6 +1723,8 @@
     bool PreCallValidateDestroyAccelerationStructureNV(VkDevice device, VkAccelerationStructureNV accelerationStructure,
                                                        const VkAllocationCallbacks* pAllocator);
     bool PreCallValidateCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth);
+    bool PreCallValidateCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor,
+                                             uint16_t lineStipplePattern);
     bool PreCallValidateCmdSetDepthBias(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp,
                                         float depthBiasSlopeFactor);
     bool PreCallValidateCmdSetBlendConstants(VkCommandBuffer commandBuffer, const float blendConstants[4]);
diff --git a/layers/core_validation_types.h b/layers/core_validation_types.h
index 266a0af..72dbdf2 100644
--- a/layers/core_validation_types.h
+++ b/layers/core_validation_types.h
@@ -972,7 +972,8 @@
     CBSTATUS_INDEX_BUFFER_BOUND     = 0x00000200,   // Index buffer has been set
     CBSTATUS_EXCLUSIVE_SCISSOR_SET  = 0x00000400,
     CBSTATUS_SHADING_RATE_PALETTE_SET = 0x00000800,
-    CBSTATUS_ALL_STATE_SET          = 0x00000DFF,   // All state set (intentionally exclude index buffer)
+    CBSTATUS_LINE_STIPPLE_SET       = 0x00001000,
+    CBSTATUS_ALL_STATE_SET          = 0x00001DFF,   // All state set (intentionally exclude index buffer)
     // clang-format on
 };
 
diff --git a/layers/generated/parameter_validation.cpp b/layers/generated/parameter_validation.cpp
index e0de668..9156bb2 100644
--- a/layers/generated/parameter_validation.cpp
+++ b/layers/generated/parameter_validation.cpp
@@ -10348,6 +10348,7 @@
     if (!device_extensions.vk_khr_get_physical_device_properties_2) skip |= OutputExtensionError("vkCmdSetLineStippleEXT", VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
     if (!device_extensions.vk_ext_line_rasterization) skip |= OutputExtensionError("vkCmdSetLineStippleEXT", VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
     // No xml-driven validation
+    if (!skip) skip |= manual_PreCallValidateCmdSetLineStippleEXT(commandBuffer, lineStippleFactor, lineStipplePattern);
     return skip;
 }
 
diff --git a/layers/parameter_validation_utils.cpp b/layers/parameter_validation_utils.cpp
index 16d0ca1..4000815 100644
--- a/layers/parameter_validation_utils.cpp
+++ b/layers/parameter_validation_utils.cpp
@@ -917,6 +917,7 @@
             bool has_dynamic_sample_locations_ext = false;
             bool has_dynamic_exclusive_scissor_nv = false;
             bool has_dynamic_shading_rate_palette_nv = false;
+            bool has_dynamic_line_stipple = false;
             if (pCreateInfos[i].pDynamicState != nullptr) {
                 const auto &dynamic_state_info = *pCreateInfos[i].pDynamicState;
                 for (uint32_t state_index = 0; state_index < dynamic_state_info.dynamicStateCount; ++state_index) {
@@ -930,6 +931,7 @@
                     if (dynamic_state == VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV) has_dynamic_exclusive_scissor_nv = true;
                     if (dynamic_state == VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV)
                         has_dynamic_shading_rate_palette_nv = true;
+                    if (dynamic_state == VK_DYNAMIC_STATE_LINE_STIPPLE_EXT) has_dynamic_line_stipple = true;
                 }
             }
 
@@ -1601,6 +1603,113 @@
                                 "vkCreateGraphicsPipelines(): parameter pCreateInfos[%d].pMultisampleState->minSampleShading.", i);
                         }
                     }
+
+                    const auto *line_state = lvl_find_in_chain<VkPipelineRasterizationLineStateCreateInfoEXT>(
+                        pCreateInfos[i].pRasterizationState->pNext);
+
+                    if (line_state) {
+                        if ((line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT ||
+                             line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT)) {
+                            if (pCreateInfos[i].pMultisampleState->alphaToCoverageEnable) {
+                                skip |=
+                                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766",
+                                            "vkCreateGraphicsPipelines(): Bresenham/Smooth line rasterization not supported with "
+                                            "pCreateInfos[%d].pMultisampleState->alphaToCoverageEnable == VK_TRUE.",
+                                            i);
+                            }
+                            if (pCreateInfos[i].pMultisampleState->alphaToOneEnable) {
+                                skip |=
+                                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766",
+                                            "vkCreateGraphicsPipelines(): Bresenham/Smooth line rasterization not supported with "
+                                            "pCreateInfos[%d].pMultisampleState->alphaToOneEnable == VK_TRUE.",
+                                            i);
+                            }
+                            if (pCreateInfos[i].pMultisampleState->sampleShadingEnable) {
+                                skip |=
+                                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766",
+                                            "vkCreateGraphicsPipelines(): Bresenham/Smooth line rasterization not supported with "
+                                            "pCreateInfos[%d].pMultisampleState->sampleShadingEnable == VK_TRUE.",
+                                            i);
+                            }
+                        }
+                        if (line_state->stippledLineEnable && !has_dynamic_line_stipple) {
+                            if (line_state->lineStippleFactor < 1 || line_state->lineStippleFactor > 256) {
+                                skip |=
+                                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkGraphicsPipelineCreateInfo-stippledLineEnable-02767",
+                                            "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineStippleFactor = %d must be in the "
+                                            "range [1,256].",
+                                            i, line_state->lineStippleFactor);
+                            }
+                        }
+                        const auto *line_features =
+                            lvl_find_in_chain<VkPhysicalDeviceLineRasterizationFeaturesEXT>(physical_device_features2.pNext);
+                        if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT &&
+                            (!line_features || !line_features->rectangularLines)) {
+                            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-02768",
+                                            "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                            "VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT requires the rectangularLines feature.",
+                                            i);
+                        }
+                        if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT &&
+                            (!line_features || !line_features->bresenhamLines)) {
+                            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-02769",
+                                            "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                            "VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT requires the bresenhamLines feature.",
+                                            i);
+                        }
+                        if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT &&
+                            (!line_features || !line_features->smoothLines)) {
+                            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                            "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-02770",
+                                            "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                            "VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT requires the smoothLines feature.",
+                                            i);
+                        }
+                        if (line_state->stippledLineEnable) {
+                            if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT &&
+                                (!line_features || !line_features->stippledRectangularLines)) {
+                                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+                                                0, "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02771",
+                                                "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                                "VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT with stipple requires the "
+                                                "stippledRectangularLines feature.",
+                                                i);
+                            }
+                            if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT &&
+                                (!line_features || !line_features->stippledBresenhamLines)) {
+                                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+                                                0, "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02772",
+                                                "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                                "VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT with stipple requires the "
+                                                "stippledBresenhamLines feature.",
+                                                i);
+                            }
+                            if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT &&
+                                (!line_features || !line_features->stippledSmoothLines)) {
+                                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+                                                0, "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02773",
+                                                "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                                "VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT with stipple requires the "
+                                                "stippledSmoothLines feature.",
+                                                i);
+                            }
+                            if (line_state->lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT &&
+                                (!line_features || !line_features->stippledSmoothLines || !device_limits.strictLines)) {
+                                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+                                                0, "VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02774",
+                                                "vkCreateGraphicsPipelines(): pCreateInfos[%d] lineRasterizationMode = "
+                                                "VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT with stipple requires the "
+                                                "stippledRectangularLines and strictLines features.",
+                                                i);
+                            }
+                        }
+                    }
                 }
 
                 bool uses_color_attachment = false;
@@ -3419,3 +3528,16 @@
     }
     return skip;
 }
+
+bool StatelessValidation::manual_PreCallValidateCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor,
+                                                                     uint16_t lineStipplePattern) {
+    bool skip = false;
+
+    if (lineStippleFactor < 1 || lineStippleFactor > 256) {
+        skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT,
+                        HandleToUint64(commandBuffer), "VUID-vkCmdSetLineStippleEXT-lineStippleFactor-02776",
+                        "vkCmdSetLineStippleEXT::lineStippleFactor=%d is not in [1,256].", lineStippleFactor);
+    }
+
+    return skip;
+}
diff --git a/layers/stateless_validation.h b/layers/stateless_validation.h
index 235fcc0..3bf229a 100644
--- a/layers/stateless_validation.h
+++ b/layers/stateless_validation.h
@@ -1176,5 +1176,7 @@
     bool manual_PreCallValidateCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo *pCreateInfo,
                                                  const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer);
 
+    bool manual_PreCallValidateCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor,
+                                                    uint16_t lineStipplePattern);
 #include "parameter_validation.h"
 };  // Class StatelessValidation
diff --git a/scripts/parameter_validation_generator.py b/scripts/parameter_validation_generator.py
index 50bec7f..3392fb5 100644
--- a/scripts/parameter_validation_generator.py
+++ b/scripts/parameter_validation_generator.py
@@ -180,6 +180,7 @@
             'vkGetAccelerationStructureHandleNV',
             'vkCmdBuildAccelerationStructureNV',
             'vkCreateFramebuffer',
+            'vkCmdSetLineStippleEXT',
             ]
 
         # Commands to ignore