Implement VK_EXT_extended_dynamic_state3

Test: dEQP-VK.pipeline.*.extended_dynamic_state.*
Change-Id: Id2a1566f3db316369b7fcba08811e1d5cf468406
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/77908
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
Reviewed-by: Jason Macnak <natsu@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index ee753ba..32e953c 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -164,12 +164,30 @@
 		case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE:
 			dynamicStateFlags.preRasterization.dynamicDepthBiasEnable = true;
 			break;
+		case VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT:
+			dynamicStateFlags.preRasterization.dynamicDepthClampEnable = true;
+			break;
+		case VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT:
+			dynamicStateFlags.preRasterization.dynamicDepthClipEnable = true;
+			break;
+		case VK_DYNAMIC_STATE_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT:
+			dynamicStateFlags.preRasterization.dynamicDepthClipNegativeOneToOne = true;
+			break;
 		case VK_DYNAMIC_STATE_CULL_MODE:
 			dynamicStateFlags.preRasterization.dynamicCullMode = true;
 			break;
 		case VK_DYNAMIC_STATE_FRONT_FACE:
 			dynamicStateFlags.preRasterization.dynamicFrontFace = true;
 			break;
+		case VK_DYNAMIC_STATE_POLYGON_MODE_EXT:
+			dynamicStateFlags.preRasterization.dynamicPolygonMode = true;
+			break;
+		case VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT:
+			dynamicStateFlags.preRasterization.dynamicLineRasterizationMode = true;
+			break;
+		case VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT:
+			dynamicStateFlags.preRasterization.dynamicProvokingVertexMode = true;
+			break;
 		case VK_DYNAMIC_STATE_VIEWPORT:
 			dynamicStateFlags.preRasterization.dynamicViewport = true;
 			break;
@@ -218,10 +236,36 @@
 			dynamicStateFlags.fragment.dynamicStencilReference = true;
 			break;
 
+		// Fragment or fragment output interface:
+		case VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT:
+			dynamicStateFlags.fragment.dynamicRasterizationSamples = true;
+			dynamicStateFlags.fragmentOutputInterface.dynamicRasterizationSamples = true;
+			break;
+		case VK_DYNAMIC_STATE_SAMPLE_MASK_EXT:
+			dynamicStateFlags.fragment.dynamicSampleMask = true;
+			dynamicStateFlags.fragmentOutputInterface.dynamicSampleMask = true;
+			break;
+		case VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT:
+			dynamicStateFlags.fragment.dynamicAlphaToCoverageEnable = true;
+			dynamicStateFlags.fragmentOutputInterface.dynamicAlphaToCoverageEnable = true;
+			break;
+
 		// Fragment output interface:
 		case VK_DYNAMIC_STATE_BLEND_CONSTANTS:
 			dynamicStateFlags.fragmentOutputInterface.dynamicBlendConstants = true;
 			break;
+		case VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT:
+			dynamicStateFlags.fragmentOutputInterface.dynamicColorBlendEnable = true;
+			break;
+		case VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT:
+			dynamicStateFlags.fragmentOutputInterface.dynamicColorBlendEquation = true;
+			break;
+		case VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT:
+			dynamicStateFlags.fragmentOutputInterface.dynamicColorBlendAdvanced = true;
+			break;
+		case VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT:
+			dynamicStateFlags.fragmentOutputInterface.dynamicColorWriteMask = true;
+			break;
 
 		default:
 			UNSUPPORTED("VkDynamicState %d", int(dynamicState));
@@ -482,7 +526,7 @@
 	return 0;
 }
 
-void MultisampleState::set(const VkPipelineMultisampleStateCreateInfo *multisampleState)
+void MultisampleState::set(const FragmentOutputInterfaceDynamicStateFlags &dynamicStateFlags, const VkPipelineMultisampleStateCreateInfo *multisampleState)
 {
 	if(multisampleState->flags != 0)
 	{
@@ -510,21 +554,24 @@
 		sampleCount = 4;
 		break;
 	default:
-		UNSUPPORTED("Unsupported sample count");
+		if(!dynamicStateFlags.dynamicRasterizationSamples)
+		{
+			UNSUPPORTED("Unsupported sample count");
+		}
+		sampleCount = 4;
 	}
 
-	VkSampleMask sampleMask;
 	if(multisampleState->pSampleMask)
 	{
-		sampleMask = multisampleState->pSampleMask[0];
+		staticSampleMask = multisampleState->pSampleMask[0];
 	}
 	else  // "If pSampleMask is NULL, it is treated as if the mask has all bits set to 1."
 	{
-		sampleMask = ~0;
+		staticSampleMask = ~0;
 	}
 
 	alphaToCoverage = (multisampleState->alphaToCoverageEnable != VK_FALSE);
-	multiSampleMask = sampleMask & ((unsigned)0xFFFFFFFF >> (32 - sampleCount));
+	multiSampleMask = staticSampleMask & ((unsigned)0xFFFFFFFF >> (32 - sampleCount));
 }
 
 void VertexInputInterfaceState::initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState,
@@ -539,6 +586,21 @@
 		UNSUPPORTED("vertexInputState->flags");
 	}
 
+	// The spec says:
+	//
+	// If the VK_EXT_extended_dynamic_state3 extension is enabled, it can be NULL if the
+	// pipeline is created with both VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE, and
+	// VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY dynamic states set and
+	// dynamicPrimitiveTopologyUnrestricted is VK_TRUE.
+	//
+	// SwiftShader sets dynamicPrimitiveTopologyUnrestricted to VK_TRUE.
+	if(inputAssemblyState == nullptr)
+	{
+		ASSERT(dynamicStateFlags.dynamicPrimitiveRestartEnable &&
+		       dynamicStateFlags.dynamicPrimitiveTopology);
+		return;
+	}
+
 	if(inputAssemblyState->flags != 0)
 	{
 		// Vulkan 1.2: "flags is reserved for future use." "flags must be 0"
@@ -629,85 +691,121 @@
 	pipelineLayout = layout;
 	dynamicStateFlags = allDynamicStateFlags.preRasterization;
 
-	if(rasterizationState->flags != 0)
+	// The spec says:
+	//
+	// If the VK_EXT_extended_dynamic_state3 extension is enabled, it can be NULL if the
+	// pipeline is created with all of VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
+	// VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE, VK_DYNAMIC_STATE_POLYGON_MODE_EXT,
+	// VK_DYNAMIC_STATE_CULL_MODE, VK_DYNAMIC_STATE_FRONT_FACE,
+	// VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE, VK_DYNAMIC_STATE_DEPTH_BIAS, and
+	// VK_DYNAMIC_STATE_LINE_WIDTH dynamic states set.
+	if(rasterizationState == nullptr)
 	{
-		// Vulkan 1.2: "flags is reserved for future use." "flags must be 0"
-		UNSUPPORTED("pCreateInfo->pRasterizationState->flags 0x%08X", int(rasterizationState->flags));
+		ASSERT(dynamicStateFlags.dynamicDepthClampEnable &&
+		       dynamicStateFlags.dynamicRasterizerDiscardEnable &&
+		       dynamicStateFlags.dynamicPolygonMode &&
+		       dynamicStateFlags.dynamicCullMode &&
+		       dynamicStateFlags.dynamicFrontFace &&
+		       dynamicStateFlags.dynamicDepthBiasEnable &&
+		       dynamicStateFlags.dynamicDepthBias &&
+		       dynamicStateFlags.dynamicLineWidth);
 	}
-
-	rasterizerDiscard = rasterizationState->rasterizerDiscardEnable != VK_FALSE;
-	cullMode = rasterizationState->cullMode;
-	frontFace = rasterizationState->frontFace;
-	polygonMode = rasterizationState->polygonMode;
-	depthBiasEnable = rasterizationState->depthBiasEnable;
-	constantDepthBias = rasterizationState->depthBiasConstantFactor;
-	slopeDepthBias = rasterizationState->depthBiasSlopeFactor;
-	depthBiasClamp = rasterizationState->depthBiasClamp;
-	depthRangeUnrestricted = device->hasExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME);
-	depthClampEnable = rasterizationState->depthClampEnable != VK_FALSE;
-	depthClipEnable = !depthClampEnable;
-
-	// From the Vulkan spec for vkCmdSetDepthBias:
-	//    The bias value O for a polygon is:
-	//        O = dbclamp(...)
-	//    where dbclamp(x) =
-	//        * x                       depthBiasClamp = 0 or NaN
-	//        * min(x, depthBiasClamp)  depthBiasClamp > 0
-	//        * max(x, depthBiasClamp)  depthBiasClamp < 0
-	// So it should be safe to resolve NaNs to 0.0f.
-	if(std::isnan(depthBiasClamp))
+	else
 	{
-		depthBiasClamp = 0.0f;
-	}
-
-	if(!dynamicStateFlags.dynamicLineWidth)
-	{
-		lineWidth = rasterizationState->lineWidth;
-	}
-
-	const VkBaseInStructure *extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(rasterizationState->pNext);
-	while(extensionCreateInfo)
-	{
-		switch(extensionCreateInfo->sType)
+		if(rasterizationState->flags != 0)
 		{
-		case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT:
-			{
-				const VkPipelineRasterizationLineStateCreateInfoEXT *lineStateCreateInfo = reinterpret_cast<const VkPipelineRasterizationLineStateCreateInfoEXT *>(extensionCreateInfo);
-				lineRasterizationMode = lineStateCreateInfo->lineRasterizationMode;
-			}
-			break;
-		case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT:
-			{
-				const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *provokingVertexModeCreateInfo =
-				    reinterpret_cast<const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *>(extensionCreateInfo);
-				provokingVertexMode = provokingVertexModeCreateInfo->provokingVertexMode;
-			}
-			break;
-		case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT:
-			{
-				const auto *depthClipInfo = reinterpret_cast<const VkPipelineRasterizationDepthClipStateCreateInfoEXT *>(extensionCreateInfo);
-				// Reserved for future use.
-				ASSERT(depthClipInfo->flags == 0);
-				depthClipEnable = depthClipInfo->depthClipEnable != VK_FALSE;
-			}
-			break;
-		case VK_STRUCTURE_TYPE_APPLICATION_INFO:
-			// SwiftShader doesn't interact with application info, but dEQP includes it
-			break;
-		case VK_STRUCTURE_TYPE_MAX_ENUM:
-			// dEQP tests that this value is ignored.
-			break;
-		default:
-			UNSUPPORTED("pCreateInfo->pRasterizationState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
-			break;
+			// Vulkan 1.2: "flags is reserved for future use." "flags must be 0"
+			UNSUPPORTED("pCreateInfo->pRasterizationState->flags 0x%08X", int(rasterizationState->flags));
 		}
 
-		extensionCreateInfo = extensionCreateInfo->pNext;
+		rasterizerDiscard = rasterizationState->rasterizerDiscardEnable != VK_FALSE;
+		cullMode = rasterizationState->cullMode;
+		frontFace = rasterizationState->frontFace;
+		polygonMode = rasterizationState->polygonMode;
+		depthBiasEnable = rasterizationState->depthBiasEnable;
+		constantDepthBias = rasterizationState->depthBiasConstantFactor;
+		slopeDepthBias = rasterizationState->depthBiasSlopeFactor;
+		depthBiasClamp = rasterizationState->depthBiasClamp;
+		depthRangeUnrestricted = device->hasExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME);
+		depthClampEnable = rasterizationState->depthClampEnable != VK_FALSE;
+		depthClipEnable = !depthClampEnable;
+		depthClipEnableIsExplicit = false;
+
+		// From the Vulkan spec for vkCmdSetDepthBias:
+		//    The bias value O for a polygon is:
+		//        O = dbclamp(...)
+		//    where dbclamp(x) =
+		//        * x                       depthBiasClamp = 0 or NaN
+		//        * min(x, depthBiasClamp)  depthBiasClamp > 0
+		//        * max(x, depthBiasClamp)  depthBiasClamp < 0
+		// So it should be safe to resolve NaNs to 0.0f.
+		if(std::isnan(depthBiasClamp))
+		{
+			depthBiasClamp = 0.0f;
+		}
+
+		if(!dynamicStateFlags.dynamicLineWidth)
+		{
+			lineWidth = rasterizationState->lineWidth;
+		}
+
+		const VkBaseInStructure *extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(rasterizationState->pNext);
+		while(extensionCreateInfo)
+		{
+			switch(extensionCreateInfo->sType)
+			{
+			case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT:
+				{
+					const VkPipelineRasterizationLineStateCreateInfoEXT *lineStateCreateInfo = reinterpret_cast<const VkPipelineRasterizationLineStateCreateInfoEXT *>(extensionCreateInfo);
+					lineRasterizationMode = lineStateCreateInfo->lineRasterizationMode;
+				}
+				break;
+			case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT:
+				{
+					const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *provokingVertexModeCreateInfo =
+					    reinterpret_cast<const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *>(extensionCreateInfo);
+					provokingVertexMode = provokingVertexModeCreateInfo->provokingVertexMode;
+				}
+				break;
+			case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT:
+				{
+					const auto *depthClipInfo = reinterpret_cast<const VkPipelineRasterizationDepthClipStateCreateInfoEXT *>(extensionCreateInfo);
+					// Reserved for future use.
+					ASSERT(depthClipInfo->flags == 0);
+					depthClipEnable = depthClipInfo->depthClipEnable != VK_FALSE;
+					depthClipEnableIsExplicit = !dynamicStateFlags.dynamicDepthClipEnable;
+				}
+				break;
+			case VK_STRUCTURE_TYPE_APPLICATION_INFO:
+				// SwiftShader doesn't interact with application info, but dEQP includes it
+				break;
+			case VK_STRUCTURE_TYPE_MAX_ENUM:
+				// dEQP tests that this value is ignored.
+				break;
+			default:
+				UNSUPPORTED("pCreateInfo->pRasterizationState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
+				break;
+			}
+
+			extensionCreateInfo = extensionCreateInfo->pNext;
+		}
+	}
+
+	// The spec says:
+	//
+	// If the VK_EXT_extended_dynamic_state3 extension is enabled, it can be NULL if the
+	// pipeline is created with both VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, and
+	// VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT dynamic states set.
+	if(viewportState == nullptr)
+	{
+		ASSERT(dynamicStateFlags.dynamicViewportWithCount &&
+		       dynamicStateFlags.dynamicScissorWithCount);
+		return;
 	}
 
 	if(!rasterizerDiscard || dynamicStateFlags.dynamicRasterizerDiscardEnable)
 	{
-		extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(viewportState->pNext);
+		const VkBaseInStructure *extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(viewportState->pNext);
 		while(extensionCreateInfo != nullptr)
 		{
 			switch(extensionCreateInfo->sType)
@@ -771,6 +869,25 @@
 		depthBiasEnable = dynamicState.depthBiasEnable;
 	}
 
+	if(dynamicStateFlags.dynamicDepthClampEnable)
+	{
+		depthClampEnable = dynamicState.depthClampEnable;
+		if(!depthClipEnableIsExplicit && !dynamicStateFlags.dynamicDepthClipEnable)
+		{
+			depthClipEnable = !dynamicState.depthClampEnable;
+		}
+	}
+
+	if(dynamicStateFlags.dynamicDepthClipEnable)
+	{
+		depthClipEnable = dynamicState.depthClipEnable;
+	}
+
+	if(dynamicStateFlags.dynamicDepthClipNegativeOneToOne)
+	{
+		depthClipNegativeOneToOne = dynamicState.depthClipNegativeOneToOne;
+	}
+
 	if(dynamicStateFlags.dynamicCullMode)
 	{
 		cullMode = dynamicState.cullMode;
@@ -781,6 +898,21 @@
 		frontFace = dynamicState.frontFace;
 	}
 
+	if(dynamicStateFlags.dynamicPolygonMode)
+	{
+		polygonMode = dynamicState.polygonMode;
+	}
+
+	if(dynamicStateFlags.dynamicLineRasterizationMode)
+	{
+		lineRasterizationMode = dynamicState.lineRasterizationMode;
+	}
+
+	if(dynamicStateFlags.dynamicProvokingVertexMode)
+	{
+		provokingVertexMode = dynamicState.provokingVertexMode;
+	}
+
 	if(dynamicStateFlags.dynamicViewport)
 	{
 		viewport = dynamicState.viewport;
@@ -817,6 +949,20 @@
 	pipelineLayout = layout;
 	dynamicStateFlags = allDynamicStateFlags.fragment;
 
+	// The spec says:
+	//
+	// If the VK_EXT_extended_dynamic_state3 extension is enabled, it can be NULL if the
+	// pipeline is created with all of VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE,
+	// VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE, VK_DYNAMIC_STATE_DEPTH_COMPARE_OP,
+	// VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE, VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE,
+	// VK_DYNAMIC_STATE_STENCIL_OP, and VK_DYNAMIC_STATE_DEPTH_BOUNDS dynamic states set.
+	//
+	// But pDepthStencilState can still be nullptr if no subpass uses depth/stencil.
+	if(depthStencilState == nullptr)
+	{
+		return;
+	}
+
 	if(renderPass)
 	{
 		const VkSubpassDescription &subpass = renderPass->getSubpass(subpassIndex);
@@ -981,7 +1127,43 @@
 {
 	dynamicStateFlags = allDynamicStateFlags.fragmentOutputInterface;
 
-	multisample.set(multisampleState);
+	// The spec says:
+	//
+	// If the VK_EXT_extended_dynamic_state3 extension is enabled, it [pMultisampleState] can be
+	// NULL if the pipeline is created with all of VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT,
+	// VK_DYNAMIC_STATE_SAMPLE_MASK_EXT, and VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT
+	// dynamic states set, and either the alphaToOne feature is not enabled or
+	// VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT is set, in which case
+	// VkPipelineMultisampleStateCreateInfo::sampleShadingEnable is assumed to be VK_FALSE.
+	//
+	// SwiftShader does not support alphaToOne.
+	if(multisampleState == nullptr)
+	{
+		ASSERT(dynamicStateFlags.dynamicRasterizationSamples &&
+		       dynamicStateFlags.dynamicSampleMask &&
+		       dynamicStateFlags.dynamicAlphaToCoverageEnable);
+	}
+	else
+	{
+		multisample.set(dynamicStateFlags, multisampleState);
+	}
+
+	// The spec says:
+	//
+	// If the VK_EXT_extended_dynamic_state3 extension is enabled, it [pColorBlendState] can be
+	// NULL if the pipeline is created with all of VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
+	// VK_DYNAMIC_STATE_LOGIC_OP_EXT, VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
+	// VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, and
+	// VK_DYNAMIC_STATE_BLEND_CONSTANTS dynamic states set.
+	//
+	// SwiftShader does not support logic op; if everything else is static there is no need to
+	// look at colorBlendState.
+	//
+	// pColorBlendState may be nullptr if there are no attachments.
+	if(colorBlendState == nullptr)
+	{
+		return;
+	}
 
 	if(renderPass)
 	{
@@ -1025,10 +1207,66 @@
 
 void FragmentOutputInterfaceState::applyState(const DynamicState &dynamicState)
 {
+	// State that affects both Fragment and Fragment Output subsets are always stored in the
+	// Fragment Output subset in SwiftShader
+	if(dynamicStateFlags.dynamicRasterizationSamples)
+	{
+		multisample.sampleCount = dynamicState.rasterizationSamples;
+	}
+
+	if(dynamicStateFlags.dynamicSampleMask || dynamicStateFlags.dynamicRasterizationSamples)
+	{
+		unsigned int sampleMask = dynamicStateFlags.dynamicSampleMask ? dynamicState.sampleMask : multisample.staticSampleMask;
+		multisample.multiSampleMask = sampleMask & ((unsigned)0xFFFFFFFF >> (32 - multisample.sampleCount));
+	}
+
+	if(dynamicStateFlags.dynamicAlphaToCoverageEnable)
+	{
+		multisample.alphaToCoverage = dynamicState.alphaToCoverageEnable;
+	}
+
 	if(dynamicStateFlags.dynamicBlendConstants)
 	{
 		blendConstants = dynamicState.blendConstants;
 	}
+
+	if(dynamicStateFlags.dynamicColorBlendEnable)
+	{
+		for(uint32_t i = 0; i < sw::MAX_COLOR_BUFFERS; ++i)
+		{
+			blendState[i].alphaBlendEnable = dynamicState.colorBlendEnables[i] != VK_FALSE;
+		}
+	}
+
+	if(dynamicStateFlags.dynamicColorBlendEquation)
+	{
+		for(uint32_t i = 0; i < sw::MAX_COLOR_BUFFERS; ++i)
+		{
+			blendState[i].sourceBlendFactor = dynamicState.colorBlendEquations[i].srcColorBlendFactor;
+			blendState[i].destBlendFactor = dynamicState.colorBlendEquations[i].dstColorBlendFactor;
+			blendState[i].sourceBlendFactorAlpha = dynamicState.colorBlendEquations[i].srcAlphaBlendFactor;
+			blendState[i].destBlendFactorAlpha = dynamicState.colorBlendEquations[i].dstAlphaBlendFactor;
+			blendState[i].blendOperationAlpha = dynamicState.colorBlendEquations[i].alphaBlendOp;
+		}
+	}
+
+	// colorBlendOp may be set either by COLOR_BLEND_EQUATION or COLOR_BLEND_ADVANCED dynamic
+	// states
+	if(dynamicStateFlags.dynamicColorBlendEquation || dynamicStateFlags.dynamicColorBlendAdvanced)
+	{
+		for(uint32_t i = 0; i < sw::MAX_COLOR_BUFFERS; ++i)
+		{
+			blendState[i].blendOperation = dynamicState.colorBlendEquations[i].colorBlendOp;
+		}
+	}
+
+	if(dynamicStateFlags.dynamicColorWriteMask)
+	{
+		for(uint32_t i = 0; i < sw::MAX_COLOR_BUFFERS; ++i)
+		{
+			colorWriteMask[i] = dynamicState.colorWriteMasks[i];
+		}
+	}
 }
 
 void FragmentOutputInterfaceState::setColorBlendState(const VkPipelineColorBlendStateCreateInfo *colorBlendState)
@@ -1076,14 +1314,17 @@
 		extensionColorBlendInfo = extensionColorBlendInfo->pNext;
 	}
 
-	ASSERT(colorBlendState->attachmentCount <= sw::MAX_COLOR_BUFFERS);
-	for(auto i = 0u; i < colorBlendState->attachmentCount; i++)
+	if(colorBlendState->pAttachments)
 	{
-		const VkPipelineColorBlendAttachmentState &attachment = colorBlendState->pAttachments[i];
-		colorWriteMask[i] = attachment.colorWriteMask;
-		blendState[i] = { (attachment.blendEnable != VK_FALSE),
-			              attachment.srcColorBlendFactor, attachment.dstColorBlendFactor, attachment.colorBlendOp,
-			              attachment.srcAlphaBlendFactor, attachment.dstAlphaBlendFactor, attachment.alphaBlendOp };
+		ASSERT(colorBlendState->attachmentCount <= sw::MAX_COLOR_BUFFERS);
+		for(auto i = 0u; i < colorBlendState->attachmentCount; i++)
+		{
+			const VkPipelineColorBlendAttachmentState &attachment = colorBlendState->pAttachments[i];
+			colorWriteMask[i] = attachment.colorWriteMask;
+			blendState[i] = { (attachment.blendEnable != VK_FALSE),
+				              attachment.srcColorBlendFactor, attachment.dstColorBlendFactor, attachment.colorBlendOp,
+				              attachment.srcAlphaBlendFactor, attachment.dstAlphaBlendFactor, attachment.alphaBlendOp };
+		}
 	}
 }
 
@@ -1301,7 +1542,8 @@
 
 	vk::Format format = attachments.colorBuffer[location]->getFormat(VK_IMAGE_ASPECT_COLOR_BIT);
 
-	if(blendOperation(state.blendOperation, state.sourceBlendFactor, state.destBlendFactor, format) == VK_BLEND_OP_DST_EXT &&
+	if(state.alphaBlendEnable &&
+	   blendOperation(state.blendOperation, state.sourceBlendFactor, state.destBlendFactor, format) == VK_BLEND_OP_DST_EXT &&
 	   blendOperation(state.blendOperationAlpha, state.sourceBlendFactorAlpha, state.destBlendFactorAlpha, format) == VK_BLEND_OP_DST_EXT)
 	{
 		return 0;
@@ -1335,8 +1577,8 @@
 	// If rasterizer discard is enabled (and not dynamically overridable), ignore the fragment
 	// and fragment output subsets, as they will not be used.
 	if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0 &&
-	   pCreateInfo->pRasterizationState->rasterizerDiscardEnable &&
-	   !dynamicStateFlags.preRasterization.dynamicRasterizerDiscardEnable)
+	   !dynamicStateFlags.preRasterization.dynamicRasterizerDiscardEnable &&
+	   pCreateInfo->pRasterizationState->rasterizerDiscardEnable)
 	{
 		validSubset &= ~(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT | VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT);
 	}
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index c1798bb..dd83d41 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -52,8 +52,14 @@
 	bool dynamicLineWidth : 1;
 	bool dynamicDepthBias : 1;
 	bool dynamicDepthBiasEnable : 1;
+	bool dynamicDepthClampEnable : 1;
+	bool dynamicDepthClipEnable : 1;
+	bool dynamicDepthClipNegativeOneToOne : 1;
 	bool dynamicCullMode : 1;
 	bool dynamicFrontFace : 1;
+        bool dynamicPolygonMode : 1;
+        bool dynamicLineRasterizationMode : 1;
+        bool dynamicProvokingVertexMode : 1;
 	bool dynamicViewport : 1;
 	bool dynamicScissor : 1;
 	bool dynamicViewportWithCount : 1;
@@ -73,16 +79,28 @@
 	bool dynamicStencilCompareMask : 1;
 	bool dynamicStencilWriteMask : 1;
 	bool dynamicStencilReference : 1;
+        // Shared with FragmentOutputInterfaceDynamicStateFlags
+	bool dynamicRasterizationSamples : 1;
+	bool dynamicSampleMask : 1;
+	bool dynamicAlphaToCoverageEnable : 1;
 };
 
 struct FragmentOutputInterfaceDynamicStateFlags
 {
 	bool dynamicBlendConstants : 1;
+	bool dynamicColorBlendEnable : 1;
+	bool dynamicColorBlendEquation : 1;
+	bool dynamicColorBlendAdvanced : 1;
+	bool dynamicColorWriteMask : 1;
+        // Share dwith FragmentDynamicStateFlags
+	bool dynamicRasterizationSamples : 1;
+	bool dynamicSampleMask : 1;
+	bool dynamicAlphaToCoverageEnable : 1;
 };
 
 struct DynamicStateFlags
 {
-    // Note: InputsDynamicStateFlags is kept local to Inputs
+	// Note: InputsDynamicStateFlags is kept local to Inputs
 	VertexInputInterfaceDynamicStateFlags vertexInputInterface;
 	PreRasterizationDynamicStateFlags preRasterization;
 	FragmentDynamicStateFlags fragment;
@@ -162,10 +180,14 @@
 	bool alphaToCoverage = false;
 
 	int sampleCount = 0;
+	// multiSampleMask is derived from sample count and sample mask.  When sample count is
+	// dynamic but sample mask is static, the static value of sample mask is needed to derive
+	// this value dynamically.
 	unsigned int multiSampleMask = 0;
+	unsigned int staticSampleMask = 0;
 	float minSampleShading = 0.0f;
 
-	void set(const VkPipelineMultisampleStateCreateInfo *multisampleState);
+	void set(const FragmentOutputInterfaceDynamicStateFlags &dynamicStateFlags, const VkPipelineMultisampleStateCreateInfo *multisampleState);
 };
 
 struct BlendState : sw::Memset<BlendState>
@@ -231,7 +253,13 @@
 	VkCompareOp depthCompareOp = VK_COMPARE_OP_NEVER;
 	VkBool32 depthTestEnable = VK_FALSE;
 	VkBool32 depthWriteEnable = VK_FALSE;
+	VkBool32 depthClampEnable = VK_FALSE;
+	VkBool32 depthClipEnable = VK_FALSE;
+	VkBool32 depthClipNegativeOneToOne = VK_FALSE;
 	VkFrontFace frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+        VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL;
+        VkLineRasterizationModeEXT lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT;
+        VkProvokingVertexModeEXT provokingVertexMode = VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT;
 	VkPrimitiveTopology primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
 	uint32_t scissorCount = 0;
 	VkRect2D scissors[vk::MAX_VIEWPORTS] = {};
@@ -244,6 +272,12 @@
 	VkBool32 rasterizerDiscardEnable = VK_FALSE;
 	VkBool32 depthBiasEnable = VK_FALSE;
 	VkBool32 primitiveRestartEnable = VK_FALSE;
+        VkBool32 alphaToCoverageEnable = VK_FALSE;
+        VkSampleCountFlagBits rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+	VkSampleMask sampleMask = 0x1;
+        VkBool32 colorBlendEnables[sw::MAX_COLOR_BUFFERS] = {};
+        VkColorBlendEquationEXT colorBlendEquations[sw::MAX_COLOR_BUFFERS] = {};
+        VkColorComponentFlags colorWriteMasks[sw::MAX_COLOR_BUFFERS] = {};
 	DynamicVertexInputBindingState vertexInputBindings[MAX_VERTEX_INPUT_BINDINGS];
 	DynamicVertexInputAttributeState vertexInputAttributes[sw::MAX_INTERFACE_COMPONENTS / 4];
 };
@@ -318,7 +352,9 @@
 
 	bool rasterizerDiscard = false;
 	bool depthClampEnable = false;
+	// depthClipEnable is implicitly !depthClampEnable.  However, it may be explicitly set by VkPipelineRasterizationDepthClipStateCreateInfoEXT / dynamic state.
 	bool depthClipEnable = false;
+	bool depthClipEnableIsExplicit = true;
 	bool depthClipNegativeOneToOne = false;
 	bool depthBiasEnable = false;
 	bool depthRangeUnrestricted = false;
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index cd664c6..c61e132 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -917,6 +917,264 @@
 	const VkBool32 primitiveRestartEnable;
 };
 
+class CmdSetDepthClampEnable : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetDepthClampEnable(VkBool32 depthClampEnable)
+	    : depthClampEnable(depthClampEnable)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.depthClampEnable = depthClampEnable;
+	}
+
+	std::string description() override { return "vkCmdSetDepthClampEnable()"; }
+
+private:
+	const VkBool32 depthClampEnable;
+};
+
+class CmdSetPolygonMode : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetPolygonMode(VkPolygonMode polygonMode)
+	    : polygonMode(polygonMode)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.polygonMode = polygonMode;
+	}
+
+	std::string description() override { return "vkCmdSetPolygonMode()"; }
+
+private:
+	const VkPolygonMode polygonMode;
+};
+
+class CmdSetRasterizationSamples : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetRasterizationSamples(VkSampleCountFlagBits rasterizationSamples)
+	    : rasterizationSamples(rasterizationSamples)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.rasterizationSamples = rasterizationSamples;
+	}
+
+	std::string description() override { return "vkCmdSetRasterizationSamples()"; }
+
+private:
+	const VkSampleCountFlagBits rasterizationSamples;
+};
+
+class CmdSetSampleMask : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetSampleMask(VkSampleCountFlagBits samples, const VkSampleMask *pSampleMask)
+	    : sampleMask(pSampleMask[0])
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.sampleMask = sampleMask;
+	}
+
+	std::string description() override { return "vkCmdSetSampleMask()"; }
+
+private:
+	const VkSampleMask sampleMask;
+};
+
+class CmdSetAlphaToCoverageEnable : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetAlphaToCoverageEnable(VkBool32 alphaToCoverageEnable)
+	    : alphaToCoverageEnable(alphaToCoverageEnable)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.alphaToCoverageEnable = alphaToCoverageEnable;
+	}
+
+	std::string description() override { return "vkCmdSetAlphaToCoverageEnable()"; }
+
+private:
+	const VkBool32 alphaToCoverageEnable;
+};
+
+class CmdSetColorBlendEnable : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetColorBlendEnable(uint32_t firstAttachment, uint32_t attachmentCount, const VkBool32 *pColorBlendEnables)
+	    : firstAttachment(firstAttachment)
+	    , colorBlendEnables(pColorBlendEnables, pColorBlendEnables + attachmentCount)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		for(size_t i = 0; i < colorBlendEnables.size(); ++i)
+		{
+			executionState.dynamicState.colorBlendEnables[firstAttachment + i] = colorBlendEnables[i] != VK_FALSE;
+		}
+	}
+
+	std::string description() override { return "vkCmdSetColorBlendEnable()"; }
+
+private:
+	const uint32_t firstAttachment;
+	const std::vector<VkBool32> colorBlendEnables;
+};
+
+class CmdSetColorBlendEquation : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetColorBlendEquation(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendEquationEXT *pColorBlendEquations)
+	    : firstAttachment(firstAttachment)
+	    , colorBlendEquations(pColorBlendEquations, pColorBlendEquations + attachmentCount)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		std::copy(colorBlendEquations.begin(), colorBlendEquations.end(), executionState.dynamicState.colorBlendEquations + firstAttachment);
+	}
+
+	std::string description() override { return "vkCmdSetColorBlendEquation()"; }
+
+private:
+	const uint32_t firstAttachment;
+	const std::vector<VkColorBlendEquationEXT> colorBlendEquations;
+};
+
+class CmdSetColorWriteMask : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetColorWriteMask(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorComponentFlags *pColorWriteMasks)
+	    : firstAttachment(firstAttachment)
+	    , colorWriteMasks(pColorWriteMasks, pColorWriteMasks + attachmentCount)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		std::copy(colorWriteMasks.begin(), colorWriteMasks.end(), executionState.dynamicState.colorWriteMasks + firstAttachment);
+	}
+
+	std::string description() override { return "vkCmdSetColorWriteMask()"; }
+
+private:
+	const uint32_t firstAttachment;
+	const std::vector<VkColorComponentFlags> colorWriteMasks;
+};
+
+class CmdSetDepthClipEnable : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetDepthClipEnable(VkBool32 depthClipEnable)
+	    : depthClipEnable(depthClipEnable)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.depthClipEnable = depthClipEnable;
+	}
+
+	std::string description() override { return "vkCmdSetDepthClipEnable()"; }
+
+private:
+	const VkBool32 depthClipEnable;
+};
+
+class CmdSetColorBlendAdvanced : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetColorBlendAdvanced(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendAdvancedEXT *pColorBlendAdvanced)
+	    : firstAttachment(firstAttachment)
+	    , colorBlendAdvanced(pColorBlendAdvanced, pColorBlendAdvanced + attachmentCount)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		for(size_t i = 0; i < colorBlendAdvanced.size(); ++i)
+		{
+			// blendOverlap, srcPremultiplied and dstPremultiplied cannot be controlled because
+			// the revelant features are not exposed.
+			ASSERT(colorBlendAdvanced[i].blendOverlap == VK_BLEND_OVERLAP_UNCORRELATED_EXT);
+			ASSERT(colorBlendAdvanced[i].dstPremultiplied == VK_TRUE);
+			ASSERT(colorBlendAdvanced[i].srcPremultiplied == VK_TRUE);
+			// clampResults is not implemented, there is no corresponding static state it seems.
+			if(colorBlendAdvanced[i].clampResults == VK_TRUE)
+			{
+				UNSUPPORTED("VkColorBlendAdvancedEXT::clampResults");
+			}
+			executionState.dynamicState.colorBlendEquations[firstAttachment + i].colorBlendOp = colorBlendAdvanced[i].advancedBlendOp;
+		}
+	}
+
+	std::string description() override { return "vkCmdSetColorBlendAdvanced()"; }
+
+private:
+	const uint32_t firstAttachment;
+	const std::vector<VkColorBlendAdvancedEXT> colorBlendAdvanced;
+};
+
+class CmdSetProvokingVertexMode : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetProvokingVertexMode(VkProvokingVertexModeEXT provokingVertexMode)
+	    : provokingVertexMode(provokingVertexMode)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.provokingVertexMode = provokingVertexMode;
+	}
+
+	std::string description() override { return "vkCmdSetProvokingVertexMode()"; }
+
+private:
+	const VkProvokingVertexModeEXT provokingVertexMode;
+};
+
+class CmdSetLineRasterizationMode : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetLineRasterizationMode(VkLineRasterizationModeEXT lineRasterizationMode)
+	    : lineRasterizationMode(lineRasterizationMode)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.lineRasterizationMode = lineRasterizationMode;
+	}
+
+	std::string description() override { return "vkCmdSetLineRasterizationMode()"; }
+
+private:
+	const VkLineRasterizationModeEXT lineRasterizationMode;
+};
+
+class CmdSetDepthClipNegativeOneToOne : public vk::CommandBuffer::Command
+{
+public:
+	CmdSetDepthClipNegativeOneToOne(VkBool32 depthClipNegativeOneToOne)
+	    : depthClipNegativeOneToOne(depthClipNegativeOneToOne)
+	{}
+
+	void execute(vk::CommandBuffer::ExecutionState &executionState) override
+	{
+		executionState.dynamicState.depthClipNegativeOneToOne = depthClipNegativeOneToOne;
+	}
+
+	std::string description() override { return "vkCmdSetDepthClipNegativeOneToOne()"; }
+
+private:
+	const VkBool32 depthClipNegativeOneToOne;
+};
+
 class CmdSetVertexInput : public vk::CommandBuffer::Command
 {
 public:
@@ -2136,6 +2394,71 @@
 	addCommand<::CmdSetPrimitiveRestartEnable>(primitiveRestartEnable);
 }
 
+void CommandBuffer::setDepthClampEnable(VkBool32 depthClampEnable)
+{
+	addCommand<::CmdSetDepthClampEnable>(depthClampEnable);
+}
+
+void CommandBuffer::setPolygonMode(VkPolygonMode polygonMode)
+{
+	addCommand<::CmdSetPolygonMode>(polygonMode);
+}
+
+void CommandBuffer::setRasterizationSamples(VkSampleCountFlagBits rasterizationSamples)
+{
+	addCommand<::CmdSetRasterizationSamples>(rasterizationSamples);
+}
+
+void CommandBuffer::setSampleMask(VkSampleCountFlagBits samples, const VkSampleMask *pSampleMask)
+{
+	addCommand<::CmdSetSampleMask>(samples, pSampleMask);
+}
+
+void CommandBuffer::setAlphaToCoverageEnable(VkBool32 alphaToCoverageEnable)
+{
+	addCommand<::CmdSetAlphaToCoverageEnable>(alphaToCoverageEnable);
+}
+
+void CommandBuffer::setColorBlendEnable(uint32_t firstAttachment, uint32_t attachmentCount, const VkBool32 *pColorBlendEnables)
+{
+	addCommand<::CmdSetColorBlendEnable>(firstAttachment, attachmentCount, pColorBlendEnables);
+}
+
+void CommandBuffer::setColorBlendEquation(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendEquationEXT *pColorBlendEquations)
+{
+	addCommand<::CmdSetColorBlendEquation>(firstAttachment, attachmentCount, pColorBlendEquations);
+}
+
+void CommandBuffer::setColorWriteMask(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorComponentFlags *pColorWriteMasks)
+{
+	addCommand<::CmdSetColorWriteMask>(firstAttachment, attachmentCount, pColorWriteMasks);
+}
+
+void CommandBuffer::setDepthClipEnable(VkBool32 depthClipEnable)
+{
+	addCommand<::CmdSetDepthClipEnable>(depthClipEnable);
+}
+
+void CommandBuffer::setColorBlendAdvanced(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendAdvancedEXT *pColorBlendAdvanced)
+{
+	addCommand<::CmdSetColorBlendAdvanced>(firstAttachment, attachmentCount, pColorBlendAdvanced);
+}
+
+void CommandBuffer::setProvokingVertexMode(VkProvokingVertexModeEXT provokingVertexMode)
+{
+	addCommand<::CmdSetProvokingVertexMode>(provokingVertexMode);
+}
+
+void CommandBuffer::setLineRasterizationMode(VkLineRasterizationModeEXT lineRasterizationMode)
+{
+	addCommand<::CmdSetLineRasterizationMode>(lineRasterizationMode);
+}
+
+void CommandBuffer::setDepthClipNegativeOneToOne(VkBool32 negativeOneToOne)
+{
+	addCommand<::CmdSetDepthClipNegativeOneToOne>(negativeOneToOne);
+}
+
 void CommandBuffer::setVertexInput(uint32_t vertexBindingDescriptionCount,
                                    const VkVertexInputBindingDescription2EXT *pVertexBindingDescriptions,
                                    uint32_t vertexAttributeDescriptionCount,
diff --git a/src/Vulkan/VkCommandBuffer.hpp b/src/Vulkan/VkCommandBuffer.hpp
index f7345c4..683198e 100644
--- a/src/Vulkan/VkCommandBuffer.hpp
+++ b/src/Vulkan/VkCommandBuffer.hpp
@@ -143,6 +143,19 @@
 	void setRasterizerDiscardEnable(VkBool32 rasterizerDiscardEnable);
 	void setDepthBiasEnable(VkBool32 depthBiasEnable);
 	void setPrimitiveRestartEnable(VkBool32 primitiveRestartEnable);
+        void setDepthClampEnable(VkBool32 depthClampEnable);
+        void setPolygonMode(VkPolygonMode polygonMode);
+        void setRasterizationSamples(VkSampleCountFlagBits rasterizationSamples);
+        void setSampleMask(VkSampleCountFlagBits samples, const VkSampleMask* pSampleMask);
+        void setAlphaToCoverageEnable(VkBool32 alphaToCoverageEnable);
+        void setColorBlendEnable(uint32_t firstAttachment, uint32_t attachmentCount, const VkBool32* pColorBlendEnables);
+        void setColorBlendEquation(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendEquationEXT* pColorBlendEquations);
+        void setColorWriteMask(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorComponentFlags* pColorWriteMasks);
+        void setDepthClipEnable(VkBool32 depthClipEnable);
+        void setColorBlendAdvanced(uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendAdvancedEXT* pColorBlendAdvanced);
+        void setProvokingVertexMode(VkProvokingVertexModeEXT provokingVertexMode);
+        void setLineRasterizationMode(VkLineRasterizationModeEXT lineRasterizationMode);
+        void setDepthClipNegativeOneToOne(VkBool32 negativeOneToOne);
 	void setVertexInput(uint32_t vertexBindingDescriptionCount,
 			const VkVertexInputBindingDescription2EXT*  pVertexBindingDescriptions,
 			uint32_t vertexAttributeDescriptionCount,
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index 0d6615c..e9c328b 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -407,7 +407,7 @@
 	MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetDepthBoundsTestEnable),
 	MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetStencilTestEnable),
 	MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetStencilOp),
-	// VK_EXT_extended_dynamic_state2 (partial promotion, VKCmdSetLogicOpEXT and VKCmdSetPatchControlPointsEXT are not promoted)
+	// VK_EXT_extended_dynamic_state2 (partial promotion, vkCmdSetLogicOpEXT and vkCmdSetPatchControlPointsEXT are not promoted)
 	MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetRasterizerDiscardEnable),
 	MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetDepthBiasEnable),
 	MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetPrimitiveRestartEnable),
@@ -562,6 +562,33 @@
 	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetLogicOpEXT),
 	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetPatchControlPointsEXT),
 	    } },
+	// VK_EXT_extended_dynamic_state3
+	{
+	    VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
+	    {
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetAlphaToCoverageEnableEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetAlphaToOneEnableEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetColorBlendEnableEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetColorBlendEquationEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetColorWriteMaskEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetDepthClampEnableEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetLogicOpEnableEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetPolygonModeEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetRasterizationSamplesEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetSampleMaskEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetTessellationDomainOriginEXT),
+	        // +VK_EXT_blend_operation_advanced
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetColorBlendAdvancedEXT),
+	        // +VK_EXT_depth_clip_control
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetDepthClipNegativeOneToOneEXT),
+	        // +VK_EXT_depth_clip_enable
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetDepthClipEnableEXT),
+	        // +VK_EXT_provoking_vertex
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetProvokingVertexModeEXT),
+	        // +VK_EXT_line_rasterization
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetLineRasterizationModeEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCmdSetLineStippleEnableEXT),
+	    } },
 	// VK_EXT_vertex_input_dynamic_state
 	{
 	    VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 5d5edcf..9725e05 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -499,10 +499,57 @@
 static void getPhysicalDeviceExtendedDynamicState2FeaturesEXT(VkPhysicalDeviceExtendedDynamicState2FeaturesEXT *features)
 {
 	features->extendedDynamicState2 = VK_TRUE;
+
+	// logicOp is not supported
 	features->extendedDynamicState2LogicOp = VK_FALSE;
+	// Tessellation shaders are not supported
 	features->extendedDynamicState2PatchControlPoints = VK_FALSE;
 }
 
+static void getPhysicalDeviceExtendedDynamicState3FeaturesEXT(VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *features)
+{
+	features->extendedDynamicState3DepthClampEnable = VK_TRUE;
+	features->extendedDynamicState3PolygonMode = VK_TRUE;
+	features->extendedDynamicState3RasterizationSamples = VK_TRUE;
+	features->extendedDynamicState3SampleMask = VK_TRUE;
+	features->extendedDynamicState3AlphaToCoverageEnable = VK_TRUE;
+	features->extendedDynamicState3ColorBlendEnable = VK_TRUE;
+	features->extendedDynamicState3ColorBlendEquation = VK_TRUE;
+	features->extendedDynamicState3ColorWriteMask = VK_TRUE;
+	features->extendedDynamicState3DepthClipEnable = VK_TRUE;
+	features->extendedDynamicState3ColorBlendAdvanced = VK_TRUE;
+	features->extendedDynamicState3ProvokingVertexMode = VK_TRUE;
+	features->extendedDynamicState3LineRasterizationMode = VK_TRUE;
+	features->extendedDynamicState3DepthClipNegativeOneToOne = VK_TRUE;
+
+	// Tessellation shaders are not supported
+	features->extendedDynamicState3TessellationDomainOrigin = VK_FALSE;
+	// logicOp is not supported
+	features->extendedDynamicState3LogicOpEnable = VK_FALSE;
+	// alphaToOne is not supported
+	features->extendedDynamicState3AlphaToOneEnable = VK_FALSE;
+	// Stippled lines are not supported
+	features->extendedDynamicState3LineStippleEnable = VK_FALSE;
+	// VK_EXT_transform_feedback is not supported
+	features->extendedDynamicState3RasterizationStream = VK_FALSE;
+	// VK_EXT_conservative_rasterization is not supported
+	features->extendedDynamicState3ConservativeRasterizationMode = VK_FALSE;
+	features->extendedDynamicState3ExtraPrimitiveOverestimationSize = VK_FALSE;
+	// VK_EXT_sample_locations is not supported
+	features->extendedDynamicState3SampleLocationsEnable = VK_FALSE;
+	// Various NV extensions that are not supported
+	features->extendedDynamicState3ViewportWScalingEnable = VK_FALSE;
+	features->extendedDynamicState3ViewportSwizzle = VK_FALSE;
+	features->extendedDynamicState3CoverageToColorEnable = VK_FALSE;
+	features->extendedDynamicState3CoverageToColorLocation = VK_FALSE;
+	features->extendedDynamicState3CoverageModulationMode = VK_FALSE;
+	features->extendedDynamicState3CoverageModulationTableEnable = VK_FALSE;
+	features->extendedDynamicState3CoverageModulationTable = VK_FALSE;
+	features->extendedDynamicState3CoverageReductionMode = VK_FALSE;
+	features->extendedDynamicState3RepresentativeFragmentTestEnable = VK_FALSE;
+	features->extendedDynamicState3ShadingRateImageEnable = VK_FALSE;
+}
+
 static void getPhysicalDeviceVertexInputDynamicStateFeaturesEXT(VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT *features)
 {
 	features->vertexInputDynamicState = VK_TRUE;
@@ -641,6 +688,9 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT:
 			getPhysicalDeviceExtendedDynamicState2FeaturesEXT(reinterpret_cast<VkPhysicalDeviceExtendedDynamicState2FeaturesEXT *>(curExtension));
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT:
+			getPhysicalDeviceExtendedDynamicState3FeaturesEXT(reinterpret_cast<VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *>(curExtension));
+			break;
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT:
 			getPhysicalDeviceVertexInputDynamicStateFeaturesEXT(reinterpret_cast<VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT *>(curExtension));
 			break;
@@ -1520,6 +1570,11 @@
 	getHostImageCopyProperties(properties);
 }
 
+void PhysicalDevice::getProperties(VkPhysicalDeviceExtendedDynamicState3PropertiesEXT *properties) const
+{
+	properties->dynamicPrimitiveTopologyUnrestricted = VK_TRUE;
+}
+
 void PhysicalDevice::getProperties(VkPhysicalDeviceVulkan12Properties *properties) const
 {
 	getDriverProperties(properties);
@@ -1597,6 +1652,43 @@
 	       CheckFeature(requested, supported, extendedDynamicState2PatchControlPoints);
 }
 
+bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *requested) const
+{
+	auto supported = getSupportedFeatures(requested);
+
+	return CheckFeature(requested, supported, extendedDynamicState3TessellationDomainOrigin) &&
+	       CheckFeature(requested, supported, extendedDynamicState3DepthClampEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3PolygonMode) &&
+	       CheckFeature(requested, supported, extendedDynamicState3RasterizationSamples) &&
+	       CheckFeature(requested, supported, extendedDynamicState3SampleMask) &&
+	       CheckFeature(requested, supported, extendedDynamicState3AlphaToCoverageEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3AlphaToOneEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3LogicOpEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ColorBlendEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ColorBlendEquation) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ColorWriteMask) &&
+	       CheckFeature(requested, supported, extendedDynamicState3RasterizationStream) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ConservativeRasterizationMode) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ExtraPrimitiveOverestimationSize) &&
+	       CheckFeature(requested, supported, extendedDynamicState3DepthClipEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3SampleLocationsEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ColorBlendAdvanced) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ProvokingVertexMode) &&
+	       CheckFeature(requested, supported, extendedDynamicState3LineRasterizationMode) &&
+	       CheckFeature(requested, supported, extendedDynamicState3LineStippleEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3DepthClipNegativeOneToOne) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ViewportWScalingEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ViewportSwizzle) &&
+	       CheckFeature(requested, supported, extendedDynamicState3CoverageToColorEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3CoverageToColorLocation) &&
+	       CheckFeature(requested, supported, extendedDynamicState3CoverageModulationMode) &&
+	       CheckFeature(requested, supported, extendedDynamicState3CoverageModulationTableEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3CoverageModulationTable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3CoverageReductionMode) &&
+	       CheckFeature(requested, supported, extendedDynamicState3RepresentativeFragmentTestEnable) &&
+	       CheckFeature(requested, supported, extendedDynamicState3ShadingRateImageEnable);
+}
+
 bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceProvokingVertexFeaturesEXT *requested) const
 {
 	auto supported = getSupportedFeatures(requested);
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index a12b8a6..9184362 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -47,6 +47,7 @@
 	bool hasExtendedFeatures(const VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceExtendedDynamicStateFeaturesEXT *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceExtendedDynamicState2FeaturesEXT *features) const;
+	bool hasExtendedFeatures(const VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDevicePrivateDataFeatures *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceTextureCompressionASTCHDRFeatures *features) const;
@@ -98,6 +99,7 @@
 	void getProperties(VkPhysicalDeviceDepthStencilResolveProperties *properties) const;
 	void getProperties(VkPhysicalDeviceCustomBorderColorPropertiesEXT *properties) const;
 	void getProperties(VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT *properties) const;
+	void getProperties(VkPhysicalDeviceExtendedDynamicState3PropertiesEXT *properties) const;
 	void getProperties(VkPhysicalDeviceSubgroupSizeControlProperties *properties) const;
 	void getProperties(VkPhysicalDeviceInlineUniformBlockProperties *properties) const;
 	void getProperties(VkPhysicalDeviceTexelBufferAlignmentProperties *properties) const;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 6e4dad0..1cd4b57 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -483,6 +483,7 @@
 	{ { VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION } },
 	{ { VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_SPEC_VERSION } },
 	{ { VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_SPEC_VERSION } },
+	{ { VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_3_SPEC_VERSION } },
 };
 
 static uint32_t numSupportedExtensions(const ExtensionProperties *extensionProperties, uint32_t extensionPropertiesCount)
@@ -1071,6 +1072,16 @@
 				}
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT:
+			{
+				const auto *dynamicStateFeatures = reinterpret_cast<const VkPhysicalDeviceExtendedDynamicState3FeaturesEXT *>(extensionCreateInfo);
+				bool hasFeatures = vk::Cast(physicalDevice)->hasExtendedFeatures(dynamicStateFeatures);
+				if(!hasFeatures)
+				{
+					return VK_ERROR_FEATURE_NOT_PRESENT;
+				}
+			}
+			break;
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT:
 			{
 				const auto *dynamicStateFeatures = reinterpret_cast<const VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT *>(extensionCreateInfo);
@@ -3127,6 +3138,166 @@
 	UNREACHABLE("extendedDynamicState2PatchControlPoints not supported");
 }
 
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthClampEnableEXT(
+    VkCommandBuffer commandBuffer,
+    VkBool32 depthClampEnable)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 depthClampEnable = %d)", commandBuffer, depthClampEnable);
+
+	vk::Cast(commandBuffer)->setDepthClampEnable(depthClampEnable);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetLogicOpEnableEXT(VkCommandBuffer commandBuffer, VkBool32 logicOpEnable)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 logicOp = %d)", commandBuffer, logicOpEnable);
+
+	UNREACHABLE("extendedDynamicState3LogicOpEnable not supported");
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetPolygonModeEXT(
+    VkCommandBuffer commandBuffer,
+    VkPolygonMode polygonMode)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkPolygonMode polygonMode = %d)", commandBuffer, polygonMode);
+
+	vk::Cast(commandBuffer)->setPolygonMode(polygonMode);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetRasterizationSamplesEXT(
+    VkCommandBuffer commandBuffer,
+    VkSampleCountFlagBits rasterizationSamples)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkSampleCountFlagBits rasterizationSamples = %d)", commandBuffer, rasterizationSamples);
+
+	vk::Cast(commandBuffer)->setRasterizationSamples(rasterizationSamples);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetSampleMaskEXT(
+    VkCommandBuffer commandBuffer,
+    VkSampleCountFlagBits samples,
+    const VkSampleMask *pSampleMask)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkSampleCountFlagBits samples = %d, const VkSampleMask* pSampleMask = %p)", commandBuffer, samples, pSampleMask);
+
+	vk::Cast(commandBuffer)->setSampleMask(samples, pSampleMask);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetAlphaToCoverageEnableEXT(
+    VkCommandBuffer commandBuffer,
+    VkBool32 alphaToCoverageEnable)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 alphaToCoverageEnable = %d)", commandBuffer, alphaToCoverageEnable);
+
+	vk::Cast(commandBuffer)->setAlphaToCoverageEnable(alphaToCoverageEnable);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetAlphaToOneEnableEXT(
+    VkCommandBuffer commandBuffer,
+    VkBool32 alphaToOneEnable)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 alphaToOneEnable = %d)", commandBuffer, alphaToOneEnable);
+
+	UNREACHABLE("extendedDynamicState3AlphaToOneEnable not supported");
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendEnableEXT(
+    VkCommandBuffer commandBuffer,
+    uint32_t firstAttachment,
+    uint32_t attachmentCount,
+    const VkBool32 *pColorBlendEnables)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, uint32_t firstAttachment = %d, uint32_t attachmentCount = %d, const VkBool32* pColorBlendEnables = %p)", commandBuffer, firstAttachment, attachmentCount, pColorBlendEnables);
+
+	vk::Cast(commandBuffer)->setColorBlendEnable(firstAttachment, attachmentCount, pColorBlendEnables);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendEquationEXT(
+    VkCommandBuffer commandBuffer,
+    uint32_t firstAttachment,
+    uint32_t attachmentCount,
+    const VkColorBlendEquationEXT *pColorBlendEquations)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, uint32_t firstAttachment = %d, uint32_t attachmentCount = %d, const VkColorBlendEquationEXT* pColorBlendEquations = %p)", commandBuffer, firstAttachment, attachmentCount, pColorBlendEquations);
+
+	vk::Cast(commandBuffer)->setColorBlendEquation(firstAttachment, attachmentCount, pColorBlendEquations);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWriteMaskEXT(
+    VkCommandBuffer commandBuffer,
+    uint32_t firstAttachment,
+    uint32_t attachmentCount,
+    const VkColorComponentFlags *pColorWriteMasks)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, uint32_t firstAttachment = %d, uint32_t attachmentCount = %d, const VkColorComponentFlags* pColorWriteMasks = %p)", commandBuffer, firstAttachment, attachmentCount, pColorWriteMasks);
+
+	vk::Cast(commandBuffer)->setColorWriteMask(firstAttachment, attachmentCount, pColorWriteMasks);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetTessellationDomainOriginEXT(
+    VkCommandBuffer commandBuffer,
+    VkTessellationDomainOrigin domainOrigin)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkTessellationDomainOrigin domainOrigin = %d)", commandBuffer, domainOrigin);
+
+	UNREACHABLE("extendedDynamicState3TessellationDomainOrigin not supported");
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthClipEnableEXT(
+    VkCommandBuffer commandBuffer,
+    VkBool32 depthClipEnable)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 depthClipEnable = %d)", commandBuffer, depthClipEnable);
+
+	vk::Cast(commandBuffer)->setDepthClipEnable(depthClipEnable);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendAdvancedEXT(
+    VkCommandBuffer commandBuffer,
+    uint32_t firstAttachment,
+    uint32_t attachmentCount,
+    const VkColorBlendAdvancedEXT *pColorBlendAdvanced)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, uint32_t firstAttachment = %d, uint32_t attachmentCount = %d, const VkColorBlendAdvancedEXT* pColorBlendAdvanced = %p)", commandBuffer, firstAttachment, attachmentCount, pColorBlendAdvanced);
+
+	vk::Cast(commandBuffer)->setColorBlendAdvanced(firstAttachment, attachmentCount, pColorBlendAdvanced);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetProvokingVertexModeEXT(
+    VkCommandBuffer commandBuffer,
+    VkProvokingVertexModeEXT provokingVertexMode)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkProvokingVertexModeEXT provokingVertexMode = %d)", commandBuffer, provokingVertexMode);
+
+	vk::Cast(commandBuffer)->setProvokingVertexMode(provokingVertexMode);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetLineRasterizationModeEXT(
+    VkCommandBuffer commandBuffer,
+    VkLineRasterizationModeEXT lineRasterizationMode)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkLineRasterizationModeEXT lineRasterizationMode = %d)", commandBuffer, lineRasterizationMode);
+
+	vk::Cast(commandBuffer)->setLineRasterizationMode(lineRasterizationMode);
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetLineStippleEnableEXT(
+    VkCommandBuffer commandBuffer,
+    VkBool32 stippledLineEnable)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 stippledLineEnable = %d)", commandBuffer, stippledLineEnable);
+
+	UNREACHABLE("extendedDynamicState3LineStippleEnable not supported");
+}
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthClipNegativeOneToOneEXT(
+    VkCommandBuffer commandBuffer,
+    VkBool32 negativeOneToOne)
+{
+	TRACE("(VkCommandBuffer commandBuffer = %p, VkBool32 negativeOneToOne = %d)", commandBuffer, negativeOneToOne);
+
+	vk::Cast(commandBuffer)->setDepthClipNegativeOneToOne(negativeOneToOne);
+}
+
 VKAPI_ATTR void VKAPI_CALL vkCmdSetVertexInputEXT(VkCommandBuffer commandBuffer, uint32_t vertexBindingDescriptionCount,
                                                   const VkVertexInputBindingDescription2EXT *pVertexBindingDescriptions,
                                                   uint32_t vertexAttributeDescriptionCount,
@@ -3960,6 +4131,12 @@
 				vk::Cast(physicalDevice)->getProperties(properties);
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_PROPERTIES_EXT:
+			{
+				auto *properties = reinterpret_cast<VkPhysicalDeviceExtendedDynamicState3PropertiesEXT *>(extensionProperties);
+				vk::Cast(physicalDevice)->getProperties(properties);
+			}
+			break;
 		default:
 			// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
 			UNSUPPORTED("pProperties->pNext sType = %s", vk::Stringify(extensionProperties->sType).c_str());