layers: multiplane view format compatibility

Add check for compatible format when creating an imageview of a
plane of a multiplane image.

Add test to exercise.

Change-Id: Id3388aa49b42a16e84dc1d459121ee908b2e5f0f
diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp
index 99b3fa0..ca1f57c 100644
--- a/layers/buffer_validation.cpp
+++ b/layers/buffer_validation.cpp
@@ -3375,18 +3375,46 @@
 
         // Validate VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT state
         if (image_flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) {
-            if ((!GetDeviceExtensions(device_data)->vk_khr_maintenance2 ||
-                 !(image_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR))) {
-                // Format MUST be compatible (in the same format compatibility class) as the format the image was created with
-                if (FormatCompatibilityClass(image_format) != FormatCompatibilityClass(view_format)) {
+            if (FormatIsMultiplane(image_format)) {
+                // View format must match the multiplane compatible format
+                uint32_t plane = 3;  // invalid
+                switch (aspect_mask) {
+                    case VK_IMAGE_ASPECT_PLANE_0_BIT:
+                        plane = 0;
+                        break;
+                    case VK_IMAGE_ASPECT_PLANE_1_BIT:
+                        plane = 1;
+                        break;
+                    case VK_IMAGE_ASPECT_PLANE_2_BIT:
+                        plane = 2;
+                        break;
+                    default:
+                        break;
+                }
+
+                VkFormat compat_format = FindMultiplaneCompatibleFormat(image_format, plane);
+                if (view_format != compat_format) {
                     std::stringstream ss;
                     ss << "vkCreateImageView(): ImageView format " << string_VkFormat(view_format)
-                       << " is not in the same format compatibility class as image (" << HandleToUint64(create_info->image)
-                       << ")  format " << string_VkFormat(image_format)
-                       << ".  Images created with the VK_IMAGE_CREATE_MUTABLE_FORMAT BIT "
-                       << "can support ImageViews with differing formats but they must be in the same compatibility class.";
+                       << " is not compatible with plane " << plane << " of underlying image format "
+                       << string_VkFormat(image_format) << ", must be " << string_VkFormat(compat_format) << ".";
                     skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
-                                    VALIDATION_ERROR_0ac007f4, "%s", ss.str().c_str());
+                                    VALIDATION_ERROR_0ac00c64, "%s", ss.str().c_str());
+                }
+            } else {
+                if ((!GetDeviceExtensions(device_data)->vk_khr_maintenance2 ||
+                     !(image_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR))) {
+                    // Format MUST be compatible (in the same format compatibility class) as the format the image was created with
+                    if (FormatCompatibilityClass(image_format) != FormatCompatibilityClass(view_format)) {
+                        std::stringstream ss;
+                        ss << "vkCreateImageView(): ImageView format " << string_VkFormat(view_format)
+                           << " is not in the same format compatibility class as image (" << HandleToUint64(create_info->image)
+                           << ")  format " << string_VkFormat(image_format)
+                           << ".  Images created with the VK_IMAGE_CREATE_MUTABLE_FORMAT BIT "
+                           << "can support ImageViews with differing formats but they must be in the same compatibility class.";
+                        skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                        VALIDATION_ERROR_0ac007f4, "%s", ss.str().c_str());
+                    }
                 }
             }
         } else {
diff --git a/layers/vk_validation_error_database.txt b/layers/vk_validation_error_database.txt
index dd2a09e..d2b7cec 100644
--- a/layers/vk_validation_error_database.txt
+++ b/layers/vk_validation_error_database.txt
@@ -828,7 +828,7 @@
 VALIDATION_ERROR_0ac00c5e~^~N~^~None~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-image-01583~^~(VK_VERSION_1_1,VK_KHR_maintenance2)~^~The spec valid usage text states 'If image was created with the VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT flag, format must be compatible with, or must be an uncompressed format that is size-compatible with, the format used to create image.' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VUID-VkImageViewCreateInfo-image-01583)~^~
 VALIDATION_ERROR_0ac00c60~^~N~^~None~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-image-01584~^~(VK_VERSION_1_1,VK_KHR_maintenance2)~^~The spec valid usage text states 'If image was created with the VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT flag, the levelCount and layerCount members of subresourceRange must both be 1.' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VUID-VkImageViewCreateInfo-image-01584)~^~
 VALIDATION_ERROR_0ac00c62~^~N~^~None~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-pNext-01585~^~(VK_KHR_image_format_list)~^~The spec valid usage text states 'If a VkImageFormatListCreateInfoKHR structure was included in the pNext chain of the VkImageCreateInfo struct used when creating image and the viewFormatCount field of VkImageFormatListCreateInfoKHR is not zero then format must be one of the formats in VkImageFormatListCreateInfoKHR::pViewFormats.' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VUID-VkImageViewCreateInfo-pNext-01585)~^~
-VALIDATION_ERROR_0ac00c64~^~N~^~None~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-image-01586~^~(VK_VERSION_1_1,VK_KHR_sampler_ycbcr_conversion)~^~The spec valid usage text states 'If image was created with the VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT flag, if the format of the image is a multi-planar format, and if subresourceRange.aspectMask is one of VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT, or VK_IMAGE_ASPECT_PLANE_2_BIT, then format must be compatible with the VkFormat for the plane of the image format indicated by subresourceRange.aspectMask, as defined in features-formats-compatible-planes' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VUID-VkImageViewCreateInfo-image-01586)~^~
+VALIDATION_ERROR_0ac00c64~^~Y~^~MultiplaneIncompatibleViewFormat~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-image-01586~^~(VK_VERSION_1_1,VK_KHR_sampler_ycbcr_conversion)~^~The spec valid usage text states 'If image was created with the VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT flag, if the format of the image is a multi-planar format, and if subresourceRange.aspectMask is one of VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT, or VK_IMAGE_ASPECT_PLANE_2_BIT, then format must be compatible with the VkFormat for the plane of the image format indicated by subresourceRange.aspectMask, as defined in features-formats-compatible-planes' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VUID-VkImageViewCreateInfo-image-01586)~^~
 VALIDATION_ERROR_0ac00d6c~^~Y~^~CreateImageViewInvalidSubresourceRange~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-subresourceRange-01718~^~core~^~The spec valid usage text states 'If subresourceRange.levelCount is not VK_REMAINING_MIP_LEVELS, subresourceRange.baseMipLevel + subresourceRange.levelCount must be less than or equal to the mipLevels specified in VkImageCreateInfo when image was created' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkImageViewCreateInfo-subresourceRange-01718)~^~
 VALIDATION_ERROR_0ac00d6e~^~Y~^~CreateImageViewInvalidSubresourceRange~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-subresourceRange-01719~^~!(VK_VERSION_1_1,VK_KHR_maintenance1)~^~The spec valid usage text states 'If subresourceRange.layerCount is not VK_REMAINING_ARRAY_LAYERS, subresourceRange.baseArrayLayer + subresourceRange.layerCount must be less than or equal to the arrayLayers specified in VkImageCreateInfo when image was created' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkImageViewCreateInfo-subresourceRange-01719)~^~
 VALIDATION_ERROR_0ac00dbe~^~N~^~None~^~VkImageViewCreateInfo~^~VUID-VkImageViewCreateInfo-image-01759~^~(VK_VERSION_1_1,VK_KHR_maintenance2)+!(VK_VERSION_1_1,VK_KHR_sampler_ycbcr_conversion)~^~The spec valid usage text states 'If image was created with the VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT flag, but without the VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT flag, format must be compatible with the format used to create image, as defined in Format Compatibility Classes' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkImageViewCreateInfo-image-01759)~^~
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index ac3ed33..e9b94f4 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -18592,7 +18592,6 @@
     VkImage image;
     vkCreateImage(m_device->handle(), &image_create_info, NULL, &image);
     m_errorMonitor->VerifyFound();
-
 }
 
 TEST_F(VkLayerTest, CreateImageViewFormatMismatchUnrelated) {
@@ -18738,6 +18737,112 @@
     m_errorMonitor->VerifyFound();
 }
 
+TEST_F(VkLayerTest, MultiplaneIncompatibleViewFormat) {
+    TEST_DESCRIPTION("Postive test of multiplane format compatibility checks");
+
+    // Enable KHR multiplane req'd extensions
+    bool mp_extensions = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+                                                    VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION);
+    if (mp_extensions) {
+        m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
+    }
+    ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
+    mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_MAINTENANCE1_EXTENSION_NAME);
+    mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
+    mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
+    mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
+    if (mp_extensions) {
+        m_device_extension_names.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
+        m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
+        m_device_extension_names.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
+        m_device_extension_names.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
+    } else {
+        printf("             test requires KHR multiplane extensions, not available.  Skipping.\n");
+        return;
+    }
+    ASSERT_NO_FATAL_FAILURE(InitState());
+
+    VkImageCreateInfo ci = {};
+    ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    ci.pNext = NULL;
+    ci.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+    ci.imageType = VK_IMAGE_TYPE_2D;
+    ci.format = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
+    ci.tiling = VK_IMAGE_TILING_OPTIMAL;
+    ci.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
+    ci.extent = {128, 128, 1};
+    ci.mipLevels = 1;
+    ci.arrayLayers = 1;
+    ci.samples = VK_SAMPLE_COUNT_1_BIT;
+    ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    // Verify format
+    VkFormatFeatureFlags features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
+    bool supported = ImageFormatAndFeaturesSupported(instance(), gpu(), ci, features);
+    if (!supported) {
+        printf("             Multiplane image format not supported.  Skipping test.\n");
+        return;
+    }
+
+    VkImage image;
+    ASSERT_VK_SUCCESS(vkCreateImage(device(), &ci, NULL, &image));
+
+    // Allocate & bind memory
+    VkPhysicalDeviceMemoryProperties phys_mem_props;
+    vkGetPhysicalDeviceMemoryProperties(gpu(), &phys_mem_props);
+    VkMemoryRequirements mem_reqs;
+    vkGetImageMemoryRequirements(device(), image, &mem_reqs);
+    VkDeviceMemory mem_obj = VK_NULL_HANDLE;
+    VkMemoryPropertyFlagBits mem_props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+    for (uint32_t type = 0; type < phys_mem_props.memoryTypeCount; type++) {
+        if ((mem_reqs.memoryTypeBits & (1 << type)) &&
+            ((phys_mem_props.memoryTypes[type].propertyFlags & mem_props) == mem_props)) {
+            VkMemoryAllocateInfo alloc_info = {};
+            alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+            alloc_info.allocationSize = mem_reqs.size;
+            alloc_info.memoryTypeIndex = type;
+            ASSERT_VK_SUCCESS(vkAllocateMemory(device(), &alloc_info, NULL, &mem_obj));
+            break;
+        }
+    }
+
+    if (VK_NULL_HANDLE == mem_obj) {
+        printf("             Unable to allocate image memory. Skipping test.\n");
+        vkDestroyImage(device(), image, NULL);
+        return;
+    }
+    ASSERT_VK_SUCCESS(vkBindImageMemory(device(), image, mem_obj, 0));
+
+    VkImageViewCreateInfo ivci = {};
+    ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    ivci.image = image;
+    ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    ivci.format = VK_FORMAT_R8_SNORM;  // Compat is VK_FORMAT_R8_UNORM
+    ivci.subresourceRange.layerCount = 1;
+    ivci.subresourceRange.baseMipLevel = 0;
+    ivci.subresourceRange.levelCount = 1;
+    ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT;
+
+    // Incompatible format error
+    VkImageView imageView = VK_NULL_HANDLE;
+    m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, VALIDATION_ERROR_0ac00c64);
+    vkCreateImageView(m_device->device(), &ivci, NULL, &imageView);
+    m_errorMonitor->VerifyFound();
+
+    // Correct format succeeds
+    ivci.format = VK_FORMAT_R8_UNORM;
+    m_errorMonitor->ExpectSuccess();
+    vkCreateImageView(m_device->device(), &ivci, NULL, &imageView);
+    m_errorMonitor->VerifyNotFound();
+
+    if (VK_NULL_HANDLE != imageView) {
+        vkDestroyImageView(m_device->device(), imageView, NULL);
+    }
+    vkFreeMemory(device(), mem_obj, NULL);
+    vkDestroyImage(m_device->device(), image, NULL);
+}
+
 TEST_F(VkLayerTest, CreateImageViewInvalidSubresourceRange) {
     TEST_DESCRIPTION("Passing bad image subrange to CreateImageView");