layers: incremental_present parameter validation

Add parameter validation for VK_KHR_incremental_present extension.
If the VkPresentRegionsKHR is included down the struct chain for
QueuePresentKHR() validate its parameters.

Change-Id: I97abe552411f229eecbbf7df7d565f7953cdd1b7
diff --git a/layers/parameter_validation.cpp b/layers/parameter_validation.cpp
index 0edce55..f3bdc4a 100644
--- a/layers/parameter_validation.cpp
+++ b/layers/parameter_validation.cpp
@@ -98,6 +98,7 @@
             bool nv_external_memory : 1;
             bool nv_external_memory_win32 : 1;
             bool nvx_device_generated_commands : 1;
+            bool incremental_present : 1;
         };
         uint64_t padding[4];
     } enables;
@@ -1675,6 +1676,8 @@
 #endif
         } else if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME) == 0) {
             device_data->enables.nvx_device_generated_commands = true;
+        } else if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME) == 0) {
+            device_data->enables.incremental_present = true;
         }
     }
 }
@@ -5180,6 +5183,42 @@
                                      VK_KHR_SWAPCHAIN_EXTENSION_NAME);
 
     skip |= parameter_validation_vkQueuePresentKHR(my_data->report_data, pPresentInfo);
+
+    if (pPresentInfo && pPresentInfo->pNext) {
+        // Verify ext struct
+        struct std_header {
+            VkStructureType sType;
+            const void *pNext;
+        };
+        std_header *pnext = (std_header *)pPresentInfo->pNext;
+        while (pnext) {
+            if (VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR == pnext->sType) {
+                skip |= require_device_extension(my_data, my_data->enables.incremental_present, "vkQueuePresentKHR",
+                                                 VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME);
+                VkPresentRegionsKHR *present_regions = (VkPresentRegionsKHR *)pnext;
+                if (present_regions->swapchainCount != pPresentInfo->swapchainCount) {
+                    skip |= log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                    __LINE__, INVALID_USAGE, LayerName,
+                                    "QueuePresentKHR(): pPresentInfo->swapchainCount has a value of %i"
+                                    " but VkPresentRegionsKHR extension swapchainCount is %i. These values must be equal.",
+                                    pPresentInfo->swapchainCount, present_regions->swapchainCount);
+                }
+                skip |= validate_struct_pnext(my_data->report_data, "QueuePresentKHR", "pCreateInfo->pNext->pNext", NULL,
+                                              present_regions->pNext, 0, NULL, GeneratedHeaderVersion);
+                skip |= validate_array(my_data->report_data, "QueuePresentKHR", "pCreateInfo->pNext->swapchainCount",
+                                       "pCreateInfo->pNext->pRegions", present_regions->swapchainCount, present_regions->pRegions,
+                                       true, false);
+                for (uint32_t i = 0; i < present_regions->swapchainCount; ++i) {
+                    skip |=
+                        validate_array(my_data->report_data, "QueuePresentKHR", "pCreateInfo->pNext->pRegions[].rectangleCount",
+                                       "pCreateInfo->pNext->pRegions[].pRectangles", present_regions->pRegions[i].rectangleCount,
+                                       present_regions->pRegions[i].pRectangles, true, false);
+                }
+            }
+            pnext = (std_header *)pnext->pNext;
+        }
+    }
+
     if (!skip) {
         result = my_data->dispatch_table.QueuePresentKHR(queue, pPresentInfo);