Split spirv_assembly tests workload

The dEQP-VK.spirv_assembly.* tests should allow the render workload to
be split into multiple submissions if required to prevent GPU timeouts
encountered in some tests.

An additional flag is added to allow specific test groups to request
that the test workload is split.

Affects: dEQP-VK.spirv_assembly.*

VK-GL-CTS Issue: 2294

Components: Vulkan

Change-Id: I9295f675c06b3b966d89afae98334ebb5c9530c8
(cherry picked from commit 789c733eef56bd443692d751431177eb4fb2519f)
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
index ab2292e..9879a0c 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
@@ -275,6 +275,7 @@
 	, failResult					(QP_TEST_RESULT_FAIL)
 	, failMessageTemplate			("${reason}")
 	, renderFullSquare				(false)
+	, splitRenderArea				(false)
 {
 	inputColors[0]		= inputs[0];
 	inputColors[1]		= inputs[1];
@@ -302,6 +303,7 @@
 	, failResult					(other.failResult)
 	, failMessageTemplate			(other.failMessageTemplate)
 	, renderFullSquare				(other.renderFullSquare)
+	, splitRenderArea				(other.splitRenderArea)
 {
 	inputColors[0]		= other.inputColors[0];
 	inputColors[1]		= other.inputColors[1];
@@ -2470,7 +2472,9 @@
 	Allocator&									allocator				= context.getDefaultAllocator();
 	vector<ModuleHandleSp>						modules;
 	map<VkShaderStageFlagBits, VkShaderModule>	moduleByStage;
-	const tcu::UVec2							renderSize				(256, 256);
+	const deUint32								fullRenderSize			= 256;
+	const deUint32								quarterRenderSize		= 64;
+	const tcu::UVec2							renderSize				(fullRenderSize, fullRenderSize);
 	const int									testSpecificSeed		= 31354125;
 	const int									seed					= context.getTestContext().getCommandLine().getBaseSeed() ^ testSpecificSeed;
 	bool										supportsGeometry		= false;
@@ -2482,6 +2486,10 @@
 	const bool									needInterface			= !instance.interfaces.empty();
 	const VkPhysicalDeviceFeatures&				features				= context.getDeviceFeatures();
 	const Vec4									defaulClearColor		(0.125f, 0.25f, 0.75f, 1.0f);
+	bool										splitRenderArea			= instance.splitRenderArea;
+
+	const deUint32								renderDimension			= splitRenderArea ? quarterRenderSize : fullRenderSize;
+	const int									numRenderSegments		= splitRenderArea ? 4 : 1;
 
 	supportsGeometry		= features.geometryShader == VK_TRUE;
 	supportsTessellation	= features.tessellationShader == VK_TRUE;
@@ -3419,7 +3427,7 @@
 		+1.0f,														//	float				maxDepthBounds;
 	};
 	const VkViewport							viewport0				= makeViewport(renderSize);
-	const VkRect2D								scissor0				= makeRect2D(renderSize);
+	const VkRect2D								scissor0				= makeRect2D(0u, 0u);
 	const VkPipelineViewportStateCreateInfo		viewportParams			=
 	{
 		VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,		//	VkStructureType		sType;
@@ -3583,6 +3591,20 @@
 		3u
 	};
 
+	const	VkDynamicState							dynamicStates[]				=
+	{
+		VK_DYNAMIC_STATE_SCISSOR
+	};
+
+	const VkPipelineDynamicStateCreateInfo			dynamicStateCreateInfo =
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,  // sType
+		DE_NULL,											   // pNext
+		0u,													   // flags
+		DE_LENGTH_OF_ARRAY(dynamicStates),					   // dynamicStateCount
+		dynamicStates										   // pDynamicStates
+	};
+
 	const VkPipelineTessellationStateCreateInfo* tessellationInfo	=	hasTessellation ? &tessellationState: DE_NULL;
 	const VkGraphicsPipelineCreateInfo		pipelineParams			=
 	{
@@ -3599,7 +3621,7 @@
 		&multisampleParams,										//	const VkPipelineMultisampleStateCreateInfo*		pMultisampleState;
 		&depthStencilParams,									//	const VkPipelineDepthStencilStateCreateInfo*	pDepthStencilState;
 		&blendParams,											//	const VkPipelineColorBlendStateCreateInfo*		pColorBlendState;
-		(const VkPipelineDynamicStateCreateInfo*)DE_NULL,		//	const VkPipelineDynamicStateCreateInfo*			pDynamicState;
+		&dynamicStateCreateInfo,								//	const VkPipelineDynamicStateCreateInfo*			pDynamicState;
 		*pipelineLayout,										//	VkPipelineLayout								layout;
 		*renderPass,											//	VkRenderPass									renderPass;
 		0u,														//	deUint32										subpass;
@@ -3656,228 +3678,248 @@
 
 	const Unique<VkFramebuffer>				framebuffer				(createFramebuffer(vk, device, &framebufferParams));
 
-	// Record commands
-	beginCommandBuffer(vk, *cmdBuf);
+	bool firstPass = true;
 
+	for (int x = 0; x < numRenderSegments; x++)
 	{
-		const VkMemoryBarrier			vertFlushBarrier	=
+		for (int y = 0; y < numRenderSegments; y++)
 		{
-			VK_STRUCTURE_TYPE_MEMORY_BARRIER,			//	VkStructureType		sType;
-			DE_NULL,									//	const void*			pNext;
-			VK_ACCESS_HOST_WRITE_BIT,					//	VkMemoryOutputFlags	outputMask;
-			VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,		//	VkMemoryInputFlags	inputMask;
-		};
-		vector<VkImageMemoryBarrier>	colorAttBarriers;
+			// Record commands
+			beginCommandBuffer(vk, *cmdBuf);
 
-		VkImageMemoryBarrier			imgBarrier          =
-		{
-			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
-			DE_NULL,									//	const void*				pNext;
-			0u,											//	VkMemoryOutputFlags		outputMask;
-			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryInputFlags		inputMask;
-			VK_IMAGE_LAYOUT_UNDEFINED,					//	VkImageLayout			oldLayout;
-			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			newLayout;
-			queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
-			queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
-			*image,										//	VkImage					image;
 			{
-				VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspect	aspect;
-				0u,											//	deUint32		baseMipLevel;
-				1u,											//	deUint32		mipLevels;
-				0u,											//	deUint32		baseArraySlice;
-				1u,											//	deUint32		arraySize;
-			}											//	VkImageSubresourceRange	subresourceRange;
-		};
-		colorAttBarriers.push_back(imgBarrier);
-		if (needInterface)
-		{
-			imgBarrier.image = *fragOutputImage;
-			colorAttBarriers.push_back(imgBarrier);
-			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 2, colorAttBarriers.data());
-		}
-		else
-		{
-			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, colorAttBarriers.data());
-		}
-	}
+				const VkMemoryBarrier			vertFlushBarrier	=
+				{
+					VK_STRUCTURE_TYPE_MEMORY_BARRIER,			//	VkStructureType		sType;
+					DE_NULL,									//	const void*			pNext;
+					VK_ACCESS_HOST_WRITE_BIT,					//	VkMemoryOutputFlags	outputMask;
+					VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,		//	VkMemoryInputFlags	inputMask;
+				};
+				vector<VkImageMemoryBarrier>	colorAttBarriers;
 
-	{
-		vector<VkClearValue>			clearValue;
-		clearValue.push_back(makeClearValueColorF32(defaulClearColor[0], defaulClearColor[1], defaulClearColor[2], defaulClearColor[3]));
-		if (needInterface)
-		{
-			clearValue.push_back(makeClearValueColorU32(0, 0, 0, 0));
-		}
-		beginRenderPass(vk, *cmdBuf, *renderPass, *framebuffer, makeRect2D(0, 0, renderSize.x(), renderSize.y()), (deUint32)clearValue.size(), clearValue.data());
-	}
+				VkImageMemoryBarrier			imgBarrier          =
+				{
+					VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
+					DE_NULL,									//	const void*				pNext;
+					0u,											//	VkMemoryOutputFlags		outputMask;
+					VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryInputFlags		inputMask;
+					VK_IMAGE_LAYOUT_UNDEFINED,					//	VkImageLayout			oldLayout;
+					VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			newLayout;
+					queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
+					queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
+					*image,										//	VkImage					image;
+					{
+						VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspect	aspect;
+						0u,											//	deUint32		baseMipLevel;
+						1u,											//	deUint32		mipLevels;
+						0u,											//	deUint32		baseArraySlice;
+						1u,											//	deUint32		arraySize;
+					}											//	VkImageSubresourceRange	subresourceRange;
+				};
+				colorAttBarriers.push_back(imgBarrier);
+				if (needInterface)
+				{
+					imgBarrier.image = *fragOutputImage;
+					colorAttBarriers.push_back(imgBarrier);
+					vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 2, colorAttBarriers.data());
+				}
+				else
+				{
+					vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, colorAttBarriers.data());
+				}
+			}
 
-	vk.cmdBindPipeline(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
-	{
-		const VkDeviceSize bindingOffset = 0;
-		vk.cmdBindVertexBuffers(*cmdBuf, 0u, 1u, &vertexBuffer.get(), &bindingOffset);
-	}
-	if (needInterface)
-	{
-		const VkDeviceSize bindingOffset = 0;
-		vk.cmdBindVertexBuffers(*cmdBuf, 1u, 1u, &vertexInputBuffer.get(), &bindingOffset);
-	}
-	if (hasPushConstants)
-	{
-		vector<deUint8> pushConstantsBytes;
-		instance.pushConstants.getBuffer()->getBytes(pushConstantsBytes);
-
-		const deUint32	size	= static_cast<deUint32>(pushConstantsBytes.size());
-		const void*		data	= &pushConstantsBytes.front();
-
-		vk.cmdPushConstants(*cmdBuf, *pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, size, data);
-	}
-	if (numResources != 0)
-	{
-		// Bind to set number 0.
-		vk.cmdBindDescriptorSets(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0, 1, &rawSet, 0, DE_NULL);
-	}
-	vk.cmdDraw(*cmdBuf, deUint32(vertexCount), 1u /*run pipeline once*/, 0u /*first vertex*/, 0u /*first instanceIndex*/);
-	endRenderPass(vk, *cmdBuf);
-
-	{
-		vector<VkImageMemoryBarrier>	renderFinishBarrier;
-		VkImageMemoryBarrier			imgBarrier				=
-		{
-			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
-			DE_NULL,									//	const void*				pNext;
-			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryOutputFlags		outputMask;
-			VK_ACCESS_TRANSFER_READ_BIT,				//	VkMemoryInputFlags		inputMask;
-			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			oldLayout;
-			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,		//	VkImageLayout			newLayout;
-			queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
-			queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
-			*image,										//	VkImage					image;
 			{
-				VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspectFlags	aspectMask;
-				0u,											//	deUint32			baseMipLevel;
-				1u,											//	deUint32			mipLevels;
-				0u,											//	deUint32			baseArraySlice;
-				1u,											//	deUint32			arraySize;
-			}											//	VkImageSubresourceRange	subresourceRange;
-		};
-		renderFinishBarrier.push_back(imgBarrier);
+				vector<VkClearValue>			clearValue;
+				clearValue.push_back(makeClearValueColorF32(defaulClearColor[0], defaulClearColor[1], defaulClearColor[2], defaulClearColor[3]));
+				if (needInterface)
+				{
+					clearValue.push_back(makeClearValueColorU32(0, 0, 0, 0));
+				}
 
-		if (needInterface)
-		{
-			imgBarrier.image = *fragOutputImage;
-			renderFinishBarrier.push_back(imgBarrier);
-			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 2, renderFinishBarrier.data());
-		}
-		else
-		{
-			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, renderFinishBarrier.data());
-		}
-	}
 
-	{
-		const VkBufferImageCopy	copyParams	=
-		{
-			(VkDeviceSize)0u,						//	VkDeviceSize			bufferOffset;
-			(deUint32)renderSize.x(),				//	deUint32				bufferRowLength;
-			(deUint32)renderSize.y(),				//	deUint32				bufferImageHeight;
+				vk::VkRect2D scissor = makeRect2D(x * renderDimension, y * renderDimension, renderDimension, renderDimension);
+				vk.cmdSetScissor(*cmdBuf, 0u, 1u, &scissor);
+
+				beginRenderPass(vk, *cmdBuf, *renderPass, *framebuffer, scissor, (deUint32)clearValue.size(), clearValue.data());
+			}
+
+			vk.cmdBindPipeline(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
 			{
-				VK_IMAGE_ASPECT_COLOR_BIT,				//	VkImageAspect		aspect;
-				0u,										//	deUint32			mipLevel;
-				0u,										//	deUint32			arrayLayer;
-				1u,										//	deUint32			arraySize;
-			},										//	VkImageSubresourceCopy	imageSubresource;
-			{ 0u, 0u, 0u },							//	VkOffset3D				imageOffset;
-			{ renderSize.x(), renderSize.y(), 1u }	//	VkExtent3D				imageExtent;
-		};
-		vk.cmdCopyImageToBuffer(*cmdBuf, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *readImageBuffer, 1u, &copyParams);
+				const VkDeviceSize bindingOffset = 0;
+				vk.cmdBindVertexBuffers(*cmdBuf, 0u, 1u, &vertexBuffer.get(), &bindingOffset);
+			}
+			if (needInterface)
+			{
+				const VkDeviceSize bindingOffset = 0;
+				vk.cmdBindVertexBuffers(*cmdBuf, 1u, 1u, &vertexInputBuffer.get(), &bindingOffset);
+			}
+			if (hasPushConstants)
+			{
+				vector<deUint8> pushConstantsBytes;
+				instance.pushConstants.getBuffer()->getBytes(pushConstantsBytes);
 
-		if (needInterface)
-		{
-			vk.cmdCopyImageToBuffer(*cmdBuf, *fragOutputImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *fragOutputBuffer, 1u, &copyParams);
+				const deUint32	size	= static_cast<deUint32>(pushConstantsBytes.size());
+				const void*		data	= &pushConstantsBytes.front();
+
+				vk.cmdPushConstants(*cmdBuf, *pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, size, data);
+			}
+			if (numResources != 0)
+			{
+				// Bind to set number 0.
+				vk.cmdBindDescriptorSets(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0, 1, &rawSet, 0, DE_NULL);
+			}
+			vk.cmdDraw(*cmdBuf, deUint32(vertexCount), 1u /*run pipeline once*/, 0u /*first vertex*/, 0u /*first instanceIndex*/);
+			endRenderPass(vk, *cmdBuf);
+
+			{
+				vector<VkImageMemoryBarrier>	renderFinishBarrier;
+				VkImageMemoryBarrier			imgBarrier				=
+				{
+					VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
+					DE_NULL,									//	const void*				pNext;
+					VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryOutputFlags		outputMask;
+					VK_ACCESS_TRANSFER_READ_BIT,				//	VkMemoryInputFlags		inputMask;
+					VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			oldLayout;
+					VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,		//	VkImageLayout			newLayout;
+					queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
+					queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
+					*image,										//	VkImage					image;
+					{
+						VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspectFlags	aspectMask;
+						0u,											//	deUint32			baseMipLevel;
+						1u,											//	deUint32			mipLevels;
+						0u,											//	deUint32			baseArraySlice;
+						1u,											//	deUint32			arraySize;
+					}											//	VkImageSubresourceRange	subresourceRange;
+				};
+				renderFinishBarrier.push_back(imgBarrier);
+
+				if (needInterface)
+				{
+					imgBarrier.image = *fragOutputImage;
+					renderFinishBarrier.push_back(imgBarrier);
+					vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 2, renderFinishBarrier.data());
+				}
+				else
+				{
+					vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, renderFinishBarrier.data());
+				}
+			}
+
+			if ( x ==  numRenderSegments -1 && y == numRenderSegments - 1)
+			{
+				{
+					const VkBufferImageCopy	copyParams	=
+					{
+						(VkDeviceSize)0u,						//	VkDeviceSize			bufferOffset;
+						(deUint32)renderSize.x(),				//	deUint32				bufferRowLength;
+						(deUint32)renderSize.y(),				//	deUint32				bufferImageHeight;
+						{
+							VK_IMAGE_ASPECT_COLOR_BIT,				//	VkImageAspect		aspect;
+							0u,										//	deUint32			mipLevel;
+							0u,										//	deUint32			arrayLayer;
+							1u,										//	deUint32			arraySize;
+						},										//	VkImageSubresourceCopy	imageSubresource;
+						{ 0u, 0u, 0u },							//	VkOffset3D				imageOffset;
+						{ renderSize.x(), renderSize.y(), 1u }
+					};
+					vk.cmdCopyImageToBuffer(*cmdBuf, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *readImageBuffer, 1u, &copyParams);
+
+					if (needInterface)
+					{
+					vk.cmdCopyImageToBuffer(*cmdBuf, *fragOutputImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *fragOutputBuffer, 1u, &copyParams);
+					}
+				}
+
+				{
+					vector<VkBufferMemoryBarrier>	cpFinishBarriers;
+					VkBufferMemoryBarrier			copyFinishBarrier	=
+					{
+						VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,	//	VkStructureType		sType;
+						DE_NULL,									//	const void*			pNext;
+						VK_ACCESS_TRANSFER_WRITE_BIT,				//	VkMemoryOutputFlags	outputMask;
+						VK_ACCESS_HOST_READ_BIT,					//	VkMemoryInputFlags	inputMask;
+						queueFamilyIndex,							//	deUint32			srcQueueFamilyIndex;
+						queueFamilyIndex,							//	deUint32			destQueueFamilyIndex;
+						*readImageBuffer,							//	VkBuffer			buffer;
+						0u,											//	VkDeviceSize		offset;
+						imageSizeBytes								//	VkDeviceSize		size;
+					};
+					cpFinishBarriers.push_back(copyFinishBarrier);
+
+					if (needInterface)
+					{
+						copyFinishBarrier.buffer	= *fragOutputBuffer;
+						copyFinishBarrier.size		= VK_WHOLE_SIZE;
+						cpFinishBarriers.push_back(copyFinishBarrier);
+
+						vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 2, cpFinishBarriers.data(), 0, (const VkImageMemoryBarrier*)DE_NULL);
+					}
+					else
+					{
+						vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, cpFinishBarriers.data(), 0, (const VkImageMemoryBarrier*)DE_NULL);
+					}
+				}
+				}
+
+			endCommandBuffer(vk, *cmdBuf);
+
+			if (firstPass)
+			{
+				// Upload vertex data
+				{
+					void* vertexBufPtr = vertexBufferMemory->getHostPtr();
+					deMemcpy(vertexBufPtr, &vertexData[0], vertexDataSize);
+					flushAlloc(vk, device, *vertexBufferMemory);
+				}
+
+				if (needInterface)
+				{
+					vector<deUint8> inputBufferBytes;
+					instance.interfaces.getInputBuffer()->getBytes(inputBufferBytes);
+
+					const deUint32				typNumBytes		= instance.interfaces.getInputType().getNumBytes();
+					const deUint32				bufNumBytes		= static_cast<deUint32>(inputBufferBytes.size());
+
+					// Require that the test instantation provides four output values.
+					DE_ASSERT(bufNumBytes == 4 * typNumBytes);
+
+					// We have four triangles. Because interpolation happens before executing the fragment shader,
+					// we need to provide the same vertex attribute for the same triangle. That means, duplicate each
+					// value three times for all four values.
+
+					const deUint8*				provided		= static_cast<const deUint8*>(&inputBufferBytes.front());
+					vector<deUint8>				data;
+
+					data.reserve(3 * bufNumBytes);
+
+					for (deUint32 offset = 0; offset < bufNumBytes; offset += typNumBytes)
+						for (deUint32 vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
+							for (deUint32 byteNdx = 0; byteNdx < typNumBytes; ++byteNdx)
+								data.push_back(provided[offset + byteNdx]);
+
+					deMemcpy(vertexInputMemory->getHostPtr(), data.data(), data.size());
+
+					const VkMappedMemoryRange	range			=
+					{
+						VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
+						DE_NULL,								//	const void*		pNext;
+						vertexInputMemory->getMemory(),			//	VkDeviceMemory	mem;
+						0,										//	VkDeviceSize	offset;
+						VK_WHOLE_SIZE,							//	VkDeviceSize	size;
+					};
+
+					VK_CHECK(vk.flushMappedMemoryRanges(device, 1u, &range));
+				}
+				firstPass = false;
+			}
+
+			// Submit & wait for completion
+			submitCommandsAndWait(vk, device, queue, cmdBuf.get());
 		}
 	}
 
-	{
-		vector<VkBufferMemoryBarrier> cpFinishBarriers;
-		VkBufferMemoryBarrier			copyFinishBarrier	=
-		{
-			VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,	//	VkStructureType		sType;
-			DE_NULL,									//	const void*			pNext;
-			VK_ACCESS_TRANSFER_WRITE_BIT,				//	VkMemoryOutputFlags	outputMask;
-			VK_ACCESS_HOST_READ_BIT,					//	VkMemoryInputFlags	inputMask;
-			queueFamilyIndex,							//	deUint32			srcQueueFamilyIndex;
-			queueFamilyIndex,							//	deUint32			destQueueFamilyIndex;
-			*readImageBuffer,							//	VkBuffer			buffer;
-			0u,											//	VkDeviceSize		offset;
-			imageSizeBytes								//	VkDeviceSize		size;
-		};
-		cpFinishBarriers.push_back(copyFinishBarrier);
-
-		if (needInterface)
-		{
-			copyFinishBarrier.buffer	= *fragOutputBuffer;
-			copyFinishBarrier.size		= VK_WHOLE_SIZE;
-			cpFinishBarriers.push_back(copyFinishBarrier);
-
-			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 2, cpFinishBarriers.data(), 0, (const VkImageMemoryBarrier*)DE_NULL);
-		}
-		else
-		{
-			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, cpFinishBarriers.data(), 0, (const VkImageMemoryBarrier*)DE_NULL);
-		}
-	}
-
-	endCommandBuffer(vk, *cmdBuf);
-
-	// Upload vertex data
-	{
-		void* vertexBufPtr = vertexBufferMemory->getHostPtr();
-		deMemcpy(vertexBufPtr, &vertexData[0], vertexDataSize);
-		flushAlloc(vk, device, *vertexBufferMemory);
-	}
-
-	if (needInterface)
-	{
-		vector<deUint8> inputBufferBytes;
-		instance.interfaces.getInputBuffer()->getBytes(inputBufferBytes);
-
-		const deUint32				typNumBytes		= instance.interfaces.getInputType().getNumBytes();
-		const deUint32				bufNumBytes		= static_cast<deUint32>(inputBufferBytes.size());
-
-		// Require that the test instantation provides four output values.
-		DE_ASSERT(bufNumBytes == 4 * typNumBytes);
-
-		// We have four triangles. Because interpolation happens before executing the fragment shader,
-		// we need to provide the same vertex attribute for the same triangle. That means, duplicate each
-		// value three times for all four values.
-
-		const deUint8*				provided		= static_cast<const deUint8*>(&inputBufferBytes.front());
-		vector<deUint8>				data;
-
-		data.reserve(3 * bufNumBytes);
-
-		for (deUint32 offset = 0; offset < bufNumBytes; offset += typNumBytes)
-			for (deUint32 vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
-				for (deUint32 byteNdx = 0; byteNdx < typNumBytes; ++byteNdx)
-					data.push_back(provided[offset + byteNdx]);
-
-		deMemcpy(vertexInputMemory->getHostPtr(), data.data(), data.size());
-
-		const VkMappedMemoryRange	range			=
-		{
-			VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
-			DE_NULL,								//	const void*		pNext;
-			vertexInputMemory->getMemory(),			//	VkDeviceMemory	mem;
-			0,										//	VkDeviceSize	offset;
-			VK_WHOLE_SIZE,							//	VkDeviceSize	size;
-		};
-
-		VK_CHECK(vk.flushMappedMemoryRanges(device, 1u, &range));
-	}
-
-	// Submit & wait for completion
-	submitCommandsAndWait(vk, device, queue, cmdBuf.get());
-
 	const void* imagePtr	= readImageBufferMemory->getHostPtr();
 	const tcu::ConstPixelBufferAccess pixelBuffer(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
 												  renderSize.x(), renderSize.y(), 1, imagePtr);
@@ -4193,7 +4235,8 @@
 						 tcu::TestCaseGroup*		tests,
 						 const qpTestResult			failResult,
 						 const string&				failMessageTemplate,
-						 const bool					renderFullSquare)
+						 const bool					renderFullSquare,
+						 const bool					splitRenderArea)
 {
 	const StageData&				stageData			= getStageData(stage);
 	DE_ASSERT(stageData.getPipelineFn || stageData.initProgramsFn);
@@ -4204,6 +4247,7 @@
 		specConstantMap[stage] = specConstants;
 
 	InstanceContext					ctx					(inputColors, outputColors, testCodeFragments, specConstantMap, pushConstants, resources, interfaces, extensions, vulkanFeatures, stage);
+	ctx.splitRenderArea = splitRenderArea;
 	for (size_t i = 0; i < pipeline.size(); ++i)
 	{
 		ctx.moduleMap[pipeline[i].moduleName].push_back(std::make_pair(pipeline[i].entryName, pipeline[i].stage));
@@ -4215,6 +4259,7 @@
 		ctx.failMessageTemplate = failMessageTemplate;
 
 	ctx.renderFullSquare = renderFullSquare;
+	ctx.splitRenderArea	= splitRenderArea;
 	addFunctionCaseWithPrograms<InstanceContext>(tests, name, "", stageData.initProgramsFn, runAndVerifyDefaultPipeline, ctx);
 }
 
@@ -4230,7 +4275,8 @@
 							  VulkanFeatures				vulkanFeatures,
 							  tcu::TestCaseGroup*			tests,
 							  const qpTestResult			failResult,
-							  const string&					failMessageTemplate)
+							  const string&					failMessageTemplate,
+							  const bool					splitRenderArea)
 {
 	createTestForStage(VK_SHADER_STAGE_VERTEX_BIT, name + "_vert",
 					   inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources,
@@ -4250,7 +4296,7 @@
 
 	createTestForStage(VK_SHADER_STAGE_FRAGMENT_BIT, name + "_frag",
 					   inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources,
-					   interfaces, extensions, vulkanFeatures, tests, failResult, failMessageTemplate);
+					   interfaces, extensions, vulkanFeatures, tests, failResult, failMessageTemplate, false, splitRenderArea);
 }
 
 void addTessCtrlTest (tcu::TestCaseGroup* group, const char* name, const map<string, string>& fragments)
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp
index e6d51a3..3b90756 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp
@@ -285,6 +285,7 @@
 	qpTestResult							failResult;
 	std::string								failMessageTemplate;	//!< ${reason} in the template will be replaced with a detailed failure message
 	bool									renderFullSquare;		// Forces to render whole render area, though with background color
+	bool									splitRenderArea;		// Split render into multiple submissions.
 
 	InstanceContext (const tcu::RGBA							(&inputs)[4],
 					 const tcu::RGBA							(&outputs)[4],
@@ -414,7 +415,8 @@
 						 tcu::TestCaseGroup*						tests,
 						 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
 						 const std::string&							failMessageTemplate = std::string(),
-						 const bool									renderFullSquare	= false);
+						 const bool									renderFullSquare	= false,
+						 const bool									splitRenderArea		= false);
 
 void createTestsForAllStages (const std::string&						name,
 							  const tcu::RGBA							(&inputColors)[4],
@@ -428,7 +430,8 @@
 							  VulkanFeatures							vulkanFeatures,
 							  tcu::TestCaseGroup*						tests,
 							  const qpTestResult						failResult			= QP_TEST_RESULT_FAIL,
-							  const std::string&						failMessageTemplate	= std::string());
+							  const std::string&						failMessageTemplate	= std::string(),
+							  const bool								splitRenderArea		= false);
 
 inline void createTestsForAllStages (const std::string&							name,
 									 const tcu::RGBA							(&inputColors)[4],
@@ -479,7 +482,8 @@
 									 tcu::TestCaseGroup*						tests,
 									 VulkanFeatures								vulkanFeatures		= VulkanFeatures(),
 									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
-									 const std::string&							failMessageTemplate	= std::string())
+									 const std::string&							failMessageTemplate	= std::string(),
+									 const bool									splitRenderArea = false)
 {
 	SpecConstants				noSpecConstants;
 	PushConstants				noPushConstants;
@@ -488,7 +492,7 @@
 	createTestsForAllStages(
 			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
 			resources, noInterfaces, extensions, vulkanFeatures,
-			tests, failResult, failMessageTemplate);
+			tests, failResult, failMessageTemplate, splitRenderArea );
 }
 
 inline void createTestsForAllStages (const std::string& name,
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
index 9e2606c..b72193d 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
@@ -9871,7 +9871,8 @@
 							const std::string&			testName,
 							const VulkanFeatures&		vulkanFeatures,
 							const vector<string>&		extensions,
-							const IVec3&				numWorkGroups);
+							const IVec3&				numWorkGroups,
+							const bool					splitRenderArea = false);
 
 template<>
 void finalizeTestsCreation (GraphicsResources&			specResource,
@@ -9881,12 +9882,13 @@
 							const std::string&			testName,
 							const VulkanFeatures&		vulkanFeatures,
 							const vector<string>&		extensions,
-							const IVec3&				)
+							const IVec3&				,
+							const bool					splitRenderArea)
 {
 	RGBA defaultColors[4];
 	getDefaultColors(defaultColors);
 
-	createTestsForAllStages(testName, defaultColors, defaultColors, fragments, specResource, extensions, &testGroup, vulkanFeatures);
+	createTestsForAllStages(testName, defaultColors, defaultColors, fragments, specResource, extensions, &testGroup, vulkanFeatures, QP_TEST_RESULT_FAIL, std::string(), splitRenderArea);
 }
 
 template<>
@@ -9897,7 +9899,8 @@
 							const std::string&			testName,
 							const VulkanFeatures&		vulkanFeatures,
 							const vector<string>&		extensions,
-							const IVec3&				numWorkGroups)
+							const IVec3&				numWorkGroups,
+							bool)
 {
 	specResource.numWorkGroups = numWorkGroups;
 	specResource.requestedVulkanFeatures = vulkanFeatures;
@@ -10200,7 +10203,7 @@
 			features.ext16BitStorage = EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK;
 			features.extFloat16Int8 = EXTFLOAT16INT8FEATURES_FLOAT16;
 
-			finalizeTestsCreation(specResource, fragments, testCtx, *testGroup.get(), testName, features, extensions, IVec3(1, 1, 1));
+			finalizeTestsCreation(specResource, fragments, testCtx, *testGroup.get(), testName, features, extensions, IVec3(1, 1, 1), true);
 		}
 	}