layers: LX196, validate createImage paramters against format limits

Relocated some validation from DeviceLimits layer to Image layer,
added additional per-format limit validation for CreateImage parameters.
diff --git a/layers/image.cpp b/layers/image.cpp
index 59bd108..3a37530 100644
--- a/layers/image.cpp
+++ b/layers/image.cpp
@@ -52,18 +52,21 @@
 using namespace std;
 
 struct layer_data {
-    debug_report_data *report_data;
-    std::vector<VkDbgMsgCallback> logging_callback;
-    VkLayerDispatchTable* device_dispatch_table;
-    VkLayerInstanceDispatchTable* instance_dispatch_table;
-    VkPhysicalDevice physicalDevice;
+    debug_report_data            *report_data;
+    vector<VkDbgMsgCallback>      logging_callback;
+    VkLayerDispatchTable*         device_dispatch_table;
+    VkLayerInstanceDispatchTable *instance_dispatch_table;
+    VkPhysicalDevice              physicalDevice;
+    VkPhysicalDeviceProperties    physicalDeviceProperties;
+
     unordered_map<VkImage, IMAGE_STATE> imageMap;
 
     layer_data() :
         report_data(nullptr),
         device_dispatch_table(nullptr),
         instance_dispatch_table(nullptr),
-        physicalDevice(0)
+        physicalDevice(0),
+        physicalDeviceProperties()
     {};
 };
 
@@ -163,6 +166,8 @@
         device_data->physicalDevice = physicalDevice;
     }
 
+    instance_data->instance_dispatch_table->GetPhysicalDeviceProperties(physicalDevice, &(device_data->physicalDeviceProperties));
+
     return result;
 }
 
@@ -263,26 +268,89 @@
 
 VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage)
 {
-    VkBool32 skipCall = VK_FALSE;
-    layer_data *device_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
-    if(pCreateInfo->format != VK_FORMAT_UNDEFINED)
+    VkBool32                skipCall              = VK_FALSE;
+    VkResult                result                = VK_ERROR_VALIDATION_FAILED;
+    VkImageFormatProperties ImageFormatProperties = {0};
+
+    layer_data       *device_data    = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+    VkPhysicalDevice  physicalDevice = device_data->physicalDevice;
+    layer_data       *phy_dev_data   = get_my_data_ptr(get_dispatch_key(physicalDevice), layer_data_map);
+
+    if (pCreateInfo->format != VK_FORMAT_UNDEFINED)
     {
         VkFormatProperties properties;
-        get_my_data_ptr(get_dispatch_key(device_data->physicalDevice), layer_data_map)->instance_dispatch_table->GetPhysicalDeviceFormatProperties(
+        phy_dev_data->instance_dispatch_table->GetPhysicalDeviceFormatProperties(
                 device_data->physicalDevice, pCreateInfo->format, &properties);
 
-        if((properties.linearTilingFeatures) == 0 && (properties.optimalTilingFeatures == 0))
+        if ((properties.linearTilingFeatures) == 0 && (properties.optimalTilingFeatures == 0))
         {
             char const str[] = "vkCreateImage parameter, VkFormat pCreateInfo->format, contains unsupported format";
             skipCall |= log_msg(device_data->report_data, VK_DBG_REPORT_WARN_BIT, (VkDbgObjectType)0, 0, 0, IMAGE_FORMAT_UNSUPPORTED, "IMAGE", str);
         }
     }
-    if (skipCall)
-        return VK_ERROR_VALIDATION_FAILED;
 
-    VkResult result = device_data->device_dispatch_table->CreateImage(device, pCreateInfo, pAllocator, pImage);
+    // Internal call to get format info.  Still goes through layers, could potentially go directly to ICD.
+    phy_dev_data->instance_dispatch_table->GetPhysicalDeviceImageFormatProperties(
+                       physicalDevice, pCreateInfo->format, pCreateInfo->imageType, pCreateInfo->tiling,
+                       pCreateInfo->usage, pCreateInfo->flags, &ImageFormatProperties);
 
-    if(result == VK_SUCCESS) {
+    VkDeviceSize imageGranularity = device_data->physicalDeviceProperties.limits.bufferImageGranularity;
+    imageGranularity = imageGranularity == 1 ? 0 : imageGranularity;
+
+    if ((pCreateInfo->extent.depth  > ImageFormatProperties.maxExtent.depth)  ||
+        (pCreateInfo->extent.width  > ImageFormatProperties.maxExtent.width)  ||
+        (pCreateInfo->extent.height > ImageFormatProperties.maxExtent.height)) {
+        skipCall |= log_msg(phy_dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_IMAGE, (uint64_t)pImage, 0,
+                        IMAGE_INVALID_FORMAT_LIMITS_VIOLATION, "Image",
+                        "CreateImage extents exceed allowable limits for format: "
+                        "Width = %d Height = %d Depth = %d:  Limits for Width = %d Height = %d Depth = %d for format %s.",
+                        pCreateInfo->extent.width, pCreateInfo->extent.height, pCreateInfo->extent.depth,
+                        ImageFormatProperties.maxExtent.width, ImageFormatProperties.maxExtent.height, ImageFormatProperties.maxExtent.depth,
+                        string_VkFormat(pCreateInfo->format));
+
+    }
+
+    uint64_t totalSize = ((uint64_t)pCreateInfo->extent.width               *
+                          (uint64_t)pCreateInfo->extent.height              *
+                          (uint64_t)pCreateInfo->extent.depth               *
+                          (uint64_t)pCreateInfo->arrayLayers                *
+                          (uint64_t)pCreateInfo->samples                    *
+                          (uint64_t)vk_format_get_size(pCreateInfo->format) +
+                          (uint64_t)imageGranularity ) & ~(uint64_t)imageGranularity;
+
+    if (totalSize > ImageFormatProperties.maxResourceSize) {
+        skipCall |= log_msg(phy_dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_IMAGE, (uint64_t)pImage, 0,
+                        IMAGE_INVALID_FORMAT_LIMITS_VIOLATION, "Image",
+                        "CreateImage resource size exceeds allowable maximum "
+                        "Image resource size = %#" PRIxLEAST64 ", maximum resource size = %#" PRIxLEAST64 " ",
+                        totalSize, ImageFormatProperties.maxResourceSize);
+    }
+
+    if (pCreateInfo->mipLevels > ImageFormatProperties.maxMipLevels) {
+        skipCall |= log_msg(phy_dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_IMAGE, (uint64_t)pImage, 0,
+                        IMAGE_INVALID_FORMAT_LIMITS_VIOLATION, "Image",
+                        "CreateImage mipLevels=%d exceeds allowable maximum supported by format of %d",
+                        pCreateInfo->mipLevels, ImageFormatProperties.maxMipLevels);
+    }
+
+    if (pCreateInfo->arrayLayers > ImageFormatProperties.maxArrayLayers) {
+        skipCall |= log_msg(phy_dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_IMAGE, (uint64_t)pImage, 0,
+                        IMAGE_INVALID_FORMAT_LIMITS_VIOLATION, "Image",
+                        "CreateImage arrayLayers=%d exceeds allowable maximum supported by format of %d",
+                        pCreateInfo->arrayLayers, ImageFormatProperties.maxArrayLayers);
+    }
+
+    if ((pCreateInfo->samples & ImageFormatProperties.sampleCounts) == 0) {
+        skipCall |= log_msg(phy_dev_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_IMAGE, (uint64_t)pImage, 0,
+                        IMAGE_INVALID_FORMAT_LIMITS_VIOLATION, "Image",
+                        "CreateImage samples %s is not supported by format 0x%.8X",
+                        string_VkSampleCountFlagBits(pCreateInfo->samples), ImageFormatProperties.sampleCounts);
+    }
+
+    if (VK_FALSE == skipCall) {
+        result = device_data->device_dispatch_table->CreateImage(device, pCreateInfo, pAllocator, pImage);
+    }
+    if (result == VK_SUCCESS) {
         device_data->imageMap[*pImage] = IMAGE_STATE(pCreateInfo);
     }
     return result;
@@ -988,6 +1056,12 @@
     }
 }
 
+VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties)
+{
+    layer_data *phy_dev_data = get_my_data_ptr(get_dispatch_key(physicalDevice), layer_data_map);
+    phy_dev_data->instance_dispatch_table->GetPhysicalDeviceProperties(physicalDevice, pProperties);
+}
+
 VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* funcName)
 {
     if (device == NULL) {
@@ -1075,6 +1149,8 @@
         return (PFN_vkVoidFunction) vkEnumerateDeviceLayerProperties;
     if (!strcmp(funcName, "vkEnumerateDeviceExtensionProperties"))
         return (PFN_vkVoidFunction) vkEnumerateDeviceExtensionProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceProperties;
 
     PFN_vkVoidFunction fptr = debug_report_get_instance_proc_addr(my_data->report_data, funcName);
     if(fptr)