layers: Add VUs 1275, 1276, 1277 related to gh1507

Adds 3 new valid usage checks related to the bugfix, and the
layer_validation_tests code to exercise 2 of them. The third check,
01277, cannot be provoked by a test because it would require a
compressed texture format with block z-dimension > 1, which does
not (currently) exist for Vulkan.

Change-Id: I6c467e64c420b6a453af57f7ff86e1465e132988
diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp
index 214b5b5..ef777d7 100644
--- a/layers/buffer_validation.cpp
+++ b/layers/buffer_validation.cpp
@@ -2647,6 +2647,42 @@
                                 function, i, pRegions[i].bufferOffset, block_size_in_bytes,
                                 validation_error_map[VALIDATION_ERROR_01274]);
             }
+
+            // imageExtent width must be a multiple of block width, or extent+offset width must equal subresource width
+            if ((vk_safe_modulo(pRegions[i].imageExtent.width, block_size.width) != 0) &&
+                (pRegions[i].imageExtent.width + pRegions[i].imageOffset.x != image_state->createInfo.extent.width)) {
+                skip |=
+                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT,
+                            reinterpret_cast<uint64_t &>(image_state->image), __LINE__, VALIDATION_ERROR_01275, "IMAGE",
+                            "%s(): pRegion[%d] extent width (%d) must be a multiple of the compressed texture block width (%d), or "
+                            "when added to offset.x (%d) must equal the image subresource width (%d). %s.",
+                            function, i, pRegions[i].imageExtent.width, block_size.width, pRegions[i].imageOffset.x,
+                            image_state->createInfo.extent.width, validation_error_map[VALIDATION_ERROR_01275]);
+            }
+
+            // imageExtent height must be a multiple of block height, or extent+offset height must equal subresource height
+            if ((vk_safe_modulo(pRegions[i].imageExtent.height, block_size.height) != 0) &&
+                (pRegions[i].imageExtent.height + pRegions[i].imageOffset.y != image_state->createInfo.extent.height)) {
+                skip |= log_msg(
+                    report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT,
+                    reinterpret_cast<uint64_t &>(image_state->image), __LINE__, VALIDATION_ERROR_01276, "IMAGE",
+                    "%s(): pRegion[%d] extent height (%d) must be a multiple of the compressed texture block height (%d), or "
+                    "when added to offset.y (%d) must equal the image subresource height (%d). %s.",
+                    function, i, pRegions[i].imageExtent.height, block_size.height, pRegions[i].imageOffset.y,
+                    image_state->createInfo.extent.height, validation_error_map[VALIDATION_ERROR_01276]);
+            }
+
+            // imageExtent depth must be a multiple of block depth, or extent+offset depth must equal subresource depth
+            if ((vk_safe_modulo(pRegions[i].imageExtent.depth, block_size.depth) != 0) &&
+                (pRegions[i].imageExtent.depth + pRegions[i].imageOffset.z != image_state->createInfo.extent.depth)) {
+                skip |=
+                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT,
+                            reinterpret_cast<uint64_t &>(image_state->image), __LINE__, VALIDATION_ERROR_01277, "IMAGE",
+                            "%s(): pRegion[%d] extent width (%d) must be a multiple of the compressed texture block depth (%d), or "
+                            "when added to offset.z (%d) must equal the image subresource depth (%d). %s.",
+                            function, i, pRegions[i].imageExtent.depth, block_size.depth, pRegions[i].imageOffset.z,
+                            image_state->createInfo.extent.depth, validation_error_map[VALIDATION_ERROR_01277]);
+            }
         }
     }
 
diff --git a/layers/vk_validation_error_database.txt b/layers/vk_validation_error_database.txt
index 1276f74..01269bb 100644
--- a/layers/vk_validation_error_database.txt
+++ b/layers/vk_validation_error_database.txt
@@ -1263,9 +1263,9 @@
 VALIDATION_ERROR_01272~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'bufferImageHeight must be a multiple of the compressed texel block height' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
 VALIDATION_ERROR_01273~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'all members of imageOffset must be a multiple of the corresponding dimensions of the compressed texel block' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
 VALIDATION_ERROR_01274~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'bufferOffset must be a multiple of the compressed texel block size in bytes' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
-VALIDATION_ERROR_01275~^~N~^~Unknown~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'imageExtent.width must be a multiple of the compressed texel block width or (imageExtent.width + imageOffset.x) must equal the image subresource width' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
-VALIDATION_ERROR_01276~^~N~^~Unknown~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'imageExtent.height must be a multiple of the compressed texel block height or (imageExtent.height + imageOffset.y) must equal the image subresource height' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
-VALIDATION_ERROR_01277~^~N~^~Unknown~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'imageExtent.depth must be a multiple of the compressed texel block depth or (imageExtent.depth + imageOffset.z) must equal the image subresource depth' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
+VALIDATION_ERROR_01275~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'imageExtent.width must be a multiple of the compressed texel block width or (imageExtent.width + imageOffset.x) must equal the image subresource width' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
+VALIDATION_ERROR_01276~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'imageExtent.height must be a multiple of the compressed texel block height or (imageExtent.height + imageOffset.y) must equal the image subresource height' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
+VALIDATION_ERROR_01277~^~Y~^~None~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'imageExtent.depth must be a multiple of the compressed texel block depth or (imageExtent.depth + imageOffset.z) must equal the image subresource depth' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
 VALIDATION_ERROR_01278~^~N~^~Unknown~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'bufferOffset, bufferRowLength, bufferImageHeight and all members of imageOffset and imageExtent must respect the image transfer granularity requirements of the queue family that it will be submitted against, as described in Physical Device Enumeration' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
 VALIDATION_ERROR_01279~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'The aspectMask member of imageSubresource must specify aspects present in the calling commands VkImage parameter' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
 VALIDATION_ERROR_01280~^~Y~^~ImageBufferCopyTests~^~vkCmdCopyImageToBuffer~^~For more information refer to Vulkan Spec Section '18.4. Copying Data Between Buffers and Images' which states 'The aspectMask member of imageSubresource must only have a single bit set' (https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html#VkBufferImageCopy)~^~
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index f5b00b2..07c5cfa 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -16214,15 +16214,22 @@
           device_features.textureCompressionASTC_LDR)) {
         printf("             No compressed formats supported - block compression tests skipped.\n");
     } else {
-        VkImageObj image_16k_4x4comp(m_device);  // 128^2 texels as 32^2 compressed (4x4) blocks, 16k
+        VkImageObj image_16k_4x4comp(m_device);   // 128^2 texels as 32^2 compressed (4x4) blocks, 16k
+        VkImageObj image_NPOT_4x4comp(m_device);  // 130^2 texels as 33^2 compressed (4x4) blocks
         if (device_features.textureCompressionBC) {
             image_16k_4x4comp.init(128, 128, VK_FORMAT_BC3_SRGB_BLOCK, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
+            image_NPOT_4x4comp.init(130, 130, VK_FORMAT_BC3_SRGB_BLOCK, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_TILING_OPTIMAL,
+                                    0);
         } else if (device_features.textureCompressionETC2) {
             image_16k_4x4comp.init(128, 128, VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
                                    VK_IMAGE_TILING_OPTIMAL, 0);
+            image_NPOT_4x4comp.init(130, 130, VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+                                    VK_IMAGE_TILING_OPTIMAL, 0);
         } else {
             image_16k_4x4comp.init(128, 128, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
                                    VK_IMAGE_TILING_OPTIMAL, 0);
+            image_NPOT_4x4comp.init(130, 130, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+                                    VK_IMAGE_TILING_OPTIMAL, 0);
         }
         ASSERT_TRUE(image_16k_4x4comp.initialized());
 
@@ -16239,13 +16246,39 @@
         vkCmdCopyImageToBuffer(m_commandBuffer->GetBufferHandle(), image_16k_4x4comp.handle(), VK_IMAGE_LAYOUT_GENERAL,
                                buffer_16k.handle(), 1, &region);
         m_errorMonitor->VerifyFound();
+        region.bufferOffset = 0;
 
-        // but, should work with a small extent
-        m_errorMonitor->ExpectSuccess();
+        // extents that are not a multiple of compressed block size
+        m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, VALIDATION_ERROR_01275);
+        region.imageExtent.width = 66;
+        vkCmdCopyImageToBuffer(m_commandBuffer->GetBufferHandle(), image_NPOT_4x4comp.handle(), VK_IMAGE_LAYOUT_GENERAL,
+                               buffer_16k.handle(), 1, &region);
+        m_errorMonitor->VerifyFound();
+        region.imageExtent.width = 128;
+
+        m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, VALIDATION_ERROR_01276);
         region.imageExtent.height = 2;
-        vkCmdCopyImageToBuffer(m_commandBuffer->GetBufferHandle(), image_16k_4x4comp.handle(), VK_IMAGE_LAYOUT_GENERAL,
+        vkCmdCopyImageToBuffer(m_commandBuffer->GetBufferHandle(), image_NPOT_4x4comp.handle(), VK_IMAGE_LAYOUT_GENERAL,
+                               buffer_16k.handle(), 1, &region);
+        m_errorMonitor->VerifyFound();
+        region.imageExtent.height = 128;
+
+        // TODO: All available compressed formats are 2D, with block depth of 1. Unable to provoke VU_01277.
+
+        // non-multiple extents are allowed if at the far edge of a non-block-multiple image - these should pass
+        m_errorMonitor->ExpectSuccess();
+        region.imageExtent.width = 66;
+        region.imageOffset.x = 64;
+        vkCmdCopyImageToBuffer(m_commandBuffer->GetBufferHandle(), image_NPOT_4x4comp.handle(), VK_IMAGE_LAYOUT_GENERAL,
+                               buffer_16k.handle(), 1, &region);
+        region.imageExtent.width = 16;
+        region.imageOffset.x = 0;
+        region.imageExtent.height = 2;
+        region.imageOffset.y = 128;
+        vkCmdCopyImageToBuffer(m_commandBuffer->GetBufferHandle(), image_NPOT_4x4comp.handle(), VK_IMAGE_LAYOUT_GENERAL,
                                buffer_16k.handle(), 1, &region);
         m_errorMonitor->VerifyNotFound();
+        region.imageOffset = {0, 0, 0};
 
         // buffer offset must be a multiple of texel block size (16)
         m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, VALIDATION_ERROR_01274);