Add tests for VK_GOOGLE_display_timing

New tests:

dEQP-VK.wsi.*.display_timing.*

Changed tests:

dEQP-VK.api.info.android.no_unknown_extensions

VK-GL-CTS issue: 227

Change-Id: I50ae75ee0301668e4ad255625f7098387365def8
(cherry picked from commit 4ca1b5ba618a69f9ab748417c9ac5247d182dff4)
diff --git a/Android.mk b/Android.mk
index ea13fd3..70666cc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -903,6 +903,7 @@
 	external/vulkancts/modules/vulkan/vktTestGroupUtil.cpp \
 	external/vulkancts/modules/vulkan/vktTestPackage.cpp \
 	external/vulkancts/modules/vulkan/vktTestPackageEntry.cpp \
+	external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.cpp \
 	external/vulkancts/modules/vulkan/wsi/vktWsiSurfaceTests.cpp \
 	external/vulkancts/modules/vulkan/wsi/vktWsiSwapchainTests.cpp \
 	external/vulkancts/modules/vulkan/wsi/vktWsiTests.cpp
diff --git a/android/cts/master/vk-master.txt b/android/cts/master/vk-master.txt
index a0e8129..3226f17 100644
--- a/android/cts/master/vk-master.txt
+++ b/android/cts/master/vk-master.txt
@@ -134380,6 +134380,14 @@
 dEQP-VK.wsi.android.swapchain.modify.resize
 dEQP-VK.wsi.android.swapchain.destroy.null_handle
 dEQP-VK.wsi.android.swapchain.get_images.incomplete
+dEQP-VK.wsi.android.display_timing.fifo.reference
+dEQP-VK.wsi.android.display_timing.fifo.display_timing
+dEQP-VK.wsi.android.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.android.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.android.display_timing.immediate.reference
+dEQP-VK.wsi.android.display_timing.immediate.display_timing
+dEQP-VK.wsi.android.display_timing.mailbox.reference
+dEQP-VK.wsi.android.display_timing.mailbox.display_timing
 dEQP-VK.synchronization.smoke.fences
 dEQP-VK.synchronization.smoke.semaphores
 dEQP-VK.synchronization.smoke.events
diff --git a/external/vulkancts/framework/vulkan/vkBasicTypes.inl b/external/vulkancts/framework/vulkan/vkBasicTypes.inl
index 9dbef50..9929d80 100644
--- a/external/vulkancts/framework/vulkan/vkBasicTypes.inl
+++ b/external/vulkancts/framework/vulkan/vkBasicTypes.inl
@@ -179,6 +179,7 @@
 	VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX				= 1000086003,
 	VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX					= 1000086004,
 	VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX				= 1000086005,
+	VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE								= 1000092000,
 };
 
 enum VkSystemAllocationScope
diff --git a/external/vulkancts/framework/vulkan/vkConcreteDeviceInterface.inl b/external/vulkancts/framework/vulkan/vkConcreteDeviceInterface.inl
index 15398a4..1976c3b 100644
--- a/external/vulkancts/framework/vulkan/vkConcreteDeviceInterface.inl
+++ b/external/vulkancts/framework/vulkan/vkConcreteDeviceInterface.inl
@@ -150,3 +150,5 @@
 virtual void		destroyDescriptorUpdateTemplateKHR				(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) const;
 virtual void		updateDescriptorSetWithTemplateKHR				(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData) const;
 virtual void		cmdPushDescriptorSetWithTemplateKHR				(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, deUint32 set, const void* pData) const;
+virtual VkResult	getRefreshCycleDurationGOOGLE					(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) const;
+virtual VkResult	getPastPresentationTimingGOOGLE					(VkDevice device, VkSwapchainKHR swapchain, deUint32* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) const;
diff --git a/external/vulkancts/framework/vulkan/vkDeviceDriverImpl.inl b/external/vulkancts/framework/vulkan/vkDeviceDriverImpl.inl
index 3a29b9b..adc39d8 100644
--- a/external/vulkancts/framework/vulkan/vkDeviceDriverImpl.inl
+++ b/external/vulkancts/framework/vulkan/vkDeviceDriverImpl.inl
@@ -746,3 +746,13 @@
 {
 	m_vk.cmdPushDescriptorSetWithTemplateKHR(commandBuffer, descriptorUpdateTemplate, layout, set, pData);
 }
+
+VkResult DeviceDriver::getRefreshCycleDurationGOOGLE (VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) const
+{
+	return m_vk.getRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties);
+}
+
+VkResult DeviceDriver::getPastPresentationTimingGOOGLE (VkDevice device, VkSwapchainKHR swapchain, deUint32* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) const
+{
+	return m_vk.getPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings);
+}
diff --git a/external/vulkancts/framework/vulkan/vkDeviceFunctionPointers.inl b/external/vulkancts/framework/vulkan/vkDeviceFunctionPointers.inl
index dd76873..7077438 100644
--- a/external/vulkancts/framework/vulkan/vkDeviceFunctionPointers.inl
+++ b/external/vulkancts/framework/vulkan/vkDeviceFunctionPointers.inl
@@ -150,3 +150,5 @@
 DestroyDescriptorUpdateTemplateKHRFunc				destroyDescriptorUpdateTemplateKHR;
 UpdateDescriptorSetWithTemplateKHRFunc				updateDescriptorSetWithTemplateKHR;
 CmdPushDescriptorSetWithTemplateKHRFunc				cmdPushDescriptorSetWithTemplateKHR;
+GetRefreshCycleDurationGOOGLEFunc					getRefreshCycleDurationGOOGLE;
+GetPastPresentationTimingGOOGLEFunc					getPastPresentationTimingGOOGLE;
diff --git a/external/vulkancts/framework/vulkan/vkFunctionPointerTypes.inl b/external/vulkancts/framework/vulkan/vkFunctionPointerTypes.inl
index d44a394..1819371 100644
--- a/external/vulkancts/framework/vulkan/vkFunctionPointerTypes.inl
+++ b/external/vulkancts/framework/vulkan/vkFunctionPointerTypes.inl
@@ -201,3 +201,5 @@
 typedef VKAPI_ATTR void					(VKAPI_CALL* DestroyDescriptorUpdateTemplateKHRFunc)				(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
 typedef VKAPI_ATTR void					(VKAPI_CALL* UpdateDescriptorSetWithTemplateKHRFunc)				(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData);
 typedef VKAPI_ATTR void					(VKAPI_CALL* CmdPushDescriptorSetWithTemplateKHRFunc)				(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, deUint32 set, const void* pData);
+typedef VKAPI_ATTR VkResult				(VKAPI_CALL* GetRefreshCycleDurationGOOGLEFunc)						(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VKAPI_ATTR VkResult				(VKAPI_CALL* GetPastPresentationTimingGOOGLEFunc)					(VkDevice device, VkSwapchainKHR swapchain, deUint32* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
diff --git a/external/vulkancts/framework/vulkan/vkInitDeviceFunctionPointers.inl b/external/vulkancts/framework/vulkan/vkInitDeviceFunctionPointers.inl
index bc96b47..c6755a4 100644
--- a/external/vulkancts/framework/vulkan/vkInitDeviceFunctionPointers.inl
+++ b/external/vulkancts/framework/vulkan/vkInitDeviceFunctionPointers.inl
@@ -150,3 +150,5 @@
 m_vk.destroyDescriptorUpdateTemplateKHR					= (DestroyDescriptorUpdateTemplateKHRFunc)				GET_PROC_ADDR("vkDestroyDescriptorUpdateTemplateKHR");
 m_vk.updateDescriptorSetWithTemplateKHR					= (UpdateDescriptorSetWithTemplateKHRFunc)				GET_PROC_ADDR("vkUpdateDescriptorSetWithTemplateKHR");
 m_vk.cmdPushDescriptorSetWithTemplateKHR				= (CmdPushDescriptorSetWithTemplateKHRFunc)				GET_PROC_ADDR("vkCmdPushDescriptorSetWithTemplateKHR");
+m_vk.getRefreshCycleDurationGOOGLE						= (GetRefreshCycleDurationGOOGLEFunc)					GET_PROC_ADDR("vkGetRefreshCycleDurationGOOGLE");
+m_vk.getPastPresentationTimingGOOGLE					= (GetPastPresentationTimingGOOGLEFunc)					GET_PROC_ADDR("vkGetPastPresentationTimingGOOGLE");
diff --git a/external/vulkancts/framework/vulkan/vkNullDriverImpl.inl b/external/vulkancts/framework/vulkan/vkNullDriverImpl.inl
index 188673a..d573957 100644
--- a/external/vulkancts/framework/vulkan/vkNullDriverImpl.inl
+++ b/external/vulkancts/framework/vulkan/vkNullDriverImpl.inl
@@ -1348,6 +1348,23 @@
 	DE_UNREF(pData);
 }
 
+VKAPI_ATTR VkResult VKAPI_CALL getRefreshCycleDurationGOOGLE (VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties)
+{
+	DE_UNREF(device);
+	DE_UNREF(swapchain);
+	DE_UNREF(pDisplayTimingProperties);
+	return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL getPastPresentationTimingGOOGLE (VkDevice device, VkSwapchainKHR swapchain, deUint32* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings)
+{
+	DE_UNREF(device);
+	DE_UNREF(swapchain);
+	DE_UNREF(pPresentationTimingCount);
+	DE_UNREF(pPresentationTimings);
+	return VK_SUCCESS;
+}
+
 static const tcu::StaticFunctionLibrary::Entry s_platformFunctions[] =
 {
 	VK_NULL_FUNC_ENTRY(vkCreateInstance,						createInstance),
@@ -1558,5 +1575,7 @@
 	VK_NULL_FUNC_ENTRY(vkDestroyDescriptorUpdateTemplateKHR,				destroyDescriptorUpdateTemplateKHR),
 	VK_NULL_FUNC_ENTRY(vkUpdateDescriptorSetWithTemplateKHR,				updateDescriptorSetWithTemplateKHR),
 	VK_NULL_FUNC_ENTRY(vkCmdPushDescriptorSetWithTemplateKHR,				cmdPushDescriptorSetWithTemplateKHR),
+	VK_NULL_FUNC_ENTRY(vkGetRefreshCycleDurationGOOGLE,						getRefreshCycleDurationGOOGLE),
+	VK_NULL_FUNC_ENTRY(vkGetPastPresentationTimingGOOGLE,					getPastPresentationTimingGOOGLE),
 };
 
diff --git a/external/vulkancts/framework/vulkan/vkStrUtil.inl b/external/vulkancts/framework/vulkan/vkStrUtil.inl
index c95a7cc..cb60124 100644
--- a/external/vulkancts/framework/vulkan/vkStrUtil.inl
+++ b/external/vulkancts/framework/vulkan/vkStrUtil.inl
@@ -380,3 +380,7 @@
 std::ostream&	operator<<	(std::ostream& s, const VkPhysicalDevicePushDescriptorPropertiesKHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkDescriptorUpdateTemplateEntryKHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkDescriptorUpdateTemplateCreateInfoKHR& value);
+std::ostream&	operator<<	(std::ostream& s, const VkRefreshCycleDurationGOOGLE& value);
+std::ostream&	operator<<	(std::ostream& s, const VkPastPresentationTimingGOOGLE& value);
+std::ostream&	operator<<	(std::ostream& s, const VkPresentTimeGOOGLE& value);
+std::ostream&	operator<<	(std::ostream& s, const VkPresentTimesInfoGOOGLE& value);
diff --git a/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl b/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl
index e88dafe..f0170ac 100644
--- a/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl
+++ b/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl
@@ -192,6 +192,7 @@
 		case VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX:					return "VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX";
 		case VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX:					return "VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX";
 		case VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX:					return "VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX";
+		case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:								return "VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE";
 		default:																		return DE_NULL;
 	}
 }
@@ -3840,3 +3841,43 @@
 	s << '}';
 	return s;
 }
+
+std::ostream& operator<< (std::ostream& s, const VkRefreshCycleDurationGOOGLE& value)
+{
+	s << "VkRefreshCycleDurationGOOGLE = {\n";
+	s << "\trefreshDuration = " << value.refreshDuration << '\n';
+	s << '}';
+	return s;
+}
+
+std::ostream& operator<< (std::ostream& s, const VkPastPresentationTimingGOOGLE& value)
+{
+	s << "VkPastPresentationTimingGOOGLE = {\n";
+	s << "\tpresentID = " << value.presentID << '\n';
+	s << "\tdesiredPresentTime = " << value.desiredPresentTime << '\n';
+	s << "\tactualPresentTime = " << value.actualPresentTime << '\n';
+	s << "\tearliestPresentTime = " << value.earliestPresentTime << '\n';
+	s << "\tpresentMargin = " << value.presentMargin << '\n';
+	s << '}';
+	return s;
+}
+
+std::ostream& operator<< (std::ostream& s, const VkPresentTimeGOOGLE& value)
+{
+	s << "VkPresentTimeGOOGLE = {\n";
+	s << "\tpresentID = " << value.presentID << '\n';
+	s << "\tdesiredPresentTime = " << value.desiredPresentTime << '\n';
+	s << '}';
+	return s;
+}
+
+std::ostream& operator<< (std::ostream& s, const VkPresentTimesInfoGOOGLE& value)
+{
+	s << "VkPresentTimesInfoGOOGLE = {\n";
+	s << "\tsType = " << value.sType << '\n';
+	s << "\tpNext = " << value.pNext << '\n';
+	s << "\tswapchainCount = " << value.swapchainCount << '\n';
+	s << "\tpTimes = " << value.pTimes << '\n';
+	s << '}';
+	return s;
+}
diff --git a/external/vulkancts/framework/vulkan/vkStructTypes.inl b/external/vulkancts/framework/vulkan/vkStructTypes.inl
index abf4f9f..fc32656 100644
--- a/external/vulkancts/framework/vulkan/vkStructTypes.inl
+++ b/external/vulkancts/framework/vulkan/vkStructTypes.inl
@@ -1716,3 +1716,31 @@
 	deUint32									set;
 };
 
+struct VkRefreshCycleDurationGOOGLE
+{
+	deUint64	refreshDuration;
+};
+
+struct VkPastPresentationTimingGOOGLE
+{
+	deUint32	presentID;
+	deUint64	desiredPresentTime;
+	deUint64	actualPresentTime;
+	deUint64	earliestPresentTime;
+	deUint64	presentMargin;
+};
+
+struct VkPresentTimeGOOGLE
+{
+	deUint32	presentID;
+	deUint64	desiredPresentTime;
+};
+
+struct VkPresentTimesInfoGOOGLE
+{
+	VkStructureType				sType;
+	const void*					pNext;
+	deUint32					swapchainCount;
+	const VkPresentTimeGOOGLE*	pTimes;
+};
+
diff --git a/external/vulkancts/framework/vulkan/vkTypeUtil.inl b/external/vulkancts/framework/vulkan/vkTypeUtil.inl
index 18f92a7..e8cf382 100644
--- a/external/vulkancts/framework/vulkan/vkTypeUtil.inl
+++ b/external/vulkancts/framework/vulkan/vkTypeUtil.inl
@@ -469,3 +469,29 @@
 	res.stride			= stride;
 	return res;
 }
+
+inline VkRefreshCycleDurationGOOGLE makeRefreshCycleDurationGOOGLE (deUint64 refreshDuration)
+{
+	VkRefreshCycleDurationGOOGLE res;
+	res.refreshDuration	= refreshDuration;
+	return res;
+}
+
+inline VkPastPresentationTimingGOOGLE makePastPresentationTimingGOOGLE (deUint32 presentID, deUint64 desiredPresentTime, deUint64 actualPresentTime, deUint64 earliestPresentTime, deUint64 presentMargin)
+{
+	VkPastPresentationTimingGOOGLE res;
+	res.presentID			= presentID;
+	res.desiredPresentTime	= desiredPresentTime;
+	res.actualPresentTime	= actualPresentTime;
+	res.earliestPresentTime	= earliestPresentTime;
+	res.presentMargin		= presentMargin;
+	return res;
+}
+
+inline VkPresentTimeGOOGLE makePresentTimeGOOGLE (deUint32 presentID, deUint64 desiredPresentTime)
+{
+	VkPresentTimeGOOGLE res;
+	res.presentID			= presentID;
+	res.desiredPresentTime	= desiredPresentTime;
+	return res;
+}
diff --git a/external/vulkancts/framework/vulkan/vkVirtualDeviceInterface.inl b/external/vulkancts/framework/vulkan/vkVirtualDeviceInterface.inl
index 85dff39..53a1040 100644
--- a/external/vulkancts/framework/vulkan/vkVirtualDeviceInterface.inl
+++ b/external/vulkancts/framework/vulkan/vkVirtualDeviceInterface.inl
@@ -150,3 +150,5 @@
 virtual void		destroyDescriptorUpdateTemplateKHR				(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) const = 0;
 virtual void		updateDescriptorSetWithTemplateKHR				(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData) const = 0;
 virtual void		cmdPushDescriptorSetWithTemplateKHR				(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, deUint32 set, const void* pData) const = 0;
+virtual VkResult	getRefreshCycleDurationGOOGLE					(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) const = 0;
+virtual VkResult	getPastPresentationTimingGOOGLE					(VkDevice device, VkSwapchainKHR swapchain, deUint32* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) const = 0;
diff --git a/external/vulkancts/modules/vulkan/api/vktApiFeatureInfo.cpp b/external/vulkancts/modules/vulkan/api/vktApiFeatureInfo.cpp
index b947cb1..7249343 100644
--- a/external/vulkancts/modules/vulkan/api/vktApiFeatureInfo.cpp
+++ b/external/vulkancts/modules/vulkan/api/vktApiFeatureInfo.cpp
@@ -2565,14 +2565,16 @@
 {
 	TestLog&				log					= context.getTestContext().getLog();
 	tcu::ResultCollector	results				(log);
-	set<string>				allowedExtensions;
+	set<string>				allowedInstanceExtensions;
+	set<string>				allowedDeviceExtensions;
 
 	// All known extensions should be added to allowedExtensions:
 	// allowedExtensions.insert("VK_GOOGLE_extension1");
+	allowedDeviceExtensions.insert("VK_GOOGLE_display_timing");
 
 	// Instance extensions
 	checkExtensions(results,
-					allowedExtensions,
+					allowedInstanceExtensions,
 					enumerateInstanceExtensionProperties(context.getPlatformInterface(), DE_NULL));
 
 	// Extensions exposed by instance layers
@@ -2582,14 +2584,14 @@
 		for (vector<VkLayerProperties>::const_iterator layer = layers.begin(); layer != layers.end(); ++layer)
 		{
 			checkExtensions(results,
-							allowedExtensions,
+							allowedInstanceExtensions,
 							enumerateInstanceExtensionProperties(context.getPlatformInterface(), layer->layerName));
 		}
 	}
 
 	// Device extensions
 	checkExtensions(results,
-					allowedExtensions,
+					allowedDeviceExtensions,
 					enumerateDeviceExtensionProperties(context.getInstanceInterface(), context.getPhysicalDevice(), DE_NULL));
 
 	// Extensions exposed by device layers
@@ -2599,7 +2601,7 @@
 		for (vector<VkLayerProperties>::const_iterator layer = layers.begin(); layer != layers.end(); ++layer)
 		{
 			checkExtensions(results,
-							allowedExtensions,
+							allowedDeviceExtensions,
 							enumerateDeviceExtensionProperties(context.getInstanceInterface(), context.getPhysicalDevice(), layer->layerName));
 		}
 	}
diff --git a/external/vulkancts/modules/vulkan/wsi/CMakeLists.txt b/external/vulkancts/modules/vulkan/wsi/CMakeLists.txt
index 3a271dc..de0fed9 100644
--- a/external/vulkancts/modules/vulkan/wsi/CMakeLists.txt
+++ b/external/vulkancts/modules/vulkan/wsi/CMakeLists.txt
@@ -9,6 +9,8 @@
 	vktWsiSurfaceTests.hpp
 	vktWsiSwapchainTests.cpp
 	vktWsiSwapchainTests.hpp
+	vktWsiDisplayTimingTests.cpp
+	vktWsiDisplayTimingTests.hpp
 	)
 
 set(DEQP_VK_WSI_LIBS
diff --git a/external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.cpp b/external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.cpp
new file mode 100644
index 0000000..f82fb2b
--- /dev/null
+++ b/external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.cpp
@@ -0,0 +1,1488 @@
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tests for VK_GOOGLE_display_timing
+ *//*--------------------------------------------------------------------*/
+
+#include "vktWsiDisplayTimingTests.hpp"
+
+#include "vktTestCaseUtil.hpp"
+#include "vktTestGroupUtil.hpp"
+#include "vkRefUtil.hpp"
+#include "vkWsiPlatform.hpp"
+#include "vkWsiUtil.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkDeviceUtil.hpp"
+#include "vkPlatform.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkPrograms.hpp"
+
+#include "vkWsiUtil.hpp"
+
+#include "tcuPlatform.hpp"
+#include "tcuResultCollector.hpp"
+#include "deClock.h"
+
+#include <vector>
+#include <string>
+
+using std::vector;
+using std::string;
+
+using tcu::Maybe;
+using tcu::UVec2;
+using tcu::TestLog;
+
+namespace vk
+{
+
+Move<VkSemaphore> createSemaphore (const DeviceInterface&		vk,
+								   VkDevice						device,
+								   VkSemaphoreCreateFlags		flags		= (VkSemaphoreCreateFlags)0,
+								   const VkAllocationCallbacks*	pAllocator	= DE_NULL)
+{
+	const VkSemaphoreCreateInfo createInfo =
+	{
+		VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+		DE_NULL,
+
+		flags
+	};
+
+	return createSemaphore(vk, device, &createInfo, pAllocator);
+}
+
+Move<VkFence> createFence (const DeviceInterface&		vk,
+						   VkDevice						device,
+						   VkFenceCreateFlags			flags		= (VkFenceCreateFlags)0,
+						   const VkAllocationCallbacks*	pAllocator	= DE_NULL)
+{
+	const VkFenceCreateInfo createInfo =
+	{
+		VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+		DE_NULL,
+
+		flags
+	};
+
+	return createFence(vk, device, &createInfo, pAllocator);
+}
+
+VkQueue getDeviceQueue (const DeviceInterface& vkd, VkDevice device, deUint32 queueFamilyIndex, deUint32 queueIndex)
+{
+	VkQueue queue;
+
+	vkd.getDeviceQueue(device, queueFamilyIndex, queueIndex, &queue);
+
+	return queue;
+}
+
+} // vk
+
+namespace vkt
+{
+namespace wsi
+{
+namespace
+{
+static const deUint64 MILLISECOND = 1000ull * 1000ull;
+static const deUint64 SECOND = 1000ull * MILLISECOND;
+
+typedef vector<vk::VkExtensionProperties> Extensions;
+
+void checkAllSupported (const Extensions& supportedExtensions, const vector<string>& requiredExtensions)
+{
+	for (vector<string>::const_iterator requiredExtName = requiredExtensions.begin();
+		 requiredExtName != requiredExtensions.end();
+		 ++requiredExtName)
+	{
+		if (!isExtensionSupported(supportedExtensions, vk::RequiredExtension(*requiredExtName)))
+			TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str());
+	}
+}
+
+vk::Move<vk::VkInstance> createInstanceWithWsi (const vk::PlatformInterface&		vkp,
+												const Extensions&					supportedExtensions,
+												vk::wsi::Type						wsiType)
+{
+	vector<string>	extensions;
+
+	extensions.push_back("VK_KHR_surface");
+	extensions.push_back(getExtensionName(wsiType));
+
+	checkAllSupported(supportedExtensions, extensions);
+
+	return vk::createDefaultInstance(vkp, vector<string>(), extensions);
+}
+
+vk::VkPhysicalDeviceFeatures getDeviceNullFeatures (void)
+{
+	vk::VkPhysicalDeviceFeatures features;
+	deMemset(&features, 0, sizeof(features));
+	return features;
+}
+
+deUint32 getNumQueueFamilyIndices (const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice)
+{
+	deUint32	numFamilies		= 0;
+
+	vki.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numFamilies, DE_NULL);
+
+	return numFamilies;
+}
+
+vector<deUint32> getSupportedQueueFamilyIndices (const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice, vk::VkSurfaceKHR surface)
+{
+	const deUint32		numTotalFamilyIndices	= getNumQueueFamilyIndices(vki, physicalDevice);
+	vector<deUint32>	supportedFamilyIndices;
+
+	for (deUint32 queueFamilyNdx = 0; queueFamilyNdx < numTotalFamilyIndices; ++queueFamilyNdx)
+	{
+		if (vk::wsi::getPhysicalDeviceSurfaceSupport(vki, physicalDevice, queueFamilyNdx, surface) == VK_TRUE)
+			supportedFamilyIndices.push_back(queueFamilyNdx);
+	}
+
+	return supportedFamilyIndices;
+}
+
+deUint32 chooseQueueFamilyIndex (const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice, vk::VkSurfaceKHR surface)
+{
+	const vector<deUint32>	supportedFamilyIndices	= getSupportedQueueFamilyIndices(vki, physicalDevice, surface);
+
+	if (supportedFamilyIndices.empty())
+		TCU_THROW(NotSupportedError, "Device doesn't support presentation");
+
+	return supportedFamilyIndices[0];
+}
+
+vk::Move<vk::VkDevice> createDeviceWithWsi (const vk::InstanceInterface&		vki,
+											vk::VkPhysicalDevice				physicalDevice,
+											const Extensions&					supportedExtensions,
+											const deUint32						queueFamilyIndex,
+											bool								requiresDisplayTiming,
+											const vk::VkAllocationCallbacks*	pAllocator = DE_NULL)
+{
+	const float							queuePriorities[]	= { 1.0f };
+	const vk::VkDeviceQueueCreateInfo	queueInfos[]		=
+	{
+		{
+			vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+			DE_NULL,
+			(vk::VkDeviceQueueCreateFlags)0,
+			queueFamilyIndex,
+			DE_LENGTH_OF_ARRAY(queuePriorities),
+			&queuePriorities[0]
+		}
+	};
+	const vk::VkPhysicalDeviceFeatures	features		= getDeviceNullFeatures();
+	const char* const					extensions[]	=
+	{
+		"VK_KHR_swapchain",
+		"VK_GOOGLE_display_timing"
+	};
+
+	const vk::VkDeviceCreateInfo		deviceParams	=
+	{
+		vk::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+		DE_NULL,
+		(vk::VkDeviceCreateFlags)0,
+		DE_LENGTH_OF_ARRAY(queueInfos),
+		&queueInfos[0],
+		0u,
+		DE_NULL,
+		requiresDisplayTiming ? 2u : 1u,
+		DE_ARRAY_BEGIN(extensions),
+		&features
+	};
+
+	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(extensions); ++ndx)
+	{
+		if (!isExtensionSupported(supportedExtensions, vk::RequiredExtension(extensions[ndx])))
+			TCU_THROW(NotSupportedError, (string(extensions[ndx]) + " is not supported").c_str());
+	}
+
+	return createDevice(vki, physicalDevice, &deviceParams, pAllocator);
+}
+
+de::MovePtr<vk::wsi::Display> createDisplay (const vk::Platform&	platform,
+											 const Extensions&		supportedExtensions,
+											 vk::wsi::Type			wsiType)
+{
+	try
+	{
+		return de::MovePtr<vk::wsi::Display>(platform.createWsiDisplay(wsiType));
+	}
+	catch (const tcu::NotSupportedError& e)
+	{
+		if (isExtensionSupported(supportedExtensions, vk::RequiredExtension(getExtensionName(wsiType))))
+		{
+			// If VK_KHR_{platform}_surface was supported, vk::Platform implementation
+			// must support creating native display & window for that WSI type.
+			throw tcu::TestError(e.getMessage());
+		}
+		else
+			throw;
+	}
+}
+
+de::MovePtr<vk::wsi::Window> createWindow (const vk::wsi::Display& display, const Maybe<UVec2>& initialSize)
+{
+	try
+	{
+		return de::MovePtr<vk::wsi::Window>(display.createWindow(initialSize));
+	}
+	catch (const tcu::NotSupportedError& e)
+	{
+		// See createDisplay - assuming that wsi::Display was supported platform port
+		// should also support creating a window.
+		throw tcu::TestError(e.getMessage());
+	}
+}
+
+void initSemaphores (const vk::DeviceInterface&		vkd,
+					 vk::VkDevice					device,
+					 std::vector<vk::VkSemaphore>&	semaphores)
+{
+	for (size_t ndx = 0; ndx < semaphores.size(); ndx++)
+		semaphores[ndx] = createSemaphore(vkd, device).disown();
+}
+
+void deinitSemaphores (const vk::DeviceInterface&	vkd,
+					 vk::VkDevice					device,
+					 std::vector<vk::VkSemaphore>&	semaphores)
+{
+	for (size_t ndx = 0; ndx < semaphores.size(); ndx++)
+	{
+		if (semaphores[ndx] != (vk::VkSemaphore)0)
+			vkd.destroySemaphore(device, semaphores[ndx], DE_NULL);
+
+		semaphores[ndx] = (vk::VkSemaphore)0;
+	}
+
+	semaphores.clear();
+}
+
+void initFences (const vk::DeviceInterface&	vkd,
+				 vk::VkDevice				device,
+				 std::vector<vk::VkFence>&	fences)
+{
+	for (size_t ndx = 0; ndx < fences.size(); ndx++)
+		fences[ndx] = createFence(vkd, device).disown();
+}
+
+void deinitFences (const vk::DeviceInterface&	vkd,
+				   vk::VkDevice					device,
+				   std::vector<vk::VkFence>&	fences)
+{
+	for (size_t ndx = 0; ndx < fences.size(); ndx++)
+	{
+		if (fences[ndx] != (vk::VkFence)0)
+			vkd.destroyFence(device, fences[ndx], DE_NULL);
+
+		fences[ndx] = (vk::VkFence)0;
+	}
+
+	fences.clear();
+}
+
+void cmdRenderFrame (const vk::DeviceInterface&	vkd,
+					 vk::VkCommandBuffer		commandBuffer,
+					 vk::VkPipelineLayout		pipelineLayout,
+					 vk::VkPipeline				pipeline,
+					 size_t						frameNdx,
+					 deUint32					quadCount)
+{
+	const deUint32	frameNdxValue	= (deUint32)frameNdx;
+
+	vkd.cmdPushConstants(commandBuffer, pipelineLayout, vk::VK_SHADER_STAGE_FRAGMENT_BIT, 0u, 4u, &frameNdxValue);
+	vkd.cmdBindPipeline(commandBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+	vkd.cmdDraw(commandBuffer, quadCount * 6u, 1u, 0u, 0u);
+}
+
+vk::Move<vk::VkCommandBuffer> createCommandBuffer (const vk::DeviceInterface&	vkd,
+												   vk::VkDevice					device,
+												   vk::VkCommandPool			commandPool,
+												   vk::VkPipelineLayout			pipelineLayout,
+												   vk::VkRenderPass				renderPass,
+												   vk::VkFramebuffer			framebuffer,
+												   vk::VkPipeline				pipeline,
+												   size_t						frameNdx,
+												   deUint32						quadCount,
+												   deUint32						imageWidth,
+												   deUint32						imageHeight)
+{
+	const vk::VkCommandBufferAllocateInfo allocateInfo =
+	{
+		vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+		DE_NULL,
+
+		commandPool,
+		vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+		1
+	};
+	const vk::VkCommandBufferBeginInfo	beginInfo		=
+	{
+		vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+		DE_NULL,
+		0u,
+		DE_NULL
+	};
+
+	vk::Move<vk::VkCommandBuffer>	commandBuffer	(vk::allocateCommandBuffer(vkd, device, &allocateInfo));
+	VK_CHECK(vkd.beginCommandBuffer(*commandBuffer, &beginInfo));
+
+	{
+		const vk::VkClearValue			clearValue			= vk::makeClearValueColorF32(0.25f, 0.50f, 0.75f, 1.00f);
+		const vk::VkRenderPassBeginInfo	renderPassBeginInfo	=
+		{
+			vk::VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+			DE_NULL,
+
+			renderPass,
+			framebuffer,
+
+			{
+				{ (deInt32)0, (deInt32)0 },
+				{ imageWidth, imageHeight }
+			},
+			1u,
+			&clearValue
+		};
+		vkd.cmdBeginRenderPass(*commandBuffer, &renderPassBeginInfo, vk::VK_SUBPASS_CONTENTS_INLINE);
+	}
+
+	cmdRenderFrame(vkd, *commandBuffer, pipelineLayout, pipeline, frameNdx, quadCount);
+
+	vkd.cmdEndRenderPass(*commandBuffer);
+
+	VK_CHECK(vkd.endCommandBuffer(*commandBuffer));
+	return commandBuffer;
+}
+
+void deinitCommandBuffers (const vk::DeviceInterface&			vkd,
+						   vk::VkDevice							device,
+						   vk::VkCommandPool					commandPool,
+						   std::vector<vk::VkCommandBuffer>&	commandBuffers)
+{
+	for (size_t ndx = 0; ndx < commandBuffers.size(); ndx++)
+	{
+		if (commandBuffers[ndx] != (vk::VkCommandBuffer)0)
+			vkd.freeCommandBuffers(device, commandPool, 1u,  &commandBuffers[ndx]);
+
+		commandBuffers[ndx] = (vk::VkCommandBuffer)0;
+	}
+
+	commandBuffers.clear();
+}
+
+vk::Move<vk::VkCommandPool> createCommandPool (const vk::DeviceInterface&	vkd,
+											   vk::VkDevice					device,
+											   deUint32						queueFamilyIndex)
+{
+	const vk::VkCommandPoolCreateInfo createInfo =
+	{
+		vk::VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+		DE_NULL,
+		0u,
+		queueFamilyIndex
+	};
+
+	return vk::createCommandPool(vkd, device, &createInfo);
+}
+
+vk::Move<vk::VkFramebuffer>	createFramebuffer (const vk::DeviceInterface&	vkd,
+											   vk::VkDevice					device,
+											   vk::VkRenderPass				renderPass,
+											   vk::VkImageView				imageView,
+											   deUint32						width,
+											   deUint32						height)
+{
+	const vk::VkFramebufferCreateInfo createInfo =
+	{
+		vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+		DE_NULL,
+
+		0u,
+		renderPass,
+		1u,
+		&imageView,
+		width,
+		height,
+		1u
+	};
+
+	return vk::createFramebuffer(vkd, device, &createInfo);
+}
+
+void initFramebuffers (const vk::DeviceInterface&		vkd,
+					   vk::VkDevice						device,
+					   vk::VkRenderPass					renderPass,
+					   std::vector<vk::VkImageView>		imageViews,
+					   deUint32							width,
+					   deUint32							height,
+					   std::vector<vk::VkFramebuffer>&	framebuffers)
+{
+	DE_ASSERT(framebuffers.size() == imageViews.size());
+
+	for (size_t ndx = 0; ndx < framebuffers.size(); ndx++)
+		framebuffers[ndx] = createFramebuffer(vkd, device, renderPass, imageViews[ndx], width, height).disown();
+}
+
+void deinitFramebuffers (const vk::DeviceInterface&			vkd,
+						 vk::VkDevice						device,
+						 std::vector<vk::VkFramebuffer>&	framebuffers)
+{
+	for (size_t ndx = 0; ndx < framebuffers.size(); ndx++)
+	{
+		if (framebuffers[ndx] != (vk::VkFramebuffer)0)
+			vkd.destroyFramebuffer(device, framebuffers[ndx], DE_NULL);
+
+		framebuffers[ndx] = (vk::VkFramebuffer)0;
+	}
+
+	framebuffers.clear();
+}
+
+vk::Move<vk::VkImageView> createImageView (const vk::DeviceInterface&	vkd,
+										   vk::VkDevice					device,
+										   vk::VkImage					image,
+										   vk::VkFormat					format)
+{
+	const vk::VkImageViewCreateInfo	createInfo =
+	{
+		vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+		DE_NULL,
+
+		0u,
+		image,
+		vk::VK_IMAGE_VIEW_TYPE_2D,
+		format,
+		vk::makeComponentMappingRGBA(),
+		{
+			vk::VK_IMAGE_ASPECT_COLOR_BIT,
+			0u,
+			1u,
+			0u,
+			1u
+		}
+	};
+
+	return vk::createImageView(vkd, device, &createInfo, DE_NULL);
+}
+
+void initImageViews (const vk::DeviceInterface&			vkd,
+					 vk::VkDevice						device,
+					 const std::vector<vk::VkImage>&	images,
+					 vk::VkFormat						format,
+					 std::vector<vk::VkImageView>&		imageViews)
+{
+	DE_ASSERT(images.size() == imageViews.size());
+
+	for (size_t ndx = 0; ndx < imageViews.size(); ndx++)
+		imageViews[ndx] = createImageView(vkd, device, images[ndx], format).disown();
+}
+
+void deinitImageViews (const vk::DeviceInterface&		vkd,
+					   vk::VkDevice						device,
+					   std::vector<vk::VkImageView>&	imageViews)
+{
+	for (size_t ndx = 0; ndx < imageViews.size(); ndx++)
+	{
+		if (imageViews[ndx] != (vk::VkImageView)0)
+			vkd.destroyImageView(device, imageViews[ndx], DE_NULL);
+
+		imageViews[ndx] = (vk::VkImageView)0;
+	}
+
+	imageViews.clear();
+}
+
+vk::Move<vk::VkRenderPass> createRenderPass (const vk::DeviceInterface&	vkd,
+											 vk::VkDevice				device,
+											 vk::VkFormat				format)
+{
+	const vk::VkAttachmentDescription	attachments[]			=
+	{
+		{
+			0u,
+			format,
+			vk::VK_SAMPLE_COUNT_1_BIT,
+
+			vk::VK_ATTACHMENT_LOAD_OP_LOAD,
+			vk::VK_ATTACHMENT_STORE_OP_STORE,
+
+			vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+			vk::VK_ATTACHMENT_STORE_OP_DONT_CARE,
+
+			vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+			vk::VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
+		}
+	};
+	const vk::VkAttachmentReference		colorAttachmentRefs[]	=
+	{
+		{
+			0u,
+			vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+		}
+	};
+	const vk::VkSubpassDescription		subpasses[]				=
+	{
+		{
+			0u,
+			vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
+			0u,
+			DE_NULL,
+
+			DE_LENGTH_OF_ARRAY(colorAttachmentRefs),
+			colorAttachmentRefs,
+			DE_NULL,
+
+			DE_NULL,
+			0u,
+			DE_NULL
+		}
+	};
+
+	const vk::VkRenderPassCreateInfo	createInfo				=
+	{
+		vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+		DE_NULL,
+		0u,
+
+		DE_LENGTH_OF_ARRAY(attachments),
+		attachments,
+
+		DE_LENGTH_OF_ARRAY(subpasses),
+		subpasses,
+
+		0u,
+		DE_NULL
+	};
+
+	return vk::createRenderPass(vkd, device, &createInfo);
+}
+
+vk::Move<vk::VkPipeline> createPipeline (const vk::DeviceInterface&	vkd,
+										 vk::VkDevice				device,
+										 vk::VkRenderPass			renderPass,
+										 vk::VkPipelineLayout		layout,
+										 vk::VkShaderModule			vertexShaderModule,
+										 vk::VkShaderModule			fragmentShaderModule,
+										 deUint32					width,
+										 deUint32					height)
+{
+	const vk::VkSpecializationInfo				shaderSpecialization	=
+	{
+		0u,
+		DE_NULL,
+		0,
+		DE_NULL
+	};
+	const vk::VkPipelineShaderStageCreateInfo		stages[]			=
+	{
+		{
+			vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+			DE_NULL,
+			0u,
+			vk::VK_SHADER_STAGE_VERTEX_BIT,
+			vertexShaderModule,
+			"main",
+			&shaderSpecialization
+		},
+		{
+			vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+			DE_NULL,
+			0u,
+			vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+			fragmentShaderModule,
+			"main",
+			&shaderSpecialization
+		}
+	};
+	const vk::VkPipelineVertexInputStateCreateInfo	vertexInputState	=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+		0u,
+		DE_NULL,
+		0u,
+		DE_NULL
+	};
+	const vk::VkPipelineInputAssemblyStateCreateInfo	inputAssemblyState	=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+		VK_FALSE
+	};
+	const vk::VkViewport viewports[] =
+	{
+		{
+			0.0f, 0.0f,
+			(float)width, (float)height,
+			0.0f, 1.0f
+		}
+	};
+	const vk::VkRect2D scissors[] =
+	{
+		{
+			{ 0u, 0u },
+			{ width, height }
+		}
+	};
+	const vk::VkPipelineViewportStateCreateInfo			viewportState		=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+
+		DE_LENGTH_OF_ARRAY(viewports),
+		viewports,
+		DE_LENGTH_OF_ARRAY(scissors),
+		scissors
+	};
+	const vk::VkPipelineRasterizationStateCreateInfo	rasterizationState	=
+	{
+		vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+		DE_NULL,
+		0u,
+		VK_TRUE,
+		VK_FALSE,
+		vk::VK_POLYGON_MODE_FILL,
+		vk::VK_CULL_MODE_NONE,
+		vk::VK_FRONT_FACE_CLOCKWISE,
+		VK_FALSE,
+		0.0f,
+		0.0f,
+		0.0f,
+		1.0f
+	};
+	const vk::VkSampleMask								sampleMask			= ~0u;
+	const vk::VkPipelineMultisampleStateCreateInfo		multisampleState	=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+		vk::VK_SAMPLE_COUNT_1_BIT,
+		VK_FALSE,
+		0.0f,
+		&sampleMask,
+		VK_FALSE,
+		VK_FALSE
+	};
+	const vk::VkPipelineDepthStencilStateCreateInfo	depthStencilState		=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+		DE_FALSE,
+		DE_FALSE,
+		vk::VK_COMPARE_OP_ALWAYS,
+		DE_FALSE,
+		DE_FALSE,
+		{
+			vk::VK_STENCIL_OP_KEEP,
+			vk::VK_STENCIL_OP_KEEP,
+			vk::VK_STENCIL_OP_KEEP,
+			vk::VK_COMPARE_OP_ALWAYS,
+			0u,
+			0u,
+			0u,
+		},
+		{
+			vk::VK_STENCIL_OP_KEEP,
+			vk::VK_STENCIL_OP_KEEP,
+			vk::VK_STENCIL_OP_KEEP,
+			vk::VK_COMPARE_OP_ALWAYS,
+			0u,
+			0u,
+			0u,
+		},
+		0.0f,
+		1.0f
+	};
+	const vk::VkPipelineColorBlendAttachmentState	attachmentBlendState			=
+	{
+		VK_FALSE,
+		vk::VK_BLEND_FACTOR_ONE,
+		vk::VK_BLEND_FACTOR_ZERO,
+		vk::VK_BLEND_OP_ADD,
+		vk::VK_BLEND_FACTOR_ONE,
+		vk::VK_BLEND_FACTOR_ZERO,
+		vk::VK_BLEND_OP_ADD,
+		(vk::VK_COLOR_COMPONENT_R_BIT|
+		 vk::VK_COLOR_COMPONENT_G_BIT|
+		 vk::VK_COLOR_COMPONENT_B_BIT|
+		 vk::VK_COLOR_COMPONENT_A_BIT),
+	};
+	const vk::VkPipelineColorBlendStateCreateInfo	blendState				=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+		DE_FALSE,
+		vk::VK_LOGIC_OP_COPY,
+		1u,
+		&attachmentBlendState,
+		{ 0.0f, 0.0f, 0.0f, 0.0f }
+	};
+	const vk::VkPipelineDynamicStateCreateInfo			dynamicState		=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+		DE_NULL,
+		0u,
+
+		0u,
+		DE_NULL
+	};
+	const vk::VkGraphicsPipelineCreateInfo				createInfo			=
+	{
+		vk::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+		DE_NULL,
+		0u,
+
+		DE_LENGTH_OF_ARRAY(stages),
+		stages,
+		&vertexInputState,
+		&inputAssemblyState,
+		DE_NULL,
+		&viewportState,
+		&rasterizationState,
+		&multisampleState,
+		&depthStencilState,
+		&blendState,
+		&dynamicState,
+		layout,
+		renderPass,
+		0u,
+		DE_NULL,
+		0u
+	};
+
+	return vk::createGraphicsPipeline(vkd, device, DE_NULL,  &createInfo);
+}
+
+vk::Move<vk::VkPipelineLayout> createPipelineLayout (const vk::DeviceInterface&	vkd,
+													 vk::VkDevice				device)
+{
+	const vk::VkPushConstantRange			pushConstants[] =
+	{
+		{
+			vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+			0u,
+			4u
+		}
+	};
+	const vk::VkPipelineLayoutCreateInfo	createInfo	=
+	{
+		vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+		DE_NULL,
+		0u,
+
+		0u,
+		DE_NULL,
+
+		DE_LENGTH_OF_ARRAY(pushConstants),
+		pushConstants
+	};
+
+	return vk::createPipelineLayout(vkd, device, &createInfo);
+}
+
+struct TestConfig
+{
+	vk::wsi::Type			wsiType;
+	bool					useDisplayTiming;
+	vk::VkPresentModeKHR	presentMode;
+};
+
+class DisplayTimingTestInstance : public TestInstance
+{
+public:
+												DisplayTimingTestInstance	(Context& context, const TestConfig& testConfig);
+												~DisplayTimingTestInstance	(void);
+
+	tcu::TestStatus								iterate						(void);
+
+private:
+	const bool								m_useDisplayTiming;
+	const deUint32							m_quadCount;
+	const vk::PlatformInterface&			m_vkp;
+	const Extensions						m_instanceExtensions;
+	const vk::Unique<vk::VkInstance>		m_instance;
+	const vk::InstanceDriver				m_vki;
+	const vk::VkPhysicalDevice				m_physicalDevice;
+	const de::UniquePtr<vk::wsi::Display>	m_nativeDisplay;
+	const de::UniquePtr<vk::wsi::Window>	m_nativeWindow;
+	const vk::Unique<vk::VkSurfaceKHR>		m_surface;
+
+	const deUint32							m_queueFamilyIndex;
+	const Extensions						m_deviceExtensions;
+	const vk::Unique<vk::VkDevice>			m_device;
+	const vk::DeviceDriver					m_vkd;
+	const vk::VkQueue						m_queue;
+
+	const vk::Unique<vk::VkCommandPool>		m_commandPool;
+	const vk::Unique<vk::VkShaderModule>	m_vertexShaderModule;
+	const vk::Unique<vk::VkShaderModule>	m_fragmentShaderModule;
+	const vk::Unique<vk::VkPipelineLayout>	m_pipelineLayout;
+
+	const vk::VkSurfaceCapabilitiesKHR		m_surfaceProperties;
+	const vector<vk::VkSurfaceFormatKHR>	m_surfaceFormats;
+	const vector<vk::VkPresentModeKHR>		m_presentModes;
+
+	tcu::ResultCollector					m_resultCollector;
+
+	vk::Move<vk::VkSwapchainKHR>			m_swapchain;
+	std::vector<vk::VkImage>				m_swapchainImages;
+
+	vk::Move<vk::VkRenderPass>				m_renderPass;
+	vk::Move<vk::VkPipeline>				m_pipeline;
+
+	std::vector<vk::VkImageView>			m_swapchainImageViews;
+	std::vector<vk::VkFramebuffer>			m_framebuffers;
+	std::vector<vk::VkCommandBuffer>		m_commandBuffers;
+	std::vector<vk::VkSemaphore>			m_acquireSemaphores;
+	std::vector<vk::VkSemaphore>			m_renderSemaphores;
+	std::vector<vk::VkFence>				m_fences;
+
+	vk::VkSemaphore							m_freeAcquireSemaphore;
+	vk::VkSemaphore							m_freeRenderSemaphore;
+
+	vk::VkSwapchainCreateInfoKHR			m_swapchainConfig;
+
+	const size_t							m_frameCount;
+	size_t									m_frameNdx;
+
+	const size_t							m_maxOutOfDateCount;
+	size_t									m_outOfDateCount;
+
+	std::map<deUint32, deUint64>			m_queuePresentTimes;
+
+    vk::VkRefreshCycleDurationGOOGLE		m_rcDuration;
+	deUint64								m_refreshDurationMultiplier;
+	deUint64								m_targetIPD;
+	deUint64								m_prevDesiredPresentTime;
+	deUint32								m_nextPresentID;
+	deUint32								m_ignoreThruPresentID;
+	bool									m_ExpectImage80Late;
+
+	void									initSwapchainResources		(void);
+	void									deinitSwapchainResources	(void);
+	void									render						(void);
+};
+
+vk::VkSwapchainCreateInfoKHR createSwapchainConfig (vk::VkSurfaceKHR						surface,
+													deUint32								queueFamilyIndex,
+													const vk::VkSurfaceCapabilitiesKHR&		properties,
+													const vector<vk::VkSurfaceFormatKHR>&	formats,
+													const vector<vk::VkPresentModeKHR>&		presentModes,
+													vk::VkPresentModeKHR					presentMode)
+{
+	const deUint32				imageLayers		= 1u;
+	const vk::VkImageUsageFlags	imageUsage		= properties.supportedUsageFlags;
+	const vk::VkBool32			clipped			= VK_FALSE;
+
+	const deUint32				imageWidth		= (properties.currentExtent.width != 0xFFFFFFFFu)
+													? properties.currentExtent.width
+													: de::min(1024u, properties.minImageExtent.width + ((properties.maxImageExtent.width - properties.minImageExtent.width) / 2));
+	const deUint32				imageHeight		= (properties.currentExtent.height != 0xFFFFFFFFu)
+													? properties.currentExtent.height
+													: de::min(1024u, properties.minImageExtent.height + ((properties.maxImageExtent.height - properties.minImageExtent.height) / 2));
+	const vk::VkExtent2D		imageSize		= { imageWidth, imageHeight };
+
+	{
+		size_t presentModeNdx;
+
+		for (presentModeNdx = 0; presentModeNdx < presentModes.size(); presentModeNdx++)
+		{
+			if (presentModes[presentModeNdx] == presentMode)
+				break;
+		}
+
+		if (presentModeNdx == presentModes.size())
+			TCU_THROW(NotSupportedError, "Present mode not supported");
+	}
+
+	// Pick the first supported transform, alpha, and format:
+	vk::VkSurfaceTransformFlagsKHR transform;
+	for (transform = 1u; transform <= properties.supportedTransforms; transform = transform << 1u)
+	{
+		if ((properties.supportedTransforms & transform) != 0)
+			break;
+    }
+
+	vk::VkCompositeAlphaFlagsKHR alpha;
+	for (alpha = 1u; alpha <= properties.supportedCompositeAlpha; alpha = alpha << 1u)
+	{
+		if ((alpha & properties.supportedCompositeAlpha) != 0)
+			break;
+	}
+
+	{
+		const vk::VkSurfaceTransformFlagBitsKHR	preTransform	= (vk::VkSurfaceTransformFlagBitsKHR)transform;
+		const vk::VkCompositeAlphaFlagBitsKHR	compositeAlpha	= (vk::VkCompositeAlphaFlagBitsKHR)alpha;
+		const vk::VkFormat						imageFormat		= formats[0].format;
+		const vk::VkColorSpaceKHR				imageColorSpace	= formats[0].colorSpace;
+		const vk::VkSwapchainCreateInfoKHR		createInfo		=
+		{
+			vk::VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+			DE_NULL,
+			0u,
+			surface,
+			properties.minImageCount,
+			imageFormat,
+			imageColorSpace,
+			imageSize,
+			imageLayers,
+			imageUsage,
+			vk::VK_SHARING_MODE_EXCLUSIVE,
+			1u,
+			&queueFamilyIndex,
+			preTransform,
+			compositeAlpha,
+			presentMode,
+			clipped,
+			(vk::VkSwapchainKHR)0
+		};
+
+		return createInfo;
+	}
+}
+
+DisplayTimingTestInstance::DisplayTimingTestInstance (Context& context, const TestConfig& testConfig)
+	: TestInstance				(context)
+	, m_useDisplayTiming		(testConfig.useDisplayTiming)
+	, m_quadCount				(16u)
+	, m_vkp						(context.getPlatformInterface())
+	, m_instanceExtensions		(vk::enumerateInstanceExtensionProperties(m_vkp, DE_NULL))
+	, m_instance				(createInstanceWithWsi(m_vkp, m_instanceExtensions, testConfig.wsiType))
+	, m_vki						(m_vkp, *m_instance)
+	, m_physicalDevice			(vk::chooseDevice(m_vki, *m_instance, context.getTestContext().getCommandLine()))
+	, m_nativeDisplay			(createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), m_instanceExtensions, testConfig.wsiType))
+	, m_nativeWindow			(createWindow(*m_nativeDisplay, tcu::nothing<UVec2>()))
+	, m_surface					(vk::wsi::createSurface(m_vki, *m_instance, testConfig.wsiType, *m_nativeDisplay, *m_nativeWindow))
+
+	, m_queueFamilyIndex		(chooseQueueFamilyIndex(m_vki, m_physicalDevice, *m_surface))
+	, m_deviceExtensions		(vk::enumerateDeviceExtensionProperties(m_vki, m_physicalDevice, DE_NULL))
+	, m_device					(createDeviceWithWsi(m_vki, m_physicalDevice, m_deviceExtensions, m_queueFamilyIndex, testConfig.useDisplayTiming))
+	, m_vkd						(m_vki, *m_device)
+	, m_queue					(getDeviceQueue(m_vkd, *m_device, m_queueFamilyIndex, 0u))
+
+	, m_commandPool				(createCommandPool(m_vkd, *m_device, m_queueFamilyIndex))
+	, m_vertexShaderModule		(vk::createShaderModule(m_vkd, *m_device, context.getBinaryCollection().get("quad-vert"), 0u))
+	, m_fragmentShaderModule	(vk::createShaderModule(m_vkd, *m_device, context.getBinaryCollection().get("quad-frag"), 0u))
+	, m_pipelineLayout			(createPipelineLayout(m_vkd, *m_device))
+
+	, m_surfaceProperties		(vk::wsi::getPhysicalDeviceSurfaceCapabilities(m_vki, m_physicalDevice, *m_surface))
+	, m_surfaceFormats			(vk::wsi::getPhysicalDeviceSurfaceFormats(m_vki, m_physicalDevice, *m_surface))
+	, m_presentModes			(vk::wsi::getPhysicalDeviceSurfacePresentModes(m_vki, m_physicalDevice, *m_surface))
+
+	, m_freeAcquireSemaphore	((vk::VkSemaphore)0)
+	, m_freeRenderSemaphore		((vk::VkSemaphore)0)
+
+	, m_swapchainConfig			(createSwapchainConfig(*m_surface, m_queueFamilyIndex, m_surfaceProperties, m_surfaceFormats, m_presentModes, testConfig.presentMode))
+
+	, m_frameCount				(60u * 5u)
+	, m_frameNdx				(0u)
+
+	, m_maxOutOfDateCount		(20u)
+	, m_outOfDateCount			(0u)
+	, m_ExpectImage80Late		(false)
+{
+	{
+		const tcu::ScopedLogSection surfaceInfo (m_context.getTestContext().getLog(), "SurfaceCapabilities", "SurfaceCapabilities");
+		m_context.getTestContext().getLog() << TestLog::Message << m_surfaceProperties << TestLog::EndMessage;
+	}
+}
+
+DisplayTimingTestInstance::~DisplayTimingTestInstance (void)
+{
+	deinitSwapchainResources();
+}
+
+void DisplayTimingTestInstance::initSwapchainResources (void)
+{
+	const size_t		fenceCount	= 6;
+	const deUint32		imageWidth	= m_swapchainConfig.imageExtent.width;
+	const deUint32		imageHeight	= m_swapchainConfig.imageExtent.height;
+	const vk::VkFormat	imageFormat	= m_swapchainConfig.imageFormat;
+
+	m_swapchain				= vk::createSwapchainKHR(m_vkd, *m_device, &m_swapchainConfig);
+	m_swapchainImages		= vk::wsi::getSwapchainImages(m_vkd, *m_device, *m_swapchain);
+
+	m_renderPass			= createRenderPass(m_vkd, *m_device, imageFormat);
+	m_pipeline				= createPipeline(m_vkd, *m_device, *m_renderPass, *m_pipelineLayout, *m_vertexShaderModule, *m_fragmentShaderModule, imageWidth, imageHeight);
+
+	m_swapchainImageViews	= std::vector<vk::VkImageView>(m_swapchainImages.size(), (vk::VkImageView)0);
+	m_framebuffers			= std::vector<vk::VkFramebuffer>(m_swapchainImages.size(), (vk::VkFramebuffer)0);
+	m_acquireSemaphores		= std::vector<vk::VkSemaphore>(m_swapchainImages.size(), (vk::VkSemaphore)0);
+	m_renderSemaphores		= std::vector<vk::VkSemaphore>(m_swapchainImages.size(), (vk::VkSemaphore)0);
+
+	m_fences				= std::vector<vk::VkFence>(fenceCount, (vk::VkFence)0);
+	m_commandBuffers		= std::vector<vk::VkCommandBuffer>(m_fences.size(), (vk::VkCommandBuffer)0);
+
+	m_freeAcquireSemaphore	= (vk::VkSemaphore)0;
+	m_freeRenderSemaphore	= (vk::VkSemaphore)0;
+
+	m_freeAcquireSemaphore	= createSemaphore(m_vkd, *m_device).disown();
+	m_freeRenderSemaphore	= createSemaphore(m_vkd, *m_device).disown();
+
+	initImageViews(m_vkd, *m_device, m_swapchainImages, imageFormat, m_swapchainImageViews);
+	initFramebuffers(m_vkd, *m_device, *m_renderPass, m_swapchainImageViews, imageWidth, imageHeight, m_framebuffers);
+	initSemaphores(m_vkd, *m_device, m_acquireSemaphores);
+	initSemaphores(m_vkd, *m_device, m_renderSemaphores);
+
+	initFences(m_vkd, *m_device, m_fences);
+
+	if (m_useDisplayTiming)
+	{
+		// This portion should do interesting bits
+		m_queuePresentTimes			= std::map<deUint32, deUint64>();
+
+		m_vkd.getRefreshCycleDurationGOOGLE(*m_device, *m_swapchain, &m_rcDuration);
+
+		m_refreshDurationMultiplier	= 1u;
+		m_targetIPD					= m_rcDuration.refreshDuration;
+		m_prevDesiredPresentTime	= 0u;
+		m_nextPresentID				= 0u;
+		m_ignoreThruPresentID		= 0u;
+	}
+}
+
+void DisplayTimingTestInstance::deinitSwapchainResources (void)
+{
+	VK_CHECK(m_vkd.queueWaitIdle(m_queue));
+
+	if (m_freeAcquireSemaphore != (vk::VkSemaphore)0)
+	{
+		m_vkd.destroySemaphore(*m_device, m_freeAcquireSemaphore, DE_NULL);
+		m_freeAcquireSemaphore = (vk::VkSemaphore)0;
+	}
+
+	if (m_freeRenderSemaphore != (vk::VkSemaphore)0)
+	{
+		m_vkd.destroySemaphore(*m_device, m_freeRenderSemaphore, DE_NULL);
+		m_freeRenderSemaphore = (vk::VkSemaphore)0;
+	}
+
+	deinitSemaphores(m_vkd, *m_device, m_acquireSemaphores);
+	deinitSemaphores(m_vkd, *m_device, m_renderSemaphores);
+	deinitFences(m_vkd, *m_device, m_fences);
+	deinitCommandBuffers(m_vkd, *m_device, *m_commandPool, m_commandBuffers);
+	deinitFramebuffers(m_vkd, *m_device, m_framebuffers);
+	deinitImageViews(m_vkd, *m_device, m_swapchainImageViews);
+
+	m_swapchainImages.clear();
+
+	m_swapchain		= vk::Move<vk::VkSwapchainKHR>();
+	m_renderPass	= vk::Move<vk::VkRenderPass>();
+	m_pipeline		= vk::Move<vk::VkPipeline>();
+
+}
+
+vector<vk::VkPastPresentationTimingGOOGLE> getPastPresentationTiming (const vk::DeviceInterface&	vkd,
+																	  vk::VkDevice					device,
+																	  vk::VkSwapchainKHR			swapchain)
+{
+	vector<vk::VkPastPresentationTimingGOOGLE>	pastPresentationTimings;
+	deUint32									numPastPresentationTimings = 0;
+
+	vkd.getPastPresentationTimingGOOGLE(device, swapchain, &numPastPresentationTimings, DE_NULL);
+
+	pastPresentationTimings.resize(numPastPresentationTimings);
+
+	if (numPastPresentationTimings > 0)
+		vkd.getPastPresentationTimingGOOGLE(device, swapchain, &numPastPresentationTimings, &pastPresentationTimings[0]);
+
+	return pastPresentationTimings;
+}
+
+void DisplayTimingTestInstance::render (void)
+{
+	const deUint64		foreverNs		= ~0x0ull;
+	const vk::VkFence	fence			= m_fences[m_frameNdx % m_fences.size()];
+	const deUint32		width			= m_swapchainConfig.imageExtent.width;
+	const deUint32		height			= m_swapchainConfig.imageExtent.height;
+	tcu::TestLog&		log				= m_context.getTestContext().getLog();
+
+	// Throttle execution
+	if (m_frameNdx >= m_fences.size())
+	{
+		VK_CHECK(m_vkd.waitForFences(*m_device, 1u, &fence, VK_TRUE, foreverNs));
+		VK_CHECK(m_vkd.resetFences(*m_device, 1u, &fence));
+
+		m_vkd.freeCommandBuffers(*m_device, *m_commandPool, 1u, &m_commandBuffers[m_frameNdx % m_commandBuffers.size()]);
+		m_commandBuffers[m_frameNdx % m_commandBuffers.size()] = (vk::VkCommandBuffer)0;
+	}
+
+	vk::VkSemaphore		currentAcquireSemaphore	= m_freeAcquireSemaphore;
+	vk::VkSemaphore		currentRenderSemaphore	= m_freeRenderSemaphore;
+	deUint32			imageIndex;
+
+	// Acquire next image
+	VK_CHECK(m_vkd.acquireNextImageKHR(*m_device, *m_swapchain, foreverNs, currentAcquireSemaphore, fence, &imageIndex));
+
+	// Create command buffer
+	m_commandBuffers[m_frameNdx % m_commandBuffers.size()] = createCommandBuffer(m_vkd, *m_device, *m_commandPool, *m_pipelineLayout, *m_renderPass, m_framebuffers[imageIndex], *m_pipeline, m_frameNdx, m_quadCount, width, height).disown();
+
+	// Obtain timing data from previous frames
+	if (m_useDisplayTiming)
+	{
+		const vector<vk::VkPastPresentationTimingGOOGLE>	pastPresentationTimings	(getPastPresentationTiming(m_vkd, *m_device, *m_swapchain));
+		bool												isEarly					= false;
+		bool												isLate					= false;
+
+		for (size_t pastPresentationInfoNdx = 0 ; pastPresentationInfoNdx < pastPresentationTimings.size(); pastPresentationInfoNdx++)
+		{
+			if (m_queuePresentTimes[pastPresentationTimings[pastPresentationInfoNdx].presentID] > pastPresentationTimings[pastPresentationInfoNdx].actualPresentTime)
+			{
+				m_resultCollector.fail("Image with PresentID " + de::toString(pastPresentationTimings[pastPresentationInfoNdx].presentID) + "was displayed before vkQueuePresentKHR was called.");
+			}
+			if (!m_ignoreThruPresentID)
+			{
+				// This is the first time that we've received an
+				// actualPresentTime for this swapchain.  In order to not
+				// perceive these early frames as "late", we need to sync-up
+				// our future desiredPresentTime's with the
+				// actualPresentTime(s) that we're receiving now.
+				const deInt64	multiple	= m_nextPresentID - pastPresentationTimings.back().presentID;
+
+				m_prevDesiredPresentTime	= pastPresentationTimings.back().actualPresentTime + (multiple * m_targetIPD);
+				m_ignoreThruPresentID		= pastPresentationTimings[pastPresentationInfoNdx].presentID + 1;
+			}
+			else if (pastPresentationTimings[pastPresentationInfoNdx].presentID > m_ignoreThruPresentID)
+			{
+				if (pastPresentationTimings[pastPresentationInfoNdx].actualPresentTime > (pastPresentationTimings[pastPresentationInfoNdx].desiredPresentTime + m_rcDuration.refreshDuration + MILLISECOND))
+				{
+					const deUint64 actual	= pastPresentationTimings[pastPresentationInfoNdx].actualPresentTime;
+					const deUint64 desired	= pastPresentationTimings[pastPresentationInfoNdx].desiredPresentTime;
+					const deUint64 rdur		= m_rcDuration.refreshDuration;
+					const deUint64 diff1	= actual - (desired + rdur);
+					const deUint64 diff2	= actual - desired;
+
+					log << TestLog::Message << "Image PresentID " << pastPresentationTimings[pastPresentationInfoNdx].presentID << " was " << diff1 << " nsec late." << TestLog::EndMessage;
+					if (m_ExpectImage80Late && (pastPresentationTimings[pastPresentationInfoNdx].presentID == 80))
+					{
+						if (diff1 > (SECOND / 2))
+							log << TestLog::Message << "\tNote: Image PresentID 80 was expected to be late by approximately 1 second." << TestLog::EndMessage;
+						else
+							m_resultCollector.fail("Image PresentID 80 was not late by approximately 1 second, as expected.");
+					}
+					log << TestLog::Message << "\t\t   actualPresentTime = " << actual << " nsec" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t - desiredPresentTime= " << desired << " nsec" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t =========================================" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t   diff              =       " << diff2 << " nsec" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t - refreshDuration   =       "    << rdur << " nsec" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t =========================================" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t   diff              =        " << diff1 << " nsec" << TestLog::EndMessage;
+
+					isLate = true;
+				}
+				else if ((pastPresentationTimings[pastPresentationInfoNdx].actualPresentTime > pastPresentationTimings[pastPresentationInfoNdx].earliestPresentTime) &&
+						 (pastPresentationTimings[pastPresentationInfoNdx].presentMargin > (2 * MILLISECOND)))
+				{
+					const deUint64 actual	= pastPresentationTimings[pastPresentationInfoNdx].actualPresentTime;
+					const deUint64 earliest	= pastPresentationTimings[pastPresentationInfoNdx].earliestPresentTime;
+					const deUint64 diff		= actual - earliest;
+
+					log << TestLog::Message << "Image PresentID " << pastPresentationTimings[pastPresentationInfoNdx].presentID << " can be presented " << diff << " nsec earlier." << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t   actualPresentTime = " << actual << " nsec" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t -earliestPresentTime= " << earliest << " nsec" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t =========================================" << TestLog::EndMessage;
+					log << TestLog::Message << "\t\t   diff              =        " << diff << " nsec" << TestLog::EndMessage;
+
+					isEarly = true;
+				}
+			}
+		}
+		// Preference is given to late presents over early presents:
+		if (isLate)
+		{
+			// Demonstrate how to slow down the frame rate if a frame is late,
+			// but don't go too slow (for test time reasons):
+			if (++m_refreshDurationMultiplier > 2)
+				m_refreshDurationMultiplier = 2;
+			else
+				log << TestLog::Message << "Increasing multiplier." << TestLog::EndMessage;
+		}
+		else if (isEarly)
+		{
+			// Demonstrate how to speed up the frame rate if a frame is early,
+			// but don't let the multiplier hit zero:
+			if (--m_refreshDurationMultiplier == 0)
+				m_refreshDurationMultiplier = 1;
+			else
+				log << TestLog::Message << "Decreasing multiplier." << TestLog::EndMessage;
+		}
+		m_targetIPD = m_rcDuration.refreshDuration * m_refreshDurationMultiplier;
+	}
+
+	// Submit command buffer
+	{
+		const vk::VkPipelineStageFlags	dstStageMask	= vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		const vk::VkSubmitInfo			submitInfo		=
+		{
+			vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,
+			DE_NULL,
+			1u,
+			&currentAcquireSemaphore,
+			&dstStageMask,
+			1u,
+			&m_commandBuffers[m_frameNdx % m_commandBuffers.size()],
+			1u,
+			&currentRenderSemaphore
+		};
+
+		VK_CHECK(m_vkd.queueSubmit(m_queue, 1u, &submitInfo, (vk::VkFence)0));
+	}
+
+	// Present frame
+	if (m_useDisplayTiming)
+	{
+		// This portion should do interesting bits
+
+		// Initially, mirror reference to move things along
+		vk::VkResult result;
+		vk::VkPresentTimeGOOGLE presentTime =
+		{
+			++m_nextPresentID,
+			m_prevDesiredPresentTime
+		};
+		// Record the current time, to record as the time of the vkQueuePresentKHR() call:
+		const deUint64 curtimeNano = deGetMicroseconds() * 1000;
+		m_queuePresentTimes[m_nextPresentID] = curtimeNano;
+
+		deUint64 desiredPresentTime = 0u;
+		if (m_prevDesiredPresentTime == 0)
+		{
+			// This must be the first present for this swapchain.  Find out the
+			// current time, as the basis for desiredPresentTime:
+			if (curtimeNano != 0)
+			{
+				presentTime.desiredPresentTime = curtimeNano;
+				presentTime.desiredPresentTime += (m_targetIPD / 2);
+			}
+			else
+			{
+				// Since we didn't find out the current time, don't give a
+				// desiredPresentTime:
+				presentTime.desiredPresentTime = 0;
+			}
+		}
+		else
+		{
+			desiredPresentTime = m_prevDesiredPresentTime + m_targetIPD;
+			if (presentTime.presentID == 80)
+			{
+				// Test if desiredPresentTime is 1 second earlier (i.e. before the previous image could have been presented)
+				presentTime.desiredPresentTime -= SECOND;
+				m_ExpectImage80Late = true;
+			}
+		}
+		m_prevDesiredPresentTime = desiredPresentTime;
+
+		const vk::VkPresentTimesInfoGOOGLE presentTimesInfo =
+		{
+			vk::VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
+			DE_NULL,
+			1u,
+			&presentTime
+		};
+		const vk::VkPresentInfoKHR presentInfo =
+		{
+			vk::VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+			&presentTimesInfo,
+			1u,
+			&currentRenderSemaphore,
+			1u,
+			&*m_swapchain,
+			&imageIndex,
+			&result
+		};
+
+		VK_CHECK(m_vkd.queuePresentKHR(m_queue, &presentInfo));
+		VK_CHECK(result);
+	}
+	else
+	{
+		vk::VkResult result;
+		const vk::VkPresentInfoKHR presentInfo =
+		{
+			vk::VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+			DE_NULL,
+			1u,
+			&currentRenderSemaphore,
+			1u,
+			&*m_swapchain,
+			&imageIndex,
+			&result
+		};
+
+		VK_CHECK(m_vkd.queuePresentKHR(m_queue, &presentInfo));
+		VK_CHECK(result);
+	}
+
+	{
+		m_freeAcquireSemaphore = m_acquireSemaphores[imageIndex];
+		m_acquireSemaphores[imageIndex] = currentAcquireSemaphore;
+
+		m_freeRenderSemaphore = m_renderSemaphores[imageIndex];
+		m_renderSemaphores[imageIndex] = currentRenderSemaphore;
+	}
+}
+
+tcu::TestStatus DisplayTimingTestInstance::iterate (void)
+{
+	// Initialize swapchain specific resources
+	// Render test
+	try
+	{
+		if (m_frameNdx == 0)
+		{
+			if (m_outOfDateCount == 0)
+				m_context.getTestContext().getLog() << tcu::TestLog::Message << "Swapchain: " << m_swapchainConfig << tcu::TestLog::EndMessage;
+
+			initSwapchainResources();
+		}
+
+		render();
+	}
+	catch (const vk::Error& error)
+	{
+		if (error.getError() == vk::VK_ERROR_OUT_OF_DATE_KHR)
+		{
+			if (m_outOfDateCount < m_maxOutOfDateCount)
+			{
+				m_context.getTestContext().getLog() << TestLog::Message << "Frame " << m_frameNdx << ": Swapchain out of date. Recreating resources." << TestLog::EndMessage;
+				deinitSwapchainResources();
+				m_frameNdx = 0;
+				m_outOfDateCount++;
+
+				return tcu::TestStatus::incomplete();
+			}
+			else
+			{
+				m_context.getTestContext().getLog() << TestLog::Message << "Frame " << m_frameNdx << ": Swapchain out of date." << TestLog::EndMessage;
+				m_resultCollector.fail("Received too many VK_ERROR_OUT_OF_DATE_KHR errors. Received " + de::toString(m_outOfDateCount) + ", max " + de::toString(m_maxOutOfDateCount));
+			}
+		}
+		else
+		{
+			m_resultCollector.fail(error.what());
+		}
+
+		deinitSwapchainResources();
+
+		return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage());
+	}
+
+	m_frameNdx++;
+
+	if (m_frameNdx >= m_frameCount)
+	{
+		deinitSwapchainResources();
+
+		return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage());
+	}
+	else
+		return tcu::TestStatus::incomplete();
+}
+
+struct Programs
+{
+	static void init (vk::SourceCollections& dst, TestConfig)
+	{
+		dst.glslSources.add("quad-vert") << glu::VertexSource(
+			"#version 450\n"
+			"out gl_PerVertex {\n"
+			"\tvec4 gl_Position;\n"
+			"};\n"
+			"highp float;\n"
+			"void main (void) {\n"
+			"\tgl_Position = vec4(((gl_VertexIndex + 2) / 3) % 2 == 0 ? -1.0 : 1.0,\n"
+			"\t                   ((gl_VertexIndex + 1) / 3) % 2 == 0 ? -1.0 : 1.0, 0.0, 1.0);\n"
+			"}\n");
+		dst.glslSources.add("quad-frag") << glu::FragmentSource(
+			"#version 310 es\n"
+			"layout(location = 0) out highp vec4 o_color;\n"
+			"layout(push_constant) uniform PushConstant {\n"
+			"\thighp uint frameNdx;\n"
+			"} pushConstants;\n"
+			"void main (void)\n"
+			"{\n"
+			"\thighp uint frameNdx = pushConstants.frameNdx;\n"
+			"\thighp uint x = frameNdx + uint(gl_FragCoord.x);\n"
+			"\thighp uint y = frameNdx + uint(gl_FragCoord.y);\n"
+			"\thighp uint r = 128u * bitfieldExtract(x, 0, 1)\n"
+			"\t             +  64u * bitfieldExtract(y, 1, 1)\n"
+			"\t             +  32u * bitfieldExtract(x, 3, 1);\n"
+			"\thighp uint g = 128u * bitfieldExtract(y, 0, 1)\n"
+			"\t             +  64u * bitfieldExtract(x, 2, 1)\n"
+			"\t             +  32u * bitfieldExtract(y, 3, 1);\n"
+			"\thighp uint b = 128u * bitfieldExtract(x, 1, 1)\n"
+			"\t             +  64u * bitfieldExtract(y, 2, 1)\n"
+			"\t             +  32u * bitfieldExtract(x, 4, 1);\n"
+			"\to_color = vec4(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0, 1.0);\n"
+			"}\n");
+	}
+};
+
+} // anonymous
+
+void createDisplayTimingTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
+{
+	const struct
+	{
+		vk::VkPresentModeKHR	mode;
+		const char*				name;
+	} presentModes[] =
+	{
+		{ vk::VK_PRESENT_MODE_FIFO_KHR,			"fifo"			},
+		{ vk::VK_PRESENT_MODE_FIFO_RELAXED_KHR,	"fifo_relaxed"	},
+		{ vk::VK_PRESENT_MODE_IMMEDIATE_KHR,	"immediate"		},
+		{ vk::VK_PRESENT_MODE_MAILBOX_KHR,		"mailbox"		},
+	};
+
+	for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
+	{
+		de::MovePtr<tcu::TestCaseGroup>	presentModeGroup	(new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name, presentModes[presentModeNdx].name));
+
+		for (size_t ref = 0; ref < 2; ref++)
+		{
+			const bool						isReference	= (ref == 0);
+			const char* const				name		= isReference ? "reference" : "display_timing";
+			TestConfig						config;
+
+			config.wsiType					= wsiType;
+			config.useDisplayTiming			= !isReference;
+			config.presentMode				= presentModes[presentModeNdx].mode;
+
+			presentModeGroup->addChild(new vkt::InstanceFactory1<DisplayTimingTestInstance, TestConfig, Programs>(testGroup->getTestContext(), tcu::NODETYPE_SELF_VALIDATE, name, name, Programs(), config));
+		}
+
+		testGroup->addChild(presentModeGroup.release());
+	}
+}
+
+} // wsi
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.hpp b/external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.hpp
new file mode 100644
index 0000000..a500b07
--- /dev/null
+++ b/external/vulkancts/modules/vulkan/wsi/vktWsiDisplayTimingTests.hpp
@@ -0,0 +1,40 @@
+#ifndef _VKTWSIDISPLAYTIMINGTESTS_HPP
+#define _VKTWSIDISPLAYTIMINGTESTS_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tests for VK_GOOGLE_display_timing
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+#include "vkDefs.hpp"
+
+namespace vkt
+{
+namespace wsi
+{
+
+void createDisplayTimingTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType);
+
+} // wsi
+} // vkt
+
+#endif // _VKTWSIDISPLAYTIMINGTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/wsi/vktWsiTests.cpp b/external/vulkancts/modules/vulkan/wsi/vktWsiTests.cpp
index 716a3b9..1c2df16 100644
--- a/external/vulkancts/modules/vulkan/wsi/vktWsiTests.cpp
+++ b/external/vulkancts/modules/vulkan/wsi/vktWsiTests.cpp
@@ -27,6 +27,7 @@
 
 #include "vktWsiSurfaceTests.hpp"
 #include "vktWsiSwapchainTests.hpp"
+#include "vktWsiDisplayTimingTests.hpp"
 
 namespace vkt
 {
@@ -38,8 +39,9 @@
 
 void createTypeSpecificTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
 {
-	addTestGroup(testGroup, "surface",		"VkSurface Tests",		createSurfaceTests,		wsiType);
-	addTestGroup(testGroup, "swapchain",	"VkSwapchain Tests",	createSwapchainTests,	wsiType);
+	addTestGroup(testGroup, "surface",				"VkSurface Tests",				createSurfaceTests,				wsiType);
+	addTestGroup(testGroup, "swapchain",			"VkSwapchain Tests",			createSwapchainTests,			wsiType);
+	addTestGroup(testGroup, "display_timing",		"Display Timing Tests",			createDisplayTimingTests,		wsiType);
 }
 
 void createWsiTests (tcu::TestCaseGroup* apiTests)
diff --git a/external/vulkancts/mustpass/1.0.2/vk-default.txt b/external/vulkancts/mustpass/1.0.2/vk-default.txt
index e8699c9..9113a61 100644
--- a/external/vulkancts/mustpass/1.0.2/vk-default.txt
+++ b/external/vulkancts/mustpass/1.0.2/vk-default.txt
@@ -134377,6 +134377,14 @@
 dEQP-VK.wsi.xlib.swapchain.render.basic
 dEQP-VK.wsi.xlib.swapchain.destroy.null_handle
 dEQP-VK.wsi.xlib.swapchain.get_images.incomplete
+dEQP-VK.wsi.xlib.display_timing.fifo.reference
+dEQP-VK.wsi.xlib.display_timing.fifo.display_timing
+dEQP-VK.wsi.xlib.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.xlib.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.xlib.display_timing.immediate.reference
+dEQP-VK.wsi.xlib.display_timing.immediate.display_timing
+dEQP-VK.wsi.xlib.display_timing.mailbox.reference
+dEQP-VK.wsi.xlib.display_timing.mailbox.display_timing
 dEQP-VK.wsi.xcb.surface.create
 dEQP-VK.wsi.xcb.surface.create_custom_allocator
 dEQP-VK.wsi.xcb.surface.create_simulate_oom
@@ -134410,6 +134418,14 @@
 dEQP-VK.wsi.xcb.swapchain.render.basic
 dEQP-VK.wsi.xcb.swapchain.destroy.null_handle
 dEQP-VK.wsi.xcb.swapchain.get_images.incomplete
+dEQP-VK.wsi.xcb.display_timing.fifo.reference
+dEQP-VK.wsi.xcb.display_timing.fifo.display_timing
+dEQP-VK.wsi.xcb.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.xcb.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.xcb.display_timing.immediate.reference
+dEQP-VK.wsi.xcb.display_timing.immediate.display_timing
+dEQP-VK.wsi.xcb.display_timing.mailbox.reference
+dEQP-VK.wsi.xcb.display_timing.mailbox.display_timing
 dEQP-VK.wsi.wayland.surface.create
 dEQP-VK.wsi.wayland.surface.create_custom_allocator
 dEQP-VK.wsi.wayland.surface.create_simulate_oom
@@ -134442,6 +134458,14 @@
 dEQP-VK.wsi.wayland.swapchain.modify.resize
 dEQP-VK.wsi.wayland.swapchain.destroy.null_handle
 dEQP-VK.wsi.wayland.swapchain.get_images.incomplete
+dEQP-VK.wsi.wayland.display_timing.fifo.reference
+dEQP-VK.wsi.wayland.display_timing.fifo.display_timing
+dEQP-VK.wsi.wayland.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.wayland.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.wayland.display_timing.immediate.reference
+dEQP-VK.wsi.wayland.display_timing.immediate.display_timing
+dEQP-VK.wsi.wayland.display_timing.mailbox.reference
+dEQP-VK.wsi.wayland.display_timing.mailbox.display_timing
 dEQP-VK.wsi.mir.surface.create
 dEQP-VK.wsi.mir.surface.create_custom_allocator
 dEQP-VK.wsi.mir.surface.create_simulate_oom
@@ -134476,6 +134500,14 @@
 dEQP-VK.wsi.mir.swapchain.modify.resize
 dEQP-VK.wsi.mir.swapchain.destroy.null_handle
 dEQP-VK.wsi.mir.swapchain.get_images.incomplete
+dEQP-VK.wsi.mir.display_timing.fifo.reference
+dEQP-VK.wsi.mir.display_timing.fifo.display_timing
+dEQP-VK.wsi.mir.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.mir.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.mir.display_timing.immediate.reference
+dEQP-VK.wsi.mir.display_timing.immediate.display_timing
+dEQP-VK.wsi.mir.display_timing.mailbox.reference
+dEQP-VK.wsi.mir.display_timing.mailbox.display_timing
 dEQP-VK.wsi.android.surface.create
 dEQP-VK.wsi.android.surface.create_custom_allocator
 dEQP-VK.wsi.android.surface.create_simulate_oom
@@ -134509,6 +134541,14 @@
 dEQP-VK.wsi.android.swapchain.modify.resize
 dEQP-VK.wsi.android.swapchain.destroy.null_handle
 dEQP-VK.wsi.android.swapchain.get_images.incomplete
+dEQP-VK.wsi.android.display_timing.fifo.reference
+dEQP-VK.wsi.android.display_timing.fifo.display_timing
+dEQP-VK.wsi.android.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.android.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.android.display_timing.immediate.reference
+dEQP-VK.wsi.android.display_timing.immediate.display_timing
+dEQP-VK.wsi.android.display_timing.mailbox.reference
+dEQP-VK.wsi.android.display_timing.mailbox.display_timing
 dEQP-VK.wsi.win32.surface.create
 dEQP-VK.wsi.win32.surface.create_custom_allocator
 dEQP-VK.wsi.win32.surface.create_simulate_oom
@@ -134542,6 +134582,14 @@
 dEQP-VK.wsi.win32.swapchain.render.basic
 dEQP-VK.wsi.win32.swapchain.destroy.null_handle
 dEQP-VK.wsi.win32.swapchain.get_images.incomplete
+dEQP-VK.wsi.win32.display_timing.fifo.reference
+dEQP-VK.wsi.win32.display_timing.fifo.display_timing
+dEQP-VK.wsi.win32.display_timing.fifo_relaxed.reference
+dEQP-VK.wsi.win32.display_timing.fifo_relaxed.display_timing
+dEQP-VK.wsi.win32.display_timing.immediate.reference
+dEQP-VK.wsi.win32.display_timing.immediate.display_timing
+dEQP-VK.wsi.win32.display_timing.mailbox.reference
+dEQP-VK.wsi.win32.display_timing.mailbox.display_timing
 dEQP-VK.synchronization.smoke.fences
 dEQP-VK.synchronization.smoke.semaphores
 dEQP-VK.synchronization.smoke.events
diff --git a/external/vulkancts/scripts/src/vulkan.h.in b/external/vulkancts/scripts/src/vulkan.h.in
index 54ad8a6..39d2cc9 100644
--- a/external/vulkancts/scripts/src/vulkan.h.in
+++ b/external/vulkancts/scripts/src/vulkan.h.in
@@ -244,6 +244,7 @@
     VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
     VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
     VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
     VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -4627,6 +4628,51 @@
     const void*                                 pData);
 #endif
 
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+    uint64_t    refreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+    uint64_t    actualPresentTime;
+    uint64_t    earliestPresentTime;
+    uint64_t    presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+    VkStructureType               sType;
+    const void*                   pNext;
+    uint32_t                      swapchainCount;
+    const VkPresentTimeGOOGLE*    pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDuratiOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentonTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint32_t*                                   pPresentationTimingCount,
+    VkPastPresentationTimingGOOGLE*             pPresentationTimings);
+#endif
+
 #ifdef __cplusplus
 }
 #endif