Dynamic state implementation

Implemented all dynamic state commands and applied
the proper state within the draw command.

Bug b/118619338

Change-Id: Ifeca42be1698f642e137e807aa59958447921fcc
Tests: dEQP-VK.dynamic_state.*
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28890
Tested-by: Alexis Hétu <sugoi@google.com>
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index f3721ef..a7198b7 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -133,6 +133,7 @@
 		rasterizerDiscard = false;
 
 		depthCompareMode = VK_COMPARE_OP_LESS;
+		depthBoundsTestEnable = false;
 		depthBufferEnable = false;
 		depthWriteEnable = false;
 
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index ba9837a..e692762 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -179,6 +179,7 @@
 
 		// Pixel processor states
 		bool rasterizerDiscard;
+		bool depthBoundsTestEnable;
 		bool depthBufferEnable;
 		VkCompareOp depthCompareMode;
 		bool depthWriteEnable;
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index bd3b6f5..0af06da 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -250,6 +250,157 @@
 	const VkIndexType indexType;
 };
 
+struct SetViewport : public CommandBuffer::Command
+{
+	SetViewport(const VkViewport& viewport, uint32_t viewportID) :
+		viewport(viewport), viewportID(viewportID)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		executionState.dynamicState.viewport = viewport;
+	}
+
+	const VkViewport viewport;
+	uint32_t viewportID;
+};
+
+struct SetScissor : public CommandBuffer::Command
+{
+	SetScissor(const VkRect2D& scissor, uint32_t scissorID) :
+		scissor(scissor), scissorID(scissorID)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		executionState.dynamicState.scissor = scissor;
+	}
+
+	const VkRect2D scissor;
+	uint32_t scissorID;
+};
+
+struct SetDepthBias : public CommandBuffer::Command
+{
+	SetDepthBias(float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor) :
+		depthBiasConstantFactor(depthBiasConstantFactor), depthBiasClamp(depthBiasClamp), depthBiasSlopeFactor(depthBiasSlopeFactor)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		executionState.dynamicState.depthBiasConstantFactor = depthBiasConstantFactor;
+		executionState.dynamicState.depthBiasClamp = depthBiasClamp;
+		executionState.dynamicState.depthBiasSlopeFactor = depthBiasSlopeFactor;
+	}
+
+	float depthBiasConstantFactor;
+	float depthBiasClamp;
+	float depthBiasSlopeFactor;
+};
+
+struct SetBlendConstants : public CommandBuffer::Command
+{
+	SetBlendConstants(const float blendConstants[4])
+	{
+		memcpy(this->blendConstants, blendConstants, sizeof(this->blendConstants));
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		memcpy(&(executionState.dynamicState.blendConstants[0]), blendConstants, sizeof(blendConstants));
+	}
+
+	float blendConstants[4];
+};
+
+struct SetDepthBounds : public CommandBuffer::Command
+{
+	SetDepthBounds(float minDepthBounds, float maxDepthBounds) :
+		minDepthBounds(minDepthBounds), maxDepthBounds(maxDepthBounds)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		executionState.dynamicState.minDepthBounds = minDepthBounds;
+		executionState.dynamicState.maxDepthBounds = maxDepthBounds;
+	}
+
+	float minDepthBounds;
+	float maxDepthBounds;
+};
+struct SetStencilCompareMask : public CommandBuffer::Command
+{
+	SetStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t compareMask) :
+		faceMask(faceMask), compareMask(compareMask)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		if(faceMask & VK_STENCIL_FACE_FRONT_BIT)
+		{
+			executionState.dynamicState.compareMask[0] = compareMask;
+		}
+		if(faceMask & VK_STENCIL_FACE_BACK_BIT)
+		{
+			executionState.dynamicState.compareMask[1] = compareMask;
+		}
+	}
+
+	VkStencilFaceFlags faceMask;
+	uint32_t compareMask;
+};
+
+struct SetStencilWriteMask : public CommandBuffer::Command
+{
+	SetStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t writeMask) :
+		faceMask(faceMask), writeMask(writeMask)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		if(faceMask & VK_STENCIL_FACE_FRONT_BIT)
+		{
+			executionState.dynamicState.writeMask[0] = writeMask;
+		}
+		if(faceMask & VK_STENCIL_FACE_BACK_BIT)
+		{
+			executionState.dynamicState.writeMask[1] = writeMask;
+		}
+	}
+
+	VkStencilFaceFlags faceMask;
+	uint32_t writeMask;
+};
+
+struct SetStencilReference : public CommandBuffer::Command
+{
+	SetStencilReference(VkStencilFaceFlags faceMask, uint32_t reference) :
+		faceMask(faceMask), reference(reference)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState) override
+	{
+		if(faceMask & VK_STENCIL_FACE_FRONT_BIT)
+		{
+			executionState.dynamicState.reference[0] = reference;
+		}
+		if(faceMask & VK_STENCIL_FACE_BACK_BIT)
+		{
+			executionState.dynamicState.reference[1] = reference;
+		}
+	}
+
+	VkStencilFaceFlags faceMask;
+	uint32_t reference;
+};
+
 void CommandBuffer::ExecutionState::bindVertexInputs(sw::Context& context, int firstVertex, int firstInstance)
 {
 	for(uint32_t i = 0; i < MAX_VERTEX_INPUT_BINDINGS; i++)
@@ -319,16 +470,52 @@
 		context.descriptorDynamicOffsets = pipelineState.descriptorDynamicOffsets;
 		context.pushConstants = executionState.pushConstants;
 
-		if (indexed)
+		if(indexed)
 		{
 			context.indexBuffer = Cast(executionState.indexBufferBinding.buffer)->getOffsetPointer(
 					executionState.indexBufferBinding.offset + first * bytesPerIndex(executionState));
 		}
 
+		// Apply either pipeline state or dynamic state
+		executionState.renderer->setScissor(pipeline->hasDynamicState(VK_DYNAMIC_STATE_SCISSOR) ?
+		                                    executionState.dynamicState.scissor : pipeline->getScissor());
+		executionState.renderer->setViewport(pipeline->hasDynamicState(VK_DYNAMIC_STATE_VIEWPORT) ?
+		                                     executionState.dynamicState.viewport : pipeline->getViewport());
+		executionState.renderer->setBlendConstant(pipeline->hasDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS) ?
+		                                          executionState.dynamicState.blendConstants : pipeline->getBlendConstants());
+		if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS))
+		{
+			// If the depth bias clamping feature is not enabled, depthBiasClamp must be 0.0
+			ASSERT(executionState.dynamicState.depthBiasClamp == 0.0f);
+
+			context.depthBias = executionState.dynamicState.depthBiasConstantFactor;
+			context.slopeDepthBias = executionState.dynamicState.depthBiasSlopeFactor;
+		}
+		if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BOUNDS) && context.depthBoundsTestEnable)
+		{
+			// Unless the VK_EXT_depth_range_unrestricted extension is enabled minDepthBounds and maxDepthBounds must be between 0.0 and 1.0, inclusive
+			ASSERT(executionState.dynamicState.minDepthBounds >= 0.0f && executionState.dynamicState.minDepthBounds <= 1.0f);
+			ASSERT(executionState.dynamicState.maxDepthBounds >= 0.0f && executionState.dynamicState.maxDepthBounds <= 1.0f);
+
+			UNIMPLEMENTED("depthBoundsTestEnable");
+		}
+		if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK) && context.stencilEnable)
+		{
+			context.frontStencil.compareMask = executionState.dynamicState.compareMask[0];
+			context.backStencil.compareMask = executionState.dynamicState.compareMask[1];
+		}
+		if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK) && context.stencilEnable)
+		{
+			context.frontStencil.writeMask = executionState.dynamicState.writeMask[0];
+			context.backStencil.writeMask = executionState.dynamicState.writeMask[1];
+		}
+		if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE) && context.stencilEnable)
+		{
+			context.frontStencil.reference = executionState.dynamicState.reference[0];
+			context.backStencil.reference = executionState.dynamicState.reference[1];
+		}
+
 		executionState.renderer->setContext(context);
-		executionState.renderer->setScissor(pipeline->getScissor());
-		executionState.renderer->setViewport(pipeline->getViewport());
-		executionState.renderer->setBlendConstant(pipeline->getBlendConstants());
 
 		executionState.bindAttachments();
 
@@ -1008,85 +1195,73 @@
 
 void CommandBuffer::setViewport(uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_VIEWPORT dynamic state enabled
-	UNIMPLEMENTED("setViewport");
+	if(firstViewport != 0 || viewportCount > 1)
+	{
+		UNIMPLEMENTED("viewport");
+	}
+
+	for(uint32_t i = 0; i < viewportCount; i++)
+	{
+		addCommand<SetViewport>(pViewports[i], i + firstViewport);
+	}
 }
 
 void CommandBuffer::setScissor(uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_SCISSOR dynamic state enabled
-	UNIMPLEMENTED("setScissor");
+	if(firstScissor != 0 || scissorCount > 1)
+	{
+		UNIMPLEMENTED("scissor");
+	}
+
+	for(uint32_t i = 0; i < scissorCount; i++)
+	{
+		addCommand<SetScissor>(pScissors[i], i + firstScissor);
+	}
 }
 
 void CommandBuffer::setLineWidth(float lineWidth)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_LINE_WIDTH dynamic state enabled
-
 	// If the wide lines feature is not enabled, lineWidth must be 1.0
 	ASSERT(lineWidth == 1.0f);
-
-	UNIMPLEMENTED("setLineWidth");
 }
 
 void CommandBuffer::setDepthBias(float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_DEPTH_BIAS dynamic state enabled
-
-	// If the depth bias clamping feature is not enabled, depthBiasClamp must be 0.0
-	ASSERT(depthBiasClamp == 0.0f);
-
-	UNIMPLEMENTED("setDepthBias");
+	addCommand<SetDepthBias>(depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor);
 }
 
 void CommandBuffer::setBlendConstants(const float blendConstants[4])
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_BLEND_CONSTANTS dynamic state enabled
-
-	// blendConstants is an array of four values specifying the R, G, B, and A components
-	// of the blend constant color used in blending, depending on the blend factor.
-
-	UNIMPLEMENTED("setBlendConstants");
+	addCommand<SetBlendConstants>(blendConstants);
 }
 
 void CommandBuffer::setDepthBounds(float minDepthBounds, float maxDepthBounds)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_DEPTH_BOUNDS dynamic state enabled
-
-	// Unless the VK_EXT_depth_range_unrestricted extension is enabled minDepthBounds and maxDepthBounds must be between 0.0 and 1.0, inclusive
-	ASSERT(minDepthBounds >= 0.0f && minDepthBounds <= 1.0f);
-	ASSERT(maxDepthBounds >= 0.0f && maxDepthBounds <= 1.0f);
-
-	UNIMPLEMENTED("setDepthBounds");
+	addCommand<SetDepthBounds>(minDepthBounds, maxDepthBounds);
 }
 
 void CommandBuffer::setStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t compareMask)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK dynamic state enabled
-
 	// faceMask must not be 0
 	ASSERT(faceMask != 0);
 
-	UNIMPLEMENTED("setStencilCompareMask");
+	addCommand<SetStencilCompareMask>(faceMask, compareMask);
 }
 
 void CommandBuffer::setStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t writeMask)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_STENCIL_WRITE_MASK dynamic state enabled
-
 	// faceMask must not be 0
 	ASSERT(faceMask != 0);
 
-	UNIMPLEMENTED("setStencilWriteMask");
+	addCommand<SetStencilWriteMask>(faceMask, writeMask);
 }
 
 void CommandBuffer::setStencilReference(VkStencilFaceFlags faceMask, uint32_t reference)
 {
-	// Note: The bound graphics pipeline must have been created with the VK_DYNAMIC_STATE_STENCIL_REFERENCE dynamic state enabled
-
 	// faceMask must not be 0
 	ASSERT(faceMask != 0);
 
-	UNIMPLEMENTED("setStencilReference");
+	addCommand<SetStencilReference>(faceMask, reference);
 }
 
 void CommandBuffer::bindDescriptorSets(VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout vkLayout,
diff --git a/src/Vulkan/VkCommandBuffer.hpp b/src/Vulkan/VkCommandBuffer.hpp
index 39b4138..0aeeb96 100644
--- a/src/Vulkan/VkCommandBuffer.hpp
+++ b/src/Vulkan/VkCommandBuffer.hpp
@@ -135,6 +135,24 @@
 		RenderPass* renderPass = nullptr;
 		Framebuffer* renderPassFramebuffer = nullptr;
 		std::array<PipelineState, VK_PIPELINE_BIND_POINT_RANGE_SIZE> pipelineState;
+
+		struct DynamicState
+		{
+			VkViewport viewport;
+			VkRect2D scissor;
+			sw::Color<float> blendConstants;
+			float depthBiasConstantFactor = 0.0f;
+			float depthBiasClamp = 0.0f;
+			float depthBiasSlopeFactor = 0.0f;
+			float minDepthBounds = 0.0f;
+			float maxDepthBounds = 0.0f;
+
+			uint32_t compareMask[2] = { 0 };
+			uint32_t writeMask[2] = { 0 };
+			uint32_t reference[2] = { 0 };
+		};
+		DynamicState dynamicState;
+
 		sw::PushConstantStorage pushConstants;
 
 		struct VertexInputBinding
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index b9b8610..815923f 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -231,12 +231,36 @@
 {
 	if(((pCreateInfo->flags & ~(VK_PIPELINE_CREATE_DERIVATIVE_BIT | VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT)) != 0) ||
 	   (pCreateInfo->stageCount != 2) ||
-	   (pCreateInfo->pTessellationState != nullptr) ||
-	   (pCreateInfo->pDynamicState != nullptr))
+	   (pCreateInfo->pTessellationState != nullptr))
 	{
 		UNIMPLEMENTED("pCreateInfo settings");
 	}
 
+	if(pCreateInfo->pDynamicState)
+	{
+		for(uint32_t i = 0; i < pCreateInfo->pDynamicState->dynamicStateCount; i++)
+		{
+			VkDynamicState dynamicState = pCreateInfo->pDynamicState->pDynamicStates[i];
+			switch(dynamicState)
+			{
+			case VK_DYNAMIC_STATE_VIEWPORT:
+			case VK_DYNAMIC_STATE_SCISSOR:
+			case VK_DYNAMIC_STATE_LINE_WIDTH:
+			case VK_DYNAMIC_STATE_DEPTH_BIAS:
+			case VK_DYNAMIC_STATE_BLEND_CONSTANTS:
+			case VK_DYNAMIC_STATE_DEPTH_BOUNDS:
+			case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK:
+			case VK_DYNAMIC_STATE_STENCIL_WRITE_MASK:
+			case VK_DYNAMIC_STATE_STENCIL_REFERENCE:
+				ASSERT(dynamicState < (sizeof(dynamicStateFlags) * 8));
+				dynamicStateFlags |= (1 << dynamicState);
+				break;
+			default:
+				UNIMPLEMENTED("dynamic state");
+			}
+		}
+	}
+
 	const VkPipelineVertexInputStateCreateInfo* vertexInputState = pCreateInfo->pVertexInputState;
 	if(vertexInputState->flags != 0)
 	{
@@ -346,6 +370,7 @@
 			UNIMPLEMENTED("depthStencilState");
 		}
 
+		context.depthBoundsTestEnable = depthStencilState->depthBoundsTestEnable;
 		context.depthBufferEnable = depthStencilState->depthTestEnable;
 		context.depthWriteEnable = depthStencilState->depthWriteEnable;
 		context.depthCompareMode = depthStencilState->depthCompareOp;
@@ -480,6 +505,11 @@
 	return blendConstants;
 }
 
+bool GraphicsPipeline::hasDynamicState(VkDynamicState dynamicState) const
+{
+	return (dynamicStateFlags & (1 << dynamicState)) != 0;
+}
+
 ComputePipeline::ComputePipeline(const VkComputePipelineCreateInfo* pCreateInfo, void* mem)
 	: Pipeline(Cast(pCreateInfo->layout))
 {
diff --git a/src/Vulkan/VkPipeline.hpp b/src/Vulkan/VkPipeline.hpp
index b220027..bf6092a 100644
--- a/src/Vulkan/VkPipeline.hpp
+++ b/src/Vulkan/VkPipeline.hpp
@@ -75,11 +75,13 @@
 	const VkRect2D& getScissor() const;
 	const VkViewport& getViewport() const;
 	const sw::Color<float>& getBlendConstants() const;
+	bool hasDynamicState(VkDynamicState dynamicState) const;
 
 private:
 	sw::SpirvShader *vertexShader = nullptr;
 	sw::SpirvShader *fragmentShader = nullptr;
 
+	uint32_t dynamicStateFlags = 0;
 	sw::Context context;
 	VkRect2D scissor;
 	VkViewport viewport;