| /* Copyright (c) 2015-2023 The Khronos Group Inc. |
| * Copyright (c) 2015-2023 Valve Corporation |
| * Copyright (c) 2015-2023 LunarG, Inc. |
| * Copyright (C) 2015-2023 Google Inc. |
| * Modifications Copyright (C) 2020-2022 Advanced Micro Devices, Inc. All rights reserved. |
| * |
| * 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. |
| */ |
| |
| #include <string> |
| #include <sstream> |
| #include <vector> |
| |
| #include "containers/range_vector.h" |
| #include "core_validation.h" |
| #include <vulkan/vk_enum_string_helper.h> |
| #include "generated/chassis.h" |
| |
| // Returns the intersection of the ranges [x, x + x_size) and [y, y + y_size) |
| static sparse_container::range<int64_t> GetRangeIntersection(int64_t x, uint64_t x_size, int64_t y, uint64_t y_size) { |
| int64_t intersection_min = std::max(x, y); |
| int64_t intersection_max = std::min(x + static_cast<int64_t>(x_size), y + static_cast<int64_t>(y_size)); |
| |
| return {intersection_min, intersection_max}; |
| } |
| |
| // Returns true if [x, x + x_size) and [y, y + y_size) overlap |
| static bool RangesIntersect(int64_t x, uint64_t x_size, int64_t y, uint64_t y_size) { |
| auto intersection = GetRangeIntersection(x, x_size, y, y_size); |
| return intersection.non_empty(); |
| } |
| |
| struct ImageRegionIntersection { |
| VkImageSubresourceLayers subresource = {}; |
| VkOffset3D offset = {0, 0, 0}; |
| VkExtent3D extent = {1, 1, 1}; |
| bool has_instersection = false; |
| std::string String() const noexcept { |
| std::stringstream ss; |
| ss << "{ subresource { aspectMask: " << string_VkImageAspectFlags(subresource.aspectMask) |
| << ", mipLevel: " << subresource.mipLevel << ", baseArrayLayer: " << subresource.baseArrayLayer |
| << ", layerCount: " << subresource.layerCount << " }, offset {" << offset.x << ", " << offset.y << ", " << offset.z |
| << "}, extent {" << extent.width << ", " << extent.height << ", " << extent.depth << "} }"; |
| return ss.str(); |
| } |
| }; |
| |
| // Returns true if source area of first vkImageCopy/vkImageCopy2KHR region intersects dest area of second region |
| // It is assumed that these are copy regions within a single image (otherwise no possibility of collision) |
| template <typename RegionType> |
| static ImageRegionIntersection GetRegionIntersection(const RegionType ®ion0, const RegionType ®ion1, VkImageType type, |
| bool is_multiplane) { |
| ImageRegionIntersection result = {}; |
| |
| // Separate planes within a multiplane image cannot intersect |
| if (is_multiplane && (region0.srcSubresource.aspectMask != region1.dstSubresource.aspectMask)) { |
| return result; |
| } |
| auto intersection = GetRangeIntersection(region0.srcSubresource.baseArrayLayer, region0.srcSubresource.layerCount, |
| region1.dstSubresource.baseArrayLayer, region1.dstSubresource.layerCount); |
| if ((region0.srcSubresource.mipLevel == region1.dstSubresource.mipLevel) && intersection.non_empty()) { |
| result.subresource.aspectMask = region0.srcSubresource.aspectMask; |
| result.subresource.baseArrayLayer = static_cast<uint32_t>(intersection.begin); |
| result.subresource.layerCount = static_cast<uint32_t>(intersection.distance()); |
| result.subresource.mipLevel = region0.srcSubresource.mipLevel; |
| result.has_instersection = true; |
| switch (type) { |
| case VK_IMAGE_TYPE_3D: |
| intersection = |
| GetRangeIntersection(region0.srcOffset.z, region0.extent.depth, region1.dstOffset.z, region1.extent.depth); |
| if (intersection.non_empty()) { |
| result.offset.z = static_cast<int32_t>(intersection.begin); |
| result.extent.depth = static_cast<uint32_t>(intersection.distance()); |
| } else { |
| result.has_instersection = false; |
| return result; |
| } |
| [[fallthrough]]; |
| case VK_IMAGE_TYPE_2D: |
| intersection = |
| GetRangeIntersection(region0.srcOffset.y, region0.extent.height, region1.dstOffset.y, region1.extent.height); |
| if (intersection.non_empty()) { |
| result.offset.y = static_cast<int32_t>(intersection.begin); |
| result.extent.height = static_cast<uint32_t>(intersection.distance()); |
| } else { |
| result.has_instersection = false; |
| return result; |
| } |
| [[fallthrough]]; |
| case VK_IMAGE_TYPE_1D: |
| intersection = |
| GetRangeIntersection(region0.srcOffset.x, region0.extent.width, region1.dstOffset.x, region1.extent.width); |
| if (intersection.non_empty()) { |
| result.offset.x = static_cast<int32_t>(intersection.begin); |
| result.extent.width = static_cast<uint32_t>(intersection.distance()); |
| } else { |
| result.has_instersection = false; |
| return result; |
| } |
| break; |
| default: |
| // Unrecognized or new IMAGE_TYPE enums will be caught in parameter_validation |
| assert(false); |
| } |
| } |
| return result; |
| } |
| |
| // Returns true if source area of first vkImageCopy/vkImageCopy2KHR region intersects dest area of second region |
| // It is assumed that these are copy regions within a single image (otherwise no possibility of collision) |
| template <typename RegionType> |
| static bool RegionIntersects(const RegionType *region0, const RegionType *region1, VkImageType type, bool is_multiplane) { |
| return GetRegionIntersection(region0, region1, type, is_multiplane).has_instersection; |
| } |
| |
| template <typename RegionType> |
| static bool RegionIntersectsBlit(const RegionType *region0, const RegionType *region1, VkImageType type, bool is_multiplane) { |
| bool result = false; |
| |
| // Separate planes within a multiplane image cannot intersect |
| if (is_multiplane && (region0->srcSubresource.aspectMask != region1->dstSubresource.aspectMask)) { |
| return result; |
| } |
| |
| if ((region0->srcSubresource.mipLevel == region1->dstSubresource.mipLevel) && |
| (RangesIntersect(region0->srcSubresource.baseArrayLayer, region0->srcSubresource.layerCount, |
| region1->dstSubresource.baseArrayLayer, region1->dstSubresource.layerCount))) { |
| result = true; |
| switch (type) { |
| case VK_IMAGE_TYPE_3D: |
| result &= RangesIntersect(region0->srcOffsets[0].z, region0->srcOffsets[1].z - region0->srcOffsets[0].z, |
| region1->dstOffsets[0].z, region1->dstOffsets[1].z - region1->dstOffsets[0].z); |
| [[fallthrough]]; |
| case VK_IMAGE_TYPE_2D: |
| result &= RangesIntersect(region0->srcOffsets[0].y, region0->srcOffsets[1].y - region0->srcOffsets[0].y, |
| region1->dstOffsets[0].y, region1->dstOffsets[1].y - region1->dstOffsets[0].y); |
| [[fallthrough]]; |
| case VK_IMAGE_TYPE_1D: |
| result &= RangesIntersect(region0->srcOffsets[0].x, region0->srcOffsets[1].x - region0->srcOffsets[0].x, |
| region1->dstOffsets[0].x, region1->dstOffsets[1].x - region1->dstOffsets[0].x); |
| break; |
| default: |
| // Unrecognized or new IMAGE_TYPE enums will be caught in parameter_validation |
| assert(false); |
| } |
| } |
| return result; |
| } |
| |
| // Test if the extent argument has all dimensions set to 0. |
| static inline bool IsExtentAllZeroes(const VkExtent3D &extent) { |
| return ((extent.width == 0) && (extent.height == 0) && (extent.depth == 0)); |
| } |
| |
| // Returns the image transfer granularity for a specific image scaled by compressed block size if necessary. |
| VkExtent3D CoreChecks::GetScaledItg(const CMD_BUFFER_STATE &cb_state, const IMAGE_STATE &image_state) const { |
| // Default to (0, 0, 0) granularity in case we can't find the real granularity for the physical device. |
| VkExtent3D granularity = {0, 0, 0}; |
| const VkFormat image_format = image_state.createInfo.format; |
| const auto pool = cb_state.command_pool; |
| if (pool) { |
| granularity = physical_device_state->queue_family_properties[pool->queueFamilyIndex].minImageTransferGranularity; |
| if (vkuFormatIsBlockedImage(image_format)) { |
| auto block_size = vkuFormatTexelBlockExtent(image_format); |
| granularity.width *= block_size.width; |
| granularity.height *= block_size.height; |
| } |
| } |
| return granularity; |
| } |
| |
| // Test elements of a VkExtent3D structure against alignment constraints contained in another VkExtent3D structure |
| static inline bool IsExtentAligned(const VkExtent3D &extent, const VkExtent3D &granularity) { |
| bool valid = true; |
| if ((SafeModulo(extent.depth, granularity.depth) != 0) || (SafeModulo(extent.width, granularity.width) != 0) || |
| (SafeModulo(extent.height, granularity.height) != 0)) { |
| valid = false; |
| } |
| return valid; |
| } |
| |
| // Check elements of a VkOffset3D structure against a queue family's Image Transfer Granularity values |
| bool CoreChecks::CheckItgOffset(const LogObjectList &objlist, const VkOffset3D &offset, const VkExtent3D &granularity, |
| const Location &offset_loc, const char *vuid) const { |
| bool skip = false; |
| VkExtent3D offset_extent = {}; |
| offset_extent.width = static_cast<uint32_t>(abs(offset.x)); |
| offset_extent.height = static_cast<uint32_t>(abs(offset.y)); |
| offset_extent.depth = static_cast<uint32_t>(abs(offset.z)); |
| if (IsExtentAllZeroes(granularity)) { |
| // If the queue family image transfer granularity is (0, 0, 0), then the offset must always be (0, 0, 0) |
| if (IsExtentAllZeroes(offset_extent) == false) { |
| skip |= LogError(vuid, objlist, offset_loc, |
| "(x=%" PRId32 ", y=%" PRId32 ", z=%" PRId32 |
| ") must be (x=0, y=0, z=0) when the command buffer's queue family " |
| "image transfer granularity is (w=0, h=0, d=0).", |
| offset.x, offset.y, offset.z); |
| } |
| } else { |
| // If the queue family image transfer granularity is not (0, 0, 0), then the offset dimensions must always be even |
| // integer multiples of the image transfer granularity. |
| if (IsExtentAligned(offset_extent, granularity) == false) { |
| skip |= LogError(vuid, objlist, offset_loc, |
| "(x=%" PRId32 ", y=%" PRId32 ", z=%" PRId32 |
| ") dimensions must be even integer multiples of this command " |
| "buffer's queue family image transfer granularity (w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 ").", |
| offset.x, offset.y, offset.z, granularity.width, granularity.height, granularity.depth); |
| } |
| } |
| return skip; |
| } |
| |
| // Test if two VkExtent3D structs are equivalent |
| static inline bool IsExtentEqual(const VkExtent3D &extent, const VkExtent3D &other_extent) { |
| bool result = true; |
| if ((extent.width != other_extent.width) || (extent.height != other_extent.height) || (extent.depth != other_extent.depth)) { |
| result = false; |
| } |
| return result; |
| } |
| |
| // Check elements of a VkExtent3D structure against a queue family's Image Transfer Granularity values |
| bool CoreChecks::CheckItgExtent(const LogObjectList &objlist, const VkExtent3D &extent, const VkOffset3D &offset, |
| const VkExtent3D &granularity, const VkExtent3D &subresource_extent, const VkImageType image_type, |
| const Location &extent_loc, const char *vuid) const { |
| bool skip = false; |
| if (IsExtentAllZeroes(granularity)) { |
| // If the queue family image transfer granularity is (0, 0, 0), then the extent must always match the image |
| // subresource extent. |
| if (IsExtentEqual(extent, subresource_extent) == false) { |
| skip |= LogError(vuid, objlist, extent_loc, |
| "(w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 ") must match the image subresource extents (w=%" PRIu32 |
| ", h=%" PRIu32 ", d=%" PRIu32 |
| ") " |
| "when the command buffer's queue family image transfer granularity is (w=0, h=0, d=0).", |
| extent.width, extent.height, extent.depth, subresource_extent.width, subresource_extent.height, |
| subresource_extent.depth); |
| } |
| } else { |
| // If the queue family image transfer granularity is not (0, 0, 0), then the extent dimensions must always be even |
| // integer multiples of the image transfer granularity or the offset + extent dimensions must always match the image |
| // subresource extent dimensions. |
| VkExtent3D offset_extent_sum = {}; |
| offset_extent_sum.width = static_cast<uint32_t>(abs(offset.x)) + extent.width; |
| offset_extent_sum.height = static_cast<uint32_t>(abs(offset.y)) + extent.height; |
| offset_extent_sum.depth = static_cast<uint32_t>(abs(offset.z)) + extent.depth; |
| bool x_ok = true; |
| bool y_ok = true; |
| bool z_ok = true; |
| switch (image_type) { |
| case VK_IMAGE_TYPE_3D: |
| z_ok = |
| ((0 == SafeModulo(extent.depth, granularity.depth)) || (subresource_extent.depth == offset_extent_sum.depth)); |
| [[fallthrough]]; |
| case VK_IMAGE_TYPE_2D: |
| y_ok = ((0 == SafeModulo(extent.height, granularity.height)) || |
| (subresource_extent.height == offset_extent_sum.height)); |
| [[fallthrough]]; |
| case VK_IMAGE_TYPE_1D: |
| x_ok = |
| ((0 == SafeModulo(extent.width, granularity.width)) || (subresource_extent.width == offset_extent_sum.width)); |
| break; |
| default: |
| // Unrecognized or new IMAGE_TYPE enums will be caught in parameter_validation |
| assert(false); |
| } |
| if (!(x_ok && y_ok && z_ok)) { |
| skip |= LogError(vuid, objlist, extent_loc, |
| "(w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 |
| ") dimensions must be even integer multiples of this command " |
| "buffer's queue family image transfer granularity (w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 |
| ") or offset (x=%" PRId32 ", y=%" PRId32 ", z=%" PRId32 |
| ") + " |
| "extent (w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 |
| ") must match the image subresource extents (w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 ").", |
| extent.width, extent.height, extent.depth, granularity.width, granularity.height, granularity.depth, |
| offset.x, offset.y, offset.z, extent.width, extent.height, extent.depth, subresource_extent.width, |
| subresource_extent.height, subresource_extent.depth); |
| } |
| } |
| return skip; |
| } |
| template <typename HandleT> |
| bool CoreChecks::ValidateImageMipLevel(const HandleT handle, const IMAGE_STATE &image_state, uint32_t mip_level, |
| const Location &mip_loc, const char *vuid) const { |
| bool skip = false; |
| if (mip_level >= image_state.createInfo.mipLevels) { |
| const LogObjectList objlist(handle, image_state.Handle()); |
| skip |= LogError(vuid, objlist, mip_loc, "is %" PRIu32 ", but provided %s has %" PRIu32 " mip levels.", mip_level, |
| FormatHandle(image_state).c_str(), image_state.createInfo.mipLevels); |
| } |
| return skip; |
| } |
| template <typename HandleT> |
| bool CoreChecks::ValidateImageArrayLayerRange(const HandleT handle, const IMAGE_STATE &img, const uint32_t base_layer, |
| const uint32_t layer_count, const Location &subresource_loc, const char *vuid) const { |
| bool skip = false; |
| if (base_layer >= img.createInfo.arrayLayers || layer_count > img.createInfo.arrayLayers || |
| (base_layer + layer_count) > img.createInfo.arrayLayers) { |
| if (layer_count != VK_REMAINING_ARRAY_LAYERS) { |
| const LogObjectList objlist(handle, img.Handle()); |
| skip |= LogError(vuid, objlist, subresource_loc.dot(Field::baseArrayLayer), |
| "is %" PRIu32 " and layerCount is %" PRIu32 ", but provided %s has %" PRIu32 " array layers.", |
| base_layer, layer_count, FormatHandle(img).c_str(), img.createInfo.arrayLayers); |
| } |
| } |
| return skip; |
| } |
| |
| // All VUID from copy_bufferimage_to_imagebuffer_common.txt with more as a result of host_image_copy |
| static const char *GetBufferMemoryImageCopyCommandVUID(const std::string &id, bool from_image, bool copy2, bool is_memory = false) { |
| // clang-format off |
| static const std::map<std::string, std::array<const char *, 6>> copy_imagebuffermemory_vuid = { |
| {"06659", { |
| "VUID-VkBufferImageCopy-imageExtent-06659", // !copy2 & !from_image |
| "VUID-VkBufferImageCopy-imageExtent-06659", // !copy2 & from_image |
| "VUID-VkBufferImageCopy2-imageExtent-06659", // copy2 & !from_image |
| "VUID-VkBufferImageCopy2-imageExtent-06659", // copy2 & from_image |
| "VUID-VkMemoryToImageCopyEXT-imageExtent-06659", // memory & !from_image |
| "VUID-VkImageToMemoryCopyEXT-imageExtent-06659", // memory & from_image |
| }}, |
| {"06660", { |
| "VUID-VkBufferImageCopy-imageExtent-06660", |
| "VUID-VkBufferImageCopy-imageExtent-06660", |
| "VUID-VkBufferImageCopy2-imageExtent-06660", |
| "VUID-VkBufferImageCopy2-imageExtent-06660", |
| "VUID-VkMemoryToImageCopyEXT-imageExtent-06660", |
| "VUID-VkImageToMemoryCopyEXT-imageExtent-06660", |
| }}, |
| {"06661", { |
| "VUID-VkBufferImageCopy-imageExtent-06661", |
| "VUID-VkBufferImageCopy-imageExtent-06661", |
| "VUID-VkBufferImageCopy2-imageExtent-06661", |
| "VUID-VkBufferImageCopy2-imageExtent-06661", |
| "VUID-VkMemoryToImageCopyEXT-imageExtent-06661", |
| "VUID-VkImageToMemoryCopyEXT-imageExtent-06661", |
| }}, |
| {"09101", { |
| "VUID-VkBufferImageCopy-bufferRowLength-09101", |
| "VUID-VkBufferImageCopy-bufferRowLength-09101", |
| "VUID-VkBufferImageCopy2-bufferRowLength-09101", |
| "VUID-VkBufferImageCopy2-bufferRowLength-09101", |
| "VUID-VkMemoryToImageCopyEXT-memoryRowLength-09101", |
| "VUID-VkImageToMemoryCopyEXT-memoryRowLength-09101", |
| }}, |
| {"00196", { |
| "VUID-VkBufferImageCopy-bufferImageHeight-09102", |
| "VUID-VkBufferImageCopy-bufferImageHeight-09102", |
| "VUID-VkBufferImageCopy2-bufferImageHeight-09102", |
| "VUID-VkBufferImageCopy2-bufferImageHeight-09102", |
| "VUID-VkMemoryToImageCopyEXT-memoryImageHeight-09102", |
| "VUID-VkImageToMemoryCopyEXT-memoryImageHeight-09102", |
| }}, |
| {"09103", { |
| "VUID-VkBufferImageCopy-aspectMask-09103", |
| "VUID-VkBufferImageCopy-aspectMask-09103", |
| "VUID-VkBufferImageCopy2-aspectMask-09103", |
| "VUID-VkBufferImageCopy2-aspectMask-09103", |
| "VUID-VkMemoryToImageCopyEXT-aspectMask-09103", |
| "VUID-VkImageToMemoryCopyEXT-aspectMask-09103", |
| }}, |
| {"07975", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07975", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07975", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07975", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07975", |
| kVUIDUndefined, |
| kVUIDUndefined, |
| }}, |
| {"07976", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07976", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07976", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07976", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07976", |
| kVUIDUndefined, |
| kVUIDUndefined, |
| }}, |
| {"00197", { |
| "VUID-vkCmdCopyBufferToImage-imageSubresource-07971", |
| "VUID-vkCmdCopyImageToBuffer-imageSubresource-07971", |
| "VUID-VkCopyBufferToImageInfo2-pRegions-06223", |
| "VUID-VkCopyImageToBufferInfo2-imageOffset-00197", |
| "VUID-VkCopyMemoryToImageInfoEXT-imageSubresource-07971", |
| "VUID-VkCopyImageToMemoryInfoEXT-imageSubresource-07971", |
| }}, |
| {"00198", { |
| "VUID-vkCmdCopyBufferToImage-imageSubresource-07972", |
| "VUID-vkCmdCopyImageToBuffer-imageSubresource-07972", |
| "VUID-VkCopyBufferToImageInfo2-pRegions-06224", |
| "VUID-VkCopyImageToBufferInfo2-imageOffset-00198", |
| "VUID-VkCopyMemoryToImageInfoEXT-imageSubresource-07972", |
| "VUID-VkCopyImageToMemoryInfoEXT-imageSubresource-07972", |
| }}, |
| {"07979", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07979", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07979", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07979", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07979", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07979", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07979", |
| }}, |
| {"09104", { |
| "VUID-vkCmdCopyBufferToImage-imageOffset-09104", |
| "VUID-vkCmdCopyImageToBuffer-imageOffset-09104", |
| "VUID-VkCopyBufferToImageInfo2-imageOffset-09104", |
| "VUID-VkCopyImageToBufferInfo2-imageOffset-09104", |
| "VUID-VkCopyMemoryToImageInfoEXT-imageOffset-09104", |
| "VUID-VkCopyImageToMemoryInfoEXT-imageOffset-09104", |
| }}, |
| {"07980", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07980", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07980", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07980", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07980", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07980", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07980", |
| }}, |
| {"09106", { |
| "VUID-vkCmdCopyBufferToImage-bufferRowLength-09106", |
| "VUID-vkCmdCopyImageToBuffer-bufferRowLength-09106", |
| "VUID-VkCopyBufferToImageInfo2-bufferRowLength-09106", |
| "VUID-VkCopyImageToBufferInfo2-bufferRowLength-09106", |
| "VUID-VkCopyMemoryToImageInfoEXT-memoryRowLength-09106", |
| "VUID-VkCopyImageToMemoryInfoEXT-memoryRowLength-09106", |
| }}, |
| {"09107", { |
| "VUID-vkCmdCopyBufferToImage-bufferImageHeight-09107", |
| "VUID-vkCmdCopyImageToBuffer-bufferImageHeight-09107", |
| "VUID-VkCopyBufferToImageInfo2-bufferImageHeight-09107", |
| "VUID-VkCopyImageToBufferInfo2-bufferImageHeight-09107", |
| "VUID-VkCopyMemoryToImageInfoEXT-memoryImageHeight-09107", |
| "VUID-VkCopyImageToMemoryInfoEXT-memoryImageHeight-09107", |
| }}, |
| {"07274", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07274", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07274", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07274", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07274", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07274", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07274", |
| }}, |
| {"07275", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07275", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07275", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07275", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07275", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07275", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07275", |
| }}, |
| {"07276", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07276", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07276", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07276", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07276", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07276", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07276", |
| }}, |
| {"09108", { |
| "VUID-vkCmdCopyBufferToImage-bufferRowLength-09108", |
| "VUID-vkCmdCopyImageToBuffer-bufferRowLength-09108", |
| "VUID-VkCopyBufferToImageInfo2-bufferRowLength-09108", |
| "VUID-VkCopyImageToBufferInfo2-bufferRowLength-09108", |
| "VUID-VkCopyMemoryToImageInfoEXT-memoryRowLength-09108", |
| "VUID-VkCopyImageToMemoryInfoEXT-memoryRowLength-09108", |
| }}, |
| {"00207", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-00207", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-00207", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-00207", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-00207", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-00207", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-00207", |
| }}, |
| {"00208", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-00208", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-00208", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-00208", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-00208", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-00208", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-00208", |
| }}, |
| {"00209", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-00209", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-00209", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-00209", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-00209", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-00209", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-00209", |
| }}, |
| {"09105", { |
| "VUID-vkCmdCopyBufferToImage-imageSubresource-09105", |
| "VUID-vkCmdCopyImageToBuffer-imageSubresource-09105", |
| "VUID-VkCopyBufferToImageInfo2-imageSubresource-09105", |
| "VUID-VkCopyImageToBufferInfo2-imageSubresource-09105", |
| "VUID-VkCopyMemoryToImageInfoEXT-imageSubresource-09105", |
| "VUID-VkCopyImageToMemoryInfoEXT-imageSubresource-09105", |
| }}, |
| {"07981", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07981", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07981", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07981", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07981", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07981", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07981", |
| }}, |
| {"07983", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07983", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07983", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07983", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07983", |
| "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07983", |
| "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07983", |
| }}, |
| {"04052", { |
| // was split up in 1.3.236 spec (internal MR 5371) |
| "VUID-vkCmdCopyBufferToImage-commandBuffer-07737", |
| "VUID-vkCmdCopyImageToBuffer-commandBuffer-07746", |
| "VUID-vkCmdCopyBufferToImage2-commandBuffer-07737", |
| "VUID-vkCmdCopyImageToBuffer2-commandBuffer-07746", |
| kVUIDUndefined, |
| kVUIDUndefined, |
| }}, |
| {"07978", { |
| "VUID-vkCmdCopyBufferToImage-dstImage-07978", |
| "VUID-vkCmdCopyImageToBuffer-srcImage-07978", |
| "VUID-VkCopyBufferToImageInfo2-dstImage-07978", |
| "VUID-VkCopyImageToBufferInfo2-srcImage-07978", |
| kVUIDUndefined, |
| kVUIDUndefined, |
| }} |
| }; |
| // clang-format on |
| |
| uint8_t index = 0; |
| if (is_memory) { |
| index = 4 + (from_image ? 1 : 0); |
| } else { |
| index |= uint8_t((from_image) ? 0x1 : 0); |
| index |= uint8_t((copy2) ? 0x2 : 0); |
| } |
| return copy_imagebuffermemory_vuid.at(id).at(index); |
| } |
| |
| bool VerifyAspectsPresent(VkImageAspectFlags aspect_mask, VkFormat format) { |
| if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { |
| if (!(vkuFormatIsColor(format) || vkuFormatIsMultiplane(format))) return false; |
| } |
| if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { |
| if (!vkuFormatHasDepth(format)) return false; |
| } |
| if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) { |
| if (!vkuFormatHasStencil(format)) return false; |
| } |
| if (0 != (aspect_mask & (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT))) { |
| if (vkuFormatPlaneCount(format) == 1) return false; |
| } |
| return true; |
| } |
| |
| template <typename T> |
| uint32_t GetRowLength(T data) { |
| return data.bufferRowLength; |
| } |
| template <> |
| uint32_t GetRowLength<VkMemoryToImageCopyEXT>(VkMemoryToImageCopyEXT data) { |
| return data.memoryRowLength; |
| } |
| template <> |
| uint32_t GetRowLength<VkImageToMemoryCopyEXT>(VkImageToMemoryCopyEXT data) { |
| return data.memoryRowLength; |
| } |
| template <typename T> |
| uint32_t GetImageHeight(T data) { |
| return data.bufferImageHeight; |
| } |
| template <> |
| uint32_t GetImageHeight<VkMemoryToImageCopyEXT>(VkMemoryToImageCopyEXT data) { |
| return data.memoryImageHeight; |
| } |
| template <> |
| uint32_t GetImageHeight<VkImageToMemoryCopyEXT>(VkImageToMemoryCopyEXT data) { |
| return data.memoryImageHeight; |
| } |
| template <typename HandleT, typename RegionType> |
| bool CoreChecks::ValidateHeterogeneousCopyData(const HandleT handle, uint32_t regionCount, const RegionType *pRegions, |
| const IMAGE_STATE &image_state, const Location &loc) const { |
| bool skip = false; |
| const bool is_2 = IsValueIn(loc.function, {Func::vkCmdCopyBufferToImage2, Func::vkCmdCopyBufferToImage2KHR}); |
| const bool from_image = IsValueIn(loc.function, {Func::vkCmdCopyImageToBuffer, Func::vkCmdCopyImageToBuffer2, |
| Func::vkCmdCopyImageToBuffer2KHR, Func::vkCopyImageToMemoryEXT}); |
| const bool is_memory = IsValueIn(loc.function, {Func::vkCopyMemoryToImageEXT, Func::vkCopyImageToMemoryEXT}); |
| |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location subresource_loc = region_loc.dot(Field::imageSubresource); |
| const RegionType region = pRegions[i]; |
| const VkImageAspectFlags region_aspect_mask = region.imageSubresource.aspectMask; |
| if (image_state.createInfo.imageType == VK_IMAGE_TYPE_1D) { |
| if ((region.imageOffset.y != 0) || (region.imageExtent.height != 1)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07979", from_image, is_2, is_memory), objlist, region_loc, |
| "imageOffset.y is %" PRId32 " and imageExtent.height is %" PRIu32 |
| ". For 1D images these must be 0 " |
| "and 1, respectively.", |
| region.imageOffset.y, region.imageExtent.height); |
| } |
| } |
| |
| if ((image_state.createInfo.imageType == VK_IMAGE_TYPE_1D) || (image_state.createInfo.imageType == VK_IMAGE_TYPE_2D)) { |
| if ((region.imageOffset.z != 0) || (region.imageExtent.depth != 1)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07980", from_image, is_2, is_memory), objlist, region_loc, |
| "imageOffset.z is %" PRId32 " and imageExtent.depth is %" PRIu32 |
| ". For 1D and 2D images these " |
| "must be 0 and 1, respectively.", |
| region.imageOffset.z, region.imageExtent.depth); |
| } |
| } |
| |
| if (image_state.createInfo.imageType == VK_IMAGE_TYPE_3D) { |
| if ((0 != region.imageSubresource.baseArrayLayer) || (1 != region.imageSubresource.layerCount)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= |
| LogError(GetBufferMemoryImageCopyCommandVUID("07983", from_image, is_2, is_memory), objlist, subresource_loc, |
| "baseArrayLayer is %" PRIu32 " and layerCount is %" PRIu32 |
| ". " |
| "For 3D images these must be 0 and 1, respectively.", |
| region.imageSubresource.baseArrayLayer, region.imageSubresource.layerCount); |
| } |
| } |
| |
| // Make sure not a empty region |
| if (region.imageExtent.width == 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("06659", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageExtent).dot(Field::width), "is zero (empty copies are not allowed)."); |
| } |
| if (region.imageExtent.height == 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("06660", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageExtent).dot(Field::height), "is zero (empty copies are not allowed)."); |
| } |
| if (region.imageExtent.depth == 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("06661", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageExtent).dot(Field::depth), "is zero (empty copies are not allowed)."); |
| } |
| |
| // BufferRowLength must be 0, or greater than or equal to the width member of imageExtent |
| uint32_t row_length = GetRowLength(region); |
| if ((row_length != 0) && (row_length < region.imageExtent.width)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| Field field = is_memory ? Field::memoryRowLength : Field::bufferRowLength; |
| skip |= |
| LogError(GetBufferMemoryImageCopyCommandVUID("09101", from_image, is_2, is_memory), objlist, region_loc.dot(field), |
| "(%" PRIu32 ") must be zero or greater-than-or-equal-to imageExtent.width (%" PRIu32 ").", row_length, |
| region.imageExtent.width); |
| } |
| |
| // BufferImageHeight must be 0, or greater than or equal to the height member of imageExtent |
| uint32_t image_height = GetImageHeight(region); |
| if ((image_height != 0) && (image_height < region.imageExtent.height)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| Field field = is_memory ? Field::memoryImageHeight : Field::bufferImageHeight; |
| skip |= |
| LogError(GetBufferMemoryImageCopyCommandVUID("00196", from_image, is_2, is_memory), objlist, region_loc.dot(field), |
| "(%" PRIu32 ") must be zero or greater-than-or-equal-to imageExtent.height (%" PRIu32 ").", image_height, |
| region.imageExtent.height); |
| } |
| |
| // subresource aspectMask must have exactly 1 bit set |
| if (GetBitSetCount(region_aspect_mask) != 1) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("09103", from_image, is_2, is_memory), objlist, |
| subresource_loc.dot(Field::aspectMask), "is %s (only one bit allowed).", |
| string_VkImageAspectFlags(region_aspect_mask).c_str()); |
| } |
| |
| // Calculate adjusted image extent, accounting for multiplane image factors |
| VkExtent3D adjusted_image_extent = image_state.GetEffectiveSubresourceExtent(region.imageSubresource); |
| // imageOffset.x and (imageExtent.width + imageOffset.x) must both be >= 0 and <= image subresource width |
| if ((region.imageOffset.x < 0) || (region.imageOffset.x > static_cast<int32_t>(adjusted_image_extent.width)) || |
| ((region.imageOffset.x + static_cast<int32_t>(region.imageExtent.width)) > |
| static_cast<int32_t>(adjusted_image_extent.width))) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("00197", from_image, is_2, is_memory), objlist, region_loc, |
| "imageOffset.x (%" PRId32 ") and (imageExtent.width + imageOffset.x) (%" PRIu32 |
| ") must be >= " |
| "zero or <= image subresource width (%" PRIu32 ").", |
| region.imageOffset.x, (region.imageOffset.x + region.imageExtent.width), adjusted_image_extent.width); |
| } |
| |
| // imageOffset.y and (imageExtent.height + imageOffset.y) must both be >= 0 and <= image subresource height |
| if ((region.imageOffset.y < 0) || (region.imageOffset.y > static_cast<int32_t>(adjusted_image_extent.height)) || |
| ((region.imageOffset.y + static_cast<int32_t>(region.imageExtent.height)) > |
| static_cast<int32_t>(adjusted_image_extent.height))) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= |
| LogError(GetBufferMemoryImageCopyCommandVUID("00198", from_image, is_2, is_memory), objlist, region_loc, |
| "imageOffset.y (%" PRId32 ") and (imageExtent.height + imageOffset.y) (%" PRIu32 |
| ") must be >= " |
| "zero or <= image subresource height (%" PRIu32 ").", |
| region.imageOffset.y, (region.imageOffset.y + region.imageExtent.height), adjusted_image_extent.height); |
| } |
| |
| // imageOffset.z and (imageExtent.depth + imageOffset.z) must both be >= 0 and <= image subresource depth |
| if ((region.imageOffset.z < 0) || (region.imageOffset.z > static_cast<int32_t>(adjusted_image_extent.depth)) || |
| ((region.imageOffset.z + static_cast<int32_t>(region.imageExtent.depth)) > |
| static_cast<int32_t>(adjusted_image_extent.depth))) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("09104", from_image, is_2, is_memory), objlist, region_loc, |
| "imageOffset.z (%" PRId32 ") and (imageExtent.depth + imageOffset.z) (%" PRIu32 |
| ") must be >= " |
| "zero or <= image subresource depth (%" PRIu32 ").", |
| region.imageOffset.z, (region.imageOffset.z + region.imageExtent.depth), adjusted_image_extent.depth); |
| } |
| |
| const VkFormat image_format = image_state.createInfo.format; |
| // image subresource aspect bit must match format |
| if (!VerifyAspectsPresent(region_aspect_mask, image_format)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("09105", from_image, is_2, is_memory), objlist, |
| subresource_loc.dot(Field::aspectMask), "%s invalid for image format %s.", |
| string_VkImageAspectFlags(region_aspect_mask).c_str(), string_VkFormat(image_format)); |
| } |
| |
| auto block_size = vkuFormatTexelBlockExtent(image_format); |
| // BufferRowLength must be a multiple of block width |
| if (SafeModulo(row_length, block_size.width) != 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| Field field = is_memory ? Field::memoryRowLength : Field::bufferRowLength; |
| skip |= LogError( |
| GetBufferMemoryImageCopyCommandVUID("09106", from_image, is_2, is_memory), objlist, region_loc.dot(field), |
| "(%" PRIu32 ") must be a multiple of the blocked image's texel width (%" PRIu32 ").", row_length, block_size.width); |
| } |
| |
| // BufferRowHeight must be a multiple of block height |
| if (SafeModulo(image_height, block_size.height) != 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| Field field = is_memory ? Field::memoryImageHeight : Field::bufferImageHeight; |
| skip |= |
| LogError(GetBufferMemoryImageCopyCommandVUID("09107", from_image, is_2, is_memory), objlist, region_loc.dot(field), |
| "(%" PRIu32 ") must be a multiple of the blocked image's texel height (%" PRIu32 ").", image_height, |
| block_size.height); |
| } |
| |
| // image offsets x must be multiple of block width |
| if (SafeModulo(region.imageOffset.x, block_size.width) != 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07274", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageOffset).dot(Field::x), |
| "(%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "width (%" PRIu32 ").", |
| region.imageOffset.x, block_size.width); |
| } |
| |
| // image offsets y must be multiple of block height |
| if (SafeModulo(region.imageOffset.y, block_size.height) != 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07275", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageOffset).dot(Field::y), |
| "(%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "height (%" PRIu32 ").", |
| region.imageOffset.y, block_size.height); |
| } |
| |
| // image offsets z must be multiple of block depth |
| if (SafeModulo(region.imageOffset.z, block_size.depth) != 0) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07276", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageOffset).dot(Field::z), |
| "(%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "depth (%" PRIu32 ").", |
| region.imageOffset.z, block_size.depth); |
| } |
| |
| // imageExtent width must be a multiple of block width, or extent+offset width must equal subresource width |
| VkExtent3D mip_extent = image_state.GetEffectiveSubresourceExtent(region.imageSubresource); |
| if ((SafeModulo(region.imageExtent.width, block_size.width) != 0) && |
| (region.imageExtent.width + region.imageOffset.x != mip_extent.width)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("00207", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageExtent).dot(Field::width), |
| "(%" PRIu32 |
| ") must be a multiple of the blocked texture block width " |
| "(%" PRIu32 "), or when added to imageOffset.x (%" PRId32 |
| ") must equal the image subresource width (%" PRIu32 ").", |
| region.imageExtent.width, block_size.width, region.imageOffset.x, mip_extent.width); |
| } |
| |
| // imageExtent height must be a multiple of block height, or extent+offset height must equal subresource height |
| if ((SafeModulo(region.imageExtent.height, block_size.height) != 0) && |
| (region.imageExtent.height + region.imageOffset.y != mip_extent.height)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("00208", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageExtent).dot(Field::height), |
| "(%" PRIu32 |
| ") must be a multiple of the blocked texture block height " |
| "(%" PRIu32 "), or when added to imageOffset.y (%" PRId32 |
| ") must equal the image subresource height (%" PRIu32 ").", |
| region.imageExtent.height, block_size.height, region.imageOffset.y, mip_extent.height); |
| } |
| |
| // imageExtent depth must be a multiple of block depth, or extent+offset depth must equal subresource depth |
| if ((SafeModulo(region.imageExtent.depth, block_size.depth) != 0) && |
| (region.imageExtent.depth + region.imageOffset.z != mip_extent.depth)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("00209", from_image, is_2, is_memory), objlist, |
| region_loc.dot(Field::imageExtent).dot(Field::depth), |
| "(%" PRIu32 |
| ") must be a multiple of the blocked texture block depth " |
| "(%" PRIu32 "), or when added to imageOffset.z (%" PRId32 |
| ") must equal the image subresource depth (%" PRIu32 ").", |
| region.imageExtent.depth, block_size.depth, region.imageOffset.z, mip_extent.depth); |
| } |
| |
| // *RowLength divided by the texel block extent width and then multiplied by the texel block size of the image must be |
| // less than or equal to 2^31-1 |
| const uint32_t element_size = |
| vkuFormatIsDepthOrStencil(image_format) ? 0 : vkuFormatElementSizeWithAspect(image_format, static_cast<VkImageAspectFlagBits>(region_aspect_mask)); |
| double test_value = row_length / block_size.width; |
| test_value = test_value * element_size; |
| const auto two_to_31_minus_1 = static_cast<double>((1u << 31) - 1); |
| if (test_value > two_to_31_minus_1) { |
| const LogObjectList objlist(handle, image_state.image()); |
| Field field = is_memory ? Field::memoryRowLength : Field::bufferRowLength; |
| skip |= |
| LogError(GetBufferMemoryImageCopyCommandVUID("09108", from_image, is_2, is_memory), objlist, region_loc.dot(field), |
| "(%" PRIu32 ") divided by the texel block extent width (%" PRIu32 |
| ") then multiplied by the " |
| "texel block size of image (%" PRIu32 ") is (%" PRIu64 ") which is greater than 2^31 - 1", |
| row_length, block_size.width, element_size, static_cast<uint64_t>(test_value)); |
| } |
| |
| // Checks that apply only to multi-planar format images |
| if (vkuFormatIsMultiplane(image_format) && !IsOnlyOneValidPlaneAspect(image_format, region_aspect_mask)) { |
| const LogObjectList objlist(handle, image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07981", from_image, is_2, is_memory), objlist, |
| subresource_loc.dot(Field::aspectMask), "(%s) is invalid for multi-planar format %s.", |
| string_VkImageAspectFlags(region_aspect_mask).c_str(), string_VkFormat(image_format)); |
| } |
| } |
| return skip; |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateBufferImageCopyData(const CMD_BUFFER_STATE &cb_state, uint32_t regionCount, const RegionType *pRegions, |
| const IMAGE_STATE &image_state, const Location &loc) const { |
| bool skip = false; |
| |
| const bool is_2 = IsValueIn(loc.function, {Func::vkCmdCopyBufferToImage2, Func::vkCmdCopyBufferToImage2KHR}); |
| const bool image_to_buffer = |
| IsValueIn(loc.function, {Func::vkCmdCopyImageToBuffer, Func::vkCmdCopyImageToBuffer2, Func::vkCmdCopyImageToBuffer2KHR}); |
| |
| const VkFormat image_format = image_state.createInfo.format; |
| |
| skip |= ValidateHeterogeneousCopyData(cb_state.commandBuffer(), regionCount, pRegions, image_state, loc); |
| |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const RegionType region = pRegions[i]; |
| const VkImageAspectFlags region_aspect_mask = region.imageSubresource.aspectMask; |
| |
| // If the the calling command's VkImage parameter's format is not a depth/stencil format, |
| // then bufferOffset must be a multiple of the calling command's VkImage parameter's element size |
| const uint32_t element_size = |
| vkuFormatIsDepthOrStencil(image_format) ? 0 : vkuFormatElementSizeWithAspect(image_format, static_cast<VkImageAspectFlagBits>(region_aspect_mask)); |
| const VkDeviceSize bufferOffset = region.bufferOffset; |
| |
| if (vkuFormatIsDepthOrStencil(image_format)) { |
| if (SafeModulo(bufferOffset, 4) != 0) { |
| const LogObjectList objlist(cb_state.commandBuffer(), image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07978", image_to_buffer, is_2), objlist, |
| region_loc.dot(Field::bufferOffset), |
| "(%" PRIu64 ") must be a multiple 4 if using a depth/stencil format (%s).", bufferOffset, |
| string_VkFormat(image_format)); |
| } |
| } else { |
| // If not depth/stencil and not multi-plane |
| if (!vkuFormatIsMultiplane(image_format) && (SafeModulo(bufferOffset, element_size) != 0)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07975", image_to_buffer, is_2), objlist, |
| region_loc.dot(Field::bufferOffset), |
| "(%" PRIu64 ") must be a multiple of %s texel size (%" PRIu32 ").", bufferOffset, |
| string_VkFormat(image_format), element_size); |
| } |
| } |
| |
| // Checks that apply only to multi-planar format images |
| if (vkuFormatIsMultiplane(image_format)) { |
| |
| // image subresource aspectMask must be VK_IMAGE_ASPECT_PLANE_*_BIT |
| if (0 != |
| (region_aspect_mask & (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT))) { |
| // Know aspect mask is valid |
| const VkFormat compatible_format = vkuFindMultiplaneCompatibleFormat(image_format, static_cast<VkImageAspectFlagBits>(region_aspect_mask)); |
| const uint32_t compatible_size = vkuFormatElementSize(compatible_format); |
| if (SafeModulo(bufferOffset, compatible_size) != 0) { |
| const LogObjectList objlist(cb_state.commandBuffer(), image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("07976", image_to_buffer, is_2), objlist, |
| region_loc.dot(Field::bufferOffset), |
| "(%" PRIu64 ") is not a multiple of %s texel size (%" PRIu32 ") for plane %" PRIu32 " (%s).", |
| bufferOffset, string_VkFormat(image_format), element_size, vkuGetPlaneIndex(static_cast<VkImageAspectFlagBits>(region_aspect_mask)), |
| string_VkFormat(compatible_format)); |
| } |
| } |
| } |
| |
| // TODO - Don't use ValidateCmdQueueFlags due to currently not having way to add more descriptive message |
| const COMMAND_POOL_STATE *command_pool = cb_state.command_pool; |
| assert(command_pool != nullptr); |
| const uint32_t queue_family_index = command_pool->queueFamilyIndex; |
| const VkQueueFlags queue_flags = physical_device_state->queue_family_properties[queue_family_index].queueFlags; |
| if (((queue_flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) == 0) && (SafeModulo(bufferOffset, 4) != 0)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), command_pool->commandPool(), image_state.image()); |
| skip |= LogError(GetBufferMemoryImageCopyCommandVUID("04052", image_to_buffer, is_2), objlist, |
| region_loc.dot(Field::bufferOffset), |
| "(%" PRIu64 |
| ") is not a multiple of 4 because, but the command buffer %s was allocated from the command pool %s " |
| "which was created with queueFamilyIndex %" PRIu32 " of type %s.", |
| bufferOffset, FormatHandle(cb_state).c_str(), FormatHandle(command_pool->commandPool()).c_str(), |
| queue_family_index, string_VkQueueFlags(queue_flags).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdCopyBufferBounds(VkCommandBuffer cb, const BUFFER_STATE &src_buffer_state, |
| const BUFFER_STATE &dst_buffer_state, uint32_t regionCount, const RegionType *pRegions, |
| const Location &loc) const { |
| bool skip = false; |
| const bool is_2 = loc.function == Func::vkCmdCopyBuffer2 || loc.function == Func::vkCmdCopyBuffer2KHR; |
| const char *vuid; |
| |
| VkDeviceSize src_buffer_size = src_buffer_state.createInfo.size; |
| VkDeviceSize dst_buffer_size = dst_buffer_state.createInfo.size; |
| const bool are_buffers_sparse = src_buffer_state.sparse || dst_buffer_state.sparse; |
| |
| const LogObjectList src_objlist(cb, dst_buffer_state.Handle()); |
| const LogObjectList dst_objlist(cb, dst_buffer_state.Handle()); |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const RegionType region = pRegions[i]; |
| |
| // The srcOffset member of each element of pRegions must be less than the size of srcBuffer |
| if (region.srcOffset >= src_buffer_size) { |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-srcOffset-00113" : "VUID-vkCmdCopyBuffer-srcOffset-00113"; |
| skip |= LogError(vuid, src_objlist, region_loc.dot(Field::srcOffset), |
| "(%" PRIuLEAST64 ") is greater than size of srcBuffer (%" PRIuLEAST64 ").", region.srcOffset, |
| src_buffer_size); |
| } |
| |
| // The dstOffset member of each element of pRegions must be less than the size of dstBuffer |
| if (region.dstOffset >= dst_buffer_size) { |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-dstOffset-00114" : "VUID-vkCmdCopyBuffer-dstOffset-00114"; |
| skip |= LogError(vuid, dst_objlist, region_loc.dot(Field::dstOffset), |
| "(%" PRIuLEAST64 ") is greater than size of dstBuffer (%" PRIuLEAST64 ").", region.dstOffset, |
| dst_buffer_size); |
| } |
| |
| // The size member of each element of pRegions must be less than or equal to the size of srcBuffer minus srcOffset |
| if (region.size > (src_buffer_size - region.srcOffset)) { |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-size-00115" : "VUID-vkCmdCopyBuffer-size-00115"; |
| skip |= LogError(vuid, src_objlist, region_loc.dot(Field::size), |
| "(%" PRIuLEAST64 ") is greater than the source buffer size (%" PRIuLEAST64 |
| ") minus srcOffset (%" PRIuLEAST64 ").", |
| region.size, src_buffer_size, region.srcOffset); |
| } |
| |
| // The size member of each element of pRegions must be less than or equal to the size of dstBuffer minus dstOffset |
| if (region.size > (dst_buffer_size - region.dstOffset)) { |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-size-00116" : "VUID-vkCmdCopyBuffer-size-00116"; |
| skip |= LogError(vuid, dst_objlist, region_loc.dot(Field::size), |
| "(%" PRIuLEAST64 ") is greater than the destination buffer size (%" PRIuLEAST64 |
| ") minus dstOffset (%" PRIuLEAST64 ").", |
| region.size, dst_buffer_size, region.dstOffset); |
| } |
| |
| // The union of the source regions, and the union of the destination regions, must not overlap in memory |
| if (!skip && !are_buffers_sparse) { |
| auto src_region = sparse_container::range<VkDeviceSize>{region.srcOffset, region.srcOffset + region.size}; |
| for (uint32_t j = 0; j < regionCount; j++) { |
| auto dst_region = |
| sparse_container::range<VkDeviceSize>{pRegions[j].dstOffset, pRegions[j].dstOffset + pRegions[j].size}; |
| if (src_buffer_state.DoesResourceMemoryOverlap(src_region, &dst_buffer_state, dst_region)) { |
| const LogObjectList objlist(cb, src_buffer_state.Handle(), dst_buffer_state.Handle()); |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-pRegions-00117" : "VUID-vkCmdCopyBuffer-pRegions-00117"; |
| skip |= LogError(vuid, objlist, region_loc, "Detected overlap between source and dest regions in memory."); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, |
| const RegionType *pRegions, const Location &loc) const { |
| bool skip = false; |
| auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_buffer_state = Get<BUFFER_STATE>(srcBuffer); |
| auto dst_buffer_state = Get<BUFFER_STATE>(dstBuffer); |
| if (!cb_state_ptr || !src_buffer_state || !dst_buffer_state) { |
| return skip; |
| } |
| const CMD_BUFFER_STATE &cb_state = *cb_state_ptr; |
| |
| const bool is_2 = loc.function == Func::vkCmdCopyBuffer2 || loc.function == Func::vkCmdCopyBuffer2KHR; |
| const char *vuid; |
| const Location src_buffer_loc = loc.dot(Field::srcBuffer); |
| const Location dst_buffer_loc = loc.dot(Field::dstBuffer); |
| |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-srcBuffer-00119" : "VUID-vkCmdCopyBuffer-srcBuffer-00119"; |
| skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *src_buffer_state, src_buffer_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-dstBuffer-00121" : "VUID-vkCmdCopyBuffer-dstBuffer-00121"; |
| skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_buffer_state, dst_buffer_loc, vuid); |
| |
| // Validate that SRC & DST buffers have correct usage flags set |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-srcBuffer-00118" : "VUID-vkCmdCopyBuffer-srcBuffer-00118"; |
| skip |= ValidateBufferUsageFlags(LogObjectList(commandBuffer, srcBuffer), *src_buffer_state, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, |
| true, vuid, src_buffer_loc); |
| vuid = is_2 ? "VUID-VkCopyBufferInfo2-dstBuffer-00120" : "VUID-vkCmdCopyBuffer-dstBuffer-00120"; |
| skip |= ValidateBufferUsageFlags(LogObjectList(commandBuffer, dstBuffer), *dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, |
| true, vuid, dst_buffer_loc); |
| |
| skip |= ValidateCmd(cb_state, loc); |
| skip |= ValidateCmdCopyBufferBounds(commandBuffer, *src_buffer_state, *dst_buffer_state, regionCount, pRegions, loc); |
| |
| vuid = is_2 ? "VUID-vkCmdCopyBuffer2-commandBuffer-01822" : "VUID-vkCmdCopyBuffer-commandBuffer-01822"; |
| skip |= ValidateProtectedBuffer(cb_state, *src_buffer_state, src_buffer_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyBuffer2-commandBuffer-01823" : "VUID-vkCmdCopyBuffer-commandBuffer-01823"; |
| skip |= ValidateProtectedBuffer(cb_state, *dst_buffer_state, dst_buffer_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyBuffer2-commandBuffer-01824" : "VUID-vkCmdCopyBuffer-commandBuffer-01824"; |
| skip |= ValidateUnprotectedBuffer(cb_state, *dst_buffer_state, dst_buffer_loc, vuid); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, |
| uint32_t regionCount, const VkBufferCopy *pRegions, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions, error_obj.location); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyBuffer2KHR(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR *pCopyBufferInfo, |
| const ErrorObject &error_obj) const { |
| return PreCallValidateCmdCopyBuffer2(commandBuffer, pCopyBufferInfo, error_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2 *pCopyBufferInfo, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdCopyBuffer(commandBuffer, pCopyBufferInfo->srcBuffer, pCopyBufferInfo->dstBuffer, |
| pCopyBufferInfo->regionCount, pCopyBufferInfo->pRegions, |
| error_obj.location.dot(Field::pCopyBufferInfo)); |
| } |
| |
| // Check valid usage Image Transfer Granularity requirements for elements of a VkBufferImageCopy/VkBufferImageCopy2 structure |
| template <typename RegionType> |
| bool CoreChecks::ValidateCopyBufferImageTransferGranularityRequirements(const CMD_BUFFER_STATE &cb_state, |
| const IMAGE_STATE &image_state, const RegionType *region, |
| const Location ®ion_loc, const char *vuid) const { |
| bool skip = false; |
| const LogObjectList objlist(cb_state.Handle(), image_state.Handle()); |
| VkExtent3D granularity = GetScaledItg(cb_state, image_state); |
| skip |= CheckItgOffset(objlist, region->imageOffset, granularity, region_loc.dot(Field::imageOffset), vuid); |
| VkExtent3D subresource_extent = image_state.GetEffectiveSubresourceExtent(region->imageSubresource); |
| skip |= CheckItgExtent(objlist, region->imageExtent, region->imageOffset, granularity, subresource_extent, |
| image_state.createInfo.imageType, region_loc.dot(Field::imageExtent), vuid); |
| return skip; |
| } |
| |
| template <typename HandleT> |
| bool CoreChecks::ValidateImageSubresourceLayers(HandleT handle, const VkImageSubresourceLayers *subresource_layers, |
| const Location &subresource_loc) const { |
| bool skip = false; |
| const VkImageAspectFlags aspect_mask = subresource_layers->aspectMask; |
| if (subresource_layers->layerCount == VK_REMAINING_ARRAY_LAYERS) { |
| if (!enabled_features.maintenance5_features.maintenance5) { |
| skip |= LogError("VUID-VkImageSubresourceLayers-layerCount-09243", handle, subresource_loc.dot(Field::layerCount), |
| "is VK_REMAINING_ARRAY_LAYERS."); |
| } |
| } else if (subresource_layers->layerCount == 0) { |
| skip |= |
| LogError("VUID-VkImageSubresourceLayers-layerCount-01700", handle, subresource_loc.dot(Field::layerCount), "is zero."); |
| } |
| // aspectMask must not contain VK_IMAGE_ASPECT_METADATA_BIT |
| if (aspect_mask & VK_IMAGE_ASPECT_METADATA_BIT) { |
| skip |= LogError("VUID-VkImageSubresourceLayers-aspectMask-00168", handle, subresource_loc.dot(Field::aspectMask), "is %s.", |
| string_VkImageAspectFlags(aspect_mask).c_str()); |
| } |
| // if aspectMask contains COLOR, it must not contain either DEPTH or STENCIL |
| if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) && (aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) { |
| skip |= LogError("VUID-VkImageSubresourceLayers-aspectMask-00167", handle, subresource_loc.dot(Field::aspectMask), "is %s.", |
| string_VkImageAspectFlags(aspect_mask).c_str()); |
| } |
| // aspectMask must not contain VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT |
| if (aspect_mask & (VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT | |
| VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT)) { |
| skip |= LogError("VUID-VkImageSubresourceLayers-aspectMask-02247", handle, subresource_loc.dot(Field::aspectMask), "is %s.", |
| string_VkImageAspectFlags(aspect_mask).c_str()); |
| } |
| return skip; |
| } |
| |
| // Check valid usage Image Transfer Granularity requirements for elements of a VkImageCopy/VkImageCopy2KHR structure |
| template <typename RegionType> |
| bool CoreChecks::ValidateCopyImageTransferGranularityRequirements(const CMD_BUFFER_STATE &cb_state, |
| const IMAGE_STATE &src_image_state, |
| const IMAGE_STATE &dst_image_state, const RegionType *region, |
| const Location ®ion_loc) const { |
| bool skip = false; |
| const bool is_2 = region_loc.function == Func::vkCmdCopyImage2 || region_loc.function == Func::vkCmdCopyImage2KHR; |
| const char *vuid; |
| |
| const VkExtent3D extent = region->extent; |
| { |
| // Source image checks |
| const LogObjectList objlist(cb_state.Handle(), src_image_state.Handle()); |
| const VkExtent3D granularity = GetScaledItg(cb_state, src_image_state); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcOffset-01783" : "VUID-vkCmdCopyImage-srcOffset-01783"; |
| skip |= CheckItgOffset(objlist, region->srcOffset, granularity, region_loc.dot(Field::srcOffset), vuid); |
| const VkExtent3D subresource_extent = src_image_state.GetEffectiveSubresourceExtent(region->srcSubresource); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcOffset-01783" : "VUID-vkCmdCopyImage-srcOffset-01783"; |
| skip |= CheckItgExtent(objlist, extent, region->srcOffset, granularity, subresource_extent, |
| src_image_state.createInfo.imageType, region_loc.dot(Field::extent), vuid); |
| } |
| |
| { |
| // Destination image checks |
| const LogObjectList objlist(cb_state.Handle(), dst_image_state.Handle()); |
| const VkExtent3D granularity = GetScaledItg(cb_state, dst_image_state); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstOffset-01784" : "VUID-vkCmdCopyImage-dstOffset-01784"; |
| skip |= CheckItgOffset(objlist, region->dstOffset, granularity, region_loc.dot(Field::dstOffset), vuid); |
| // Adjust dest extent, if necessary |
| const VkExtent3D dest_effective_extent = |
| GetAdjustedDestImageExtent(src_image_state.createInfo.format, dst_image_state.createInfo.format, extent); |
| const VkExtent3D subresource_extent = dst_image_state.GetEffectiveSubresourceExtent(region->dstSubresource); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstOffset-01784" : "VUID-vkCmdCopyImage-dstOffset-01784"; |
| skip |= CheckItgExtent(objlist, dest_effective_extent, region->dstOffset, granularity, subresource_extent, |
| dst_image_state.createInfo.imageType, region_loc.dot(Field::extent), vuid); |
| } |
| return skip; |
| } |
| |
| static const char *GetImageCopyVUID(const std::string &id, bool copy2, bool host_version) { |
| // clang-format off |
| static const std::map<std::string, std::array<const char *, 3>> copy_image_vuid = { |
| {"00146", { |
| "VUID-vkCmdCopyImage-srcImage-00146", // !copy2 & !host_version |
| "VUID-VkCopyImageInfo2-srcImage-00146", // copy2 && !host_version |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07979", // host_version |
| }}, |
| {"01785", { |
| "VUID-vkCmdCopyImage-srcImage-01785", |
| "VUID-VkCopyImageInfo2-srcImage-01785", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07980", |
| }}, |
| {"07278", { |
| "VUID-vkCmdCopyImage-pRegions-07278", |
| "VUID-VkCopyImageInfo2-pRegions-07278", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07274", |
| }}, |
| {"07279", { |
| "VUID-vkCmdCopyImage-pRegions-07279", |
| "VUID-VkCopyImageInfo2-pRegions-07279", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07275", |
| }}, |
| {"07280", { |
| "VUID-vkCmdCopyImage-pRegions-07280", |
| "VUID-VkCopyImageInfo2-pRegions-07280", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07276", |
| }}, |
| {"01728", { |
| "VUID-vkCmdCopyImage-srcImage-01728", |
| "VUID-VkCopyImageInfo2-srcImage-01728", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-00207", |
| }}, |
| {"01729", { |
| "VUID-vkCmdCopyImage-srcImage-01729", |
| "VUID-VkCopyImageInfo2-srcImage-01729", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-00208", |
| }}, |
| {"01730", { |
| "VUID-vkCmdCopyImage-srcImage-01730", |
| "VUID-VkCopyImageInfo2-srcImage-01730", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-00209", |
| }}, |
| {"00152", { |
| "VUID-vkCmdCopyImage-dstImage-00152", |
| "VUID-VkCopyImageInfo2-dstImage-00152", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07979", |
| }}, |
| {"01786", { |
| "VUID-vkCmdCopyImage-dstImage-01786", |
| "VUID-VkCopyImageInfo2-dstImage-01786", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07980", |
| }}, |
| {"04443", { |
| "VUID-vkCmdCopyImage-srcImage-04443", |
| "VUID-VkCopyImageInfo2-srcImage-04443", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07983", |
| }}, |
| {"04444", { |
| "VUID-vkCmdCopyImage-dstImage-04444", |
| "VUID-VkCopyImageInfo2-dstImage-04444", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07983", |
| }}, |
| {"07281", { |
| "VUID-vkCmdCopyImage-pRegions-07281", |
| "VUID-VkCopyImageInfo2-pRegions-07281", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07274", |
| }}, |
| {"07282", { |
| "VUID-vkCmdCopyImage-pRegions-07282", |
| "VUID-VkCopyImageInfo2-pRegions-07282", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07275", |
| }}, |
| {"07283", { |
| "VUID-vkCmdCopyImage-pRegions-07283", |
| "VUID-VkCopyImageInfo2-pRegions-07283", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07276", |
| }}, |
| {"01732", { |
| "VUID-vkCmdCopyImage-dstImage-01732", |
| "VUID-VkCopyImageInfo2-dstImage-01732", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-00207", |
| }}, |
| {"01733", { |
| "VUID-vkCmdCopyImage-dstImage-01733", |
| "VUID-VkCopyImageInfo2-dstImage-01733", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-00208", |
| }}, |
| {"01734", { |
| "VUID-vkCmdCopyImage-dstImage-01734", |
| "VUID-VkCopyImageInfo2-dstImage-01734", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-00209", |
| }}, |
| {"07967src", { |
| "VUID-vkCmdCopyImage-srcSubresource-07967", |
| "VUID-VkCopyImageInfo2-srcSubresource-07967", |
| "VUID-VkCopyImageToImageInfoEXT-srcSubresource-07967", |
| }}, |
| {"07967dst", { |
| "VUID-vkCmdCopyImage-dstSubresource-07967", |
| "VUID-VkCopyImageInfo2-dstSubresource-07967", |
| "VUID-VkCopyImageToImageInfoEXT-dstSubresource-07967", |
| }}, |
| {"07968src", { |
| "VUID-vkCmdCopyImage-srcSubresource-07968", |
| "VUID-VkCopyImageInfo2-srcSubresource-07968", |
| "VUID-VkCopyImageToImageInfoEXT-srcSubresource-07968", |
| }}, |
| {"07968dst", { |
| "VUID-vkCmdCopyImage-dstSubresource-07968", |
| "VUID-VkCopyImageInfo2-dstSubresource-07968", |
| "VUID-VkCopyImageToImageInfoEXT-dstSubresource-07968", |
| }}, |
| {"00142", { |
| "VUID-vkCmdCopyImage-aspectMask-00142", |
| "VUID-VkCopyImageInfo2-aspectMask-00142", |
| "VUID-VkCopyImageToImageInfoEXT-srcSubresource-09105", |
| }}, |
| {"00143", { |
| "VUID-vkCmdCopyImage-aspectMask-00143", |
| "VUID-VkCopyImageInfo2-aspectMask-00143", |
| "VUID-VkCopyImageToImageInfoEXT-dstSubresource-09105", |
| }}, |
| {"00144", { |
| "VUID-vkCmdCopyImage-srcOffset-00144", |
| "VUID-VkCopyImageInfo2-srcOffset-00144", |
| "VUID-VkCopyImageToImageInfoEXT-srcSubresource-07971", |
| }}, |
| {"00145", { |
| "VUID-vkCmdCopyImage-srcOffset-00145", |
| "VUID-VkCopyImageInfo2-srcOffset-00145", |
| "VUID-VkCopyImageToImageInfoEXT-srcSubresource-07972", |
| }}, |
| {"00147", { |
| "VUID-vkCmdCopyImage-srcOffset-00147", |
| "VUID-VkCopyImageInfo2-srcOffset-00147", |
| "VUID-VkCopyImageToImageInfoEXT-srcOffset-09104", |
| }}, |
| {"00150", { |
| "VUID-vkCmdCopyImage-dstOffset-00150", |
| "VUID-VkCopyImageInfo2-dstOffset-00150", |
| "VUID-VkCopyImageToImageInfoEXT-dstSubresource-07971", |
| }}, |
| {"00151", { |
| "VUID-vkCmdCopyImage-dstOffset-00151", |
| "VUID-VkCopyImageInfo2-dstOffset-00151", |
| "VUID-VkCopyImageToImageInfoEXT-dstSubresource-07972", |
| }}, |
| {"00153", { |
| "VUID-vkCmdCopyImage-dstOffset-00153", |
| "VUID-VkCopyImageInfo2-dstOffset-00153", |
| "VUID-VkCopyImageToImageInfoEXT-dstOffset-09104", |
| }}, |
| {"07966src", { |
| "VUID-vkCmdCopyImage-srcImage-07966", |
| "VUID-VkCopyImageInfo2-srcImage-07966", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07966", |
| }}, |
| {"07966dst", { |
| "VUID-vkCmdCopyImage-dstImage-07966", |
| "VUID-VkCopyImageInfo2-dstImage-07966", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07966", |
| }}, |
| {"07969src", { |
| "VUID-vkCmdCopyImage-srcImage-07969", |
| "VUID-VkCopyImageInfo2-srcImage-07969", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-07969", |
| }}, |
| {"07969dst", { |
| "VUID-vkCmdCopyImage-dstImage-07969", |
| "VUID-VkCopyImageInfo2-dstImage-07969", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-07969", |
| }}, |
| }; |
| // clang-format on |
| uint8_t index = 0; |
| |
| if (host_version) { |
| index = 2; |
| } else if (copy2) { |
| index = 1; |
| } |
| return copy_image_vuid.at(id).at(index); |
| } |
| |
| // Validate contents of a VkImageCopy or VkImageCopy2KHR struct |
| template <typename HandleT, typename RegionType> |
| bool CoreChecks::ValidateImageCopyData(const HandleT handle, const uint32_t regionCount, const RegionType *pRegions, |
| const IMAGE_STATE &src_image_state, const IMAGE_STATE &dst_image_state, bool is_host, |
| const Location &loc) const { |
| bool skip = false; |
| const bool is_2 = loc.function == Func::vkCmdCopyImage2 || loc.function == Func::vkCmdCopyImage2KHR; |
| const char *vuid; |
| |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const RegionType region = pRegions[i]; |
| |
| // For comp<->uncomp copies, the copy extent for the dest image must be adjusted |
| const VkExtent3D src_copy_extent = region.extent; |
| const VkExtent3D dst_copy_extent = |
| GetAdjustedDestImageExtent(src_image_state.createInfo.format, dst_image_state.createInfo.format, region.extent); |
| |
| bool slice_override = false; |
| uint32_t depth_slices = 0; |
| |
| // Special case for copying between a 1D/2D array and a 3D image |
| // TBD: This seems like the only way to reconcile 3 mutually-exclusive VU checks for 2D/3D copies. Heads up. |
| if ((VK_IMAGE_TYPE_3D == src_image_state.createInfo.imageType) && |
| (VK_IMAGE_TYPE_3D != dst_image_state.createInfo.imageType)) { |
| depth_slices = region.dstSubresource.layerCount; // Slice count from 2D subresource |
| slice_override = (depth_slices != 1); |
| } else if ((VK_IMAGE_TYPE_3D == dst_image_state.createInfo.imageType) && |
| (VK_IMAGE_TYPE_3D != src_image_state.createInfo.imageType)) { |
| depth_slices = region.srcSubresource.layerCount; // Slice count from 2D subresource |
| slice_override = (depth_slices != 1); |
| } |
| |
| // Do all checks on source image |
| if (src_image_state.createInfo.imageType == VK_IMAGE_TYPE_1D) { |
| if ((0 != region.srcOffset.y) || (1 != src_copy_extent.height)) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("00146", is_2, is_host), objlist, region_loc, |
| "srcOffset.y is %" PRId32 " and extent.height is %" PRIu32 |
| ". For 1D images these must " |
| "be 0 and 1, respectively.", |
| region.srcOffset.y, src_copy_extent.height); |
| } |
| } |
| |
| if (((src_image_state.createInfo.imageType == VK_IMAGE_TYPE_1D) || |
| ((src_image_state.createInfo.imageType == VK_IMAGE_TYPE_2D) && is_host)) && |
| ((0 != region.srcOffset.z) || (1 != src_copy_extent.depth))) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| const char *image_type = is_host ? "1D or 2D" : "1D"; |
| skip |= LogError(GetImageCopyVUID("01785", is_2, is_host), objlist, region_loc, |
| "srcOffset.z is %" PRId32 " and extent.depth is %" PRIu32 |
| ". For %s images " |
| "these must be 0 and 1, respectively.", |
| region.srcOffset.z, src_copy_extent.depth, image_type); |
| } |
| |
| if ((src_image_state.createInfo.imageType == VK_IMAGE_TYPE_2D) && (0 != region.srcOffset.z) && (!is_host)) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01787" : "VUID-vkCmdCopyImage-srcImage-01787"; |
| skip |= LogError(vuid, objlist, region_loc, "srcOffset.z is %" PRId32 ". For 2D images the z-offset must be 0.", |
| region.srcOffset.z); |
| } |
| |
| { // Used to be compressed checks, now apply to all |
| const VkExtent3D block_size = vkuFormatTexelBlockExtent(src_image_state.createInfo.format); |
| if (SafeModulo(region.srcOffset.x, block_size.width) != 0) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("07278", is_2, is_host), objlist, region_loc, |
| "srcOffset.x (%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "width (%" PRIu32 ").", |
| region.srcOffset.x, block_size.width); |
| } |
| |
| // image offsets y must be multiple of block height |
| if (SafeModulo(region.srcOffset.y, block_size.height) != 0) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("07279", is_2, is_host), objlist, region_loc, |
| "srcOffset.y (%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "height (%" PRIu32 ").", |
| region.srcOffset.y, block_size.height); |
| } |
| |
| // image offsets z must be multiple of block depth |
| if (SafeModulo(region.srcOffset.z, block_size.depth) != 0) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("07280", is_2, is_host), objlist, region_loc, |
| "srcOffset.z (%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "depth (%" PRIu32 ").", |
| region.srcOffset.z, block_size.depth); |
| } |
| |
| const VkExtent3D mip_extent = src_image_state.GetEffectiveSubresourceExtent(region.srcSubresource); |
| if ((SafeModulo(src_copy_extent.width, block_size.width) != 0) && |
| (src_copy_extent.width + region.srcOffset.x != mip_extent.width)) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("01728", is_2, is_host), objlist, region_loc, |
| "extent width (%" PRIu32 |
| ") must be a multiple of the blocked texture block " |
| "width (%" PRIu32 "), or when added to srcOffset.x (%" PRId32 |
| ") must equal the image subresource width (%" PRIu32 ").", |
| src_copy_extent.width, block_size.width, region.srcOffset.x, mip_extent.width); |
| } |
| |
| // Extent height must be a multiple of block height, or extent+offset height must equal subresource height |
| if ((SafeModulo(src_copy_extent.height, block_size.height) != 0) && |
| (src_copy_extent.height + region.srcOffset.y != mip_extent.height)) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("01729", is_2, is_host), objlist, region_loc, |
| "extent height (%" PRIu32 |
| ") must be a multiple of the compressed texture block " |
| "height (%" PRIu32 "), or when added to srcOffset.y (%" PRId32 |
| ") must equal the image subresource height (%" PRIu32 ").", |
| src_copy_extent.height, block_size.height, region.srcOffset.y, mip_extent.height); |
| } |
| |
| // Extent depth must be a multiple of block depth, or extent+offset depth must equal subresource depth |
| uint32_t copy_depth = (slice_override ? depth_slices : src_copy_extent.depth); |
| if ((SafeModulo(copy_depth, block_size.depth) != 0) && (copy_depth + region.srcOffset.z != mip_extent.depth)) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("01730", is_2, is_host), objlist, region_loc, |
| "extent width (%" PRIu32 |
| ") must be a multiple of the compressed texture block " |
| "depth (%" PRIu32 "), or when added to srcOffset.z (%" PRId32 |
| ") must equal the image subresource depth (%" PRIu32 ").", |
| src_copy_extent.depth, block_size.depth, region.srcOffset.z, mip_extent.depth); |
| } |
| } |
| |
| // Do all checks on dest image |
| if (dst_image_state.createInfo.imageType == VK_IMAGE_TYPE_1D) { |
| if ((0 != region.dstOffset.y) || (1 != dst_copy_extent.height)) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("00152", is_2, is_host), objlist, region_loc, |
| "dstOffset.y is %" PRId32 " and dst_copy_extent.height is %" PRIu32 |
| ". For 1D images " |
| "these must be 0 and 1, respectively.", |
| region.dstOffset.y, dst_copy_extent.height); |
| } |
| } |
| |
| if (((dst_image_state.createInfo.imageType == VK_IMAGE_TYPE_1D) || |
| ((dst_image_state.createInfo.imageType == VK_IMAGE_TYPE_2D) && is_host)) && |
| ((0 != region.dstOffset.z) || (1 != dst_copy_extent.depth))) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| const char *image_type = is_host ? "1D or 2D" : "1D"; |
| skip |= LogError(GetImageCopyVUID("01786", is_2, is_host), objlist, region_loc, |
| "dstOffset.z is %" PRId32 " and extent.depth is %" PRIu32 |
| ". For %s images these must be 0 " |
| "and 1, respectively.", |
| region.dstOffset.z, dst_copy_extent.depth, image_type); |
| } |
| |
| if ((dst_image_state.createInfo.imageType == VK_IMAGE_TYPE_2D) && (0 != region.dstOffset.z) && !(is_host)) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstImage-01788" : "VUID-vkCmdCopyImage-dstImage-01788"; |
| skip |= LogError(vuid, objlist, region_loc, "dstOffset.z is %" PRId32 ". For 2D images the z-offset must be 0.", |
| region.dstOffset.z); |
| } |
| |
| // Handle difference between Maintenance 1 |
| if (IsExtEnabled(device_extensions.vk_khr_maintenance1) || is_host) { |
| if (src_image_state.createInfo.imageType == VK_IMAGE_TYPE_3D) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| if ((0 != region.srcSubresource.baseArrayLayer) || (1 != region.srcSubresource.layerCount)) { |
| skip |= LogError(GetImageCopyVUID("04443", is_2, is_host), objlist, region_loc, |
| "srcSubresource.baseArrayLayer is %" PRIu32 |
| " and srcSubresource.layerCount " |
| "is %" PRIu32 ". For VK_IMAGE_TYPE_3D images these must be 0 and 1, respectively.", |
| region.srcSubresource.baseArrayLayer, region.srcSubresource.layerCount); |
| } |
| } |
| if (dst_image_state.createInfo.imageType == VK_IMAGE_TYPE_3D) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| if ((0 != region.dstSubresource.baseArrayLayer) || (1 != region.dstSubresource.layerCount)) { |
| skip |= LogError(GetImageCopyVUID("04444", is_2, is_host), objlist, region_loc, |
| "dstSubresource.baseArrayLayer is %" PRIu32 |
| " and dstSubresource.layerCount " |
| "is %" PRIu32 ". For VK_IMAGE_TYPE_3D images these must be 0 and 1, respectively.", |
| region.dstSubresource.baseArrayLayer, region.dstSubresource.layerCount); |
| } |
| } |
| } else { // Pre maint 1 |
| if (src_image_state.createInfo.imageType == VK_IMAGE_TYPE_3D || |
| dst_image_state.createInfo.imageType == VK_IMAGE_TYPE_3D) { |
| if ((0 != region.srcSubresource.baseArrayLayer) || (1 != region.srcSubresource.layerCount)) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-apiVersion-07932" : "VUID-vkCmdCopyImage-apiVersion-07932"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcSubresource.baseArrayLayer is %" PRIu32 |
| " and " |
| "srcSubresource.layerCount is %" PRIu32 |
| ". For copies with either source or dest of type " |
| "VK_IMAGE_TYPE_3D, these must be 0 and 1, respectively.", |
| region.srcSubresource.baseArrayLayer, region.srcSubresource.layerCount); |
| } |
| if ((0 != region.dstSubresource.baseArrayLayer) || (1 != region.dstSubresource.layerCount)) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-apiVersion-07932" : "VUID-vkCmdCopyImage-apiVersion-07932"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "dstSubresource.baseArrayLayer is %" PRIu32 |
| " and " |
| "dstSubresource.layerCount is %" PRIu32 |
| ". For copies with either source or dest of type " |
| "VK_IMAGE_TYPE_3D, these must be 0 and 1, respectively.", |
| region.dstSubresource.baseArrayLayer, region.dstSubresource.layerCount); |
| } |
| } |
| } |
| |
| { |
| const VkExtent3D block_size = vkuFormatTexelBlockExtent(dst_image_state.createInfo.format); |
| // image offsets x must be multiple of block width |
| if (SafeModulo(region.dstOffset.x, block_size.width) != 0) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("07281", is_2, is_host), objlist, region_loc, |
| "srcOffset.x (%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "width (%" PRIu32 ").", |
| region.dstOffset.x, block_size.width); |
| } |
| |
| // image offsets y must be multiple of block height |
| if (SafeModulo(region.dstOffset.y, block_size.height) != 0) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("07282", is_2, is_host), objlist, region_loc, |
| "srcOffset.y (%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "height (%" PRIu32 ").", |
| region.dstOffset.y, block_size.height); |
| } |
| |
| // image offsets z must be multiple of block depth |
| if (SafeModulo(region.dstOffset.z, block_size.depth) != 0) { |
| const LogObjectList objlist(handle, src_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("07283", is_2, is_host), objlist, region_loc, |
| "srcOffset.z (%" PRId32 |
| ") must be a multiple of the blocked image's texel " |
| "depth (%" PRIu32 ").", |
| region.dstOffset.z, block_size.depth); |
| } |
| |
| const VkExtent3D mip_extent = dst_image_state.GetEffectiveSubresourceExtent(region.dstSubresource); |
| if ((SafeModulo(dst_copy_extent.width, block_size.width) != 0) && |
| (dst_copy_extent.width + region.dstOffset.x != mip_extent.width)) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("01732", is_2, is_host), objlist, region_loc, |
| "dst_copy_extent width (%" PRIu32 |
| ") must be a multiple of the blocked texture " |
| "block width (%" PRIu32 "), or when added to dstOffset.x (%" PRId32 |
| ") must equal the image subresource width (%" PRIu32 ").", |
| dst_copy_extent.width, block_size.width, region.dstOffset.x, mip_extent.width); |
| } |
| |
| // Extent height must be a multiple of block height, or dst_copy_extent+offset height must equal subresource height |
| if ((SafeModulo(dst_copy_extent.height, block_size.height) != 0) && |
| (dst_copy_extent.height + region.dstOffset.y != mip_extent.height)) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("01733", is_2, is_host), objlist, region_loc, |
| "dst_copy_extent height (%" PRIu32 |
| ") must be a multiple of the compressed " |
| "texture block height (%" PRIu32 "), or when added to dstOffset.y (%" PRId32 |
| ") must equal the image subresource " |
| "height (%" PRIu32 ").", |
| dst_copy_extent.height, block_size.height, region.dstOffset.y, mip_extent.height); |
| } |
| |
| // Extent depth must be a multiple of block depth, or dst_copy_extent+offset depth must equal subresource depth |
| uint32_t copy_depth = (slice_override ? depth_slices : dst_copy_extent.depth); |
| if ((SafeModulo(copy_depth, block_size.depth) != 0) && (copy_depth + region.dstOffset.z != mip_extent.depth)) { |
| const LogObjectList objlist(handle, dst_image_state.image()); |
| skip |= LogError(GetImageCopyVUID("01734", is_2, is_host), objlist, region_loc, |
| "dst_copy_extent width (%" PRIu32 |
| ") must be a multiple of the compressed texture " |
| "block depth (%" PRIu32 "), or when added to dstOffset.z (%" PRId32 |
| ") must equal the image subresource depth (%" PRIu32 ").", |
| dst_copy_extent.depth, block_size.depth, region.dstOffset.z, mip_extent.depth); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| // Returns non-zero if offset and extent exceed image extents |
| static constexpr uint32_t kXBit = 1; |
| static constexpr uint32_t kYBit = 2; |
| static constexpr uint32_t kZBit = 4; |
| static uint32_t ExceedsBounds(const VkOffset3D *offset, const VkExtent3D *extent, const VkExtent3D *image_extent) { |
| uint32_t result = 0; |
| // Extents/depths cannot be negative but checks left in for clarity |
| if ((offset->z + extent->depth > image_extent->depth) || (offset->z < 0) || |
| ((offset->z + static_cast<int32_t>(extent->depth)) < 0)) { |
| result |= kZBit; |
| } |
| if ((offset->y + extent->height > image_extent->height) || (offset->y < 0) || |
| ((offset->y + static_cast<int32_t>(extent->height)) < 0)) { |
| result |= kYBit; |
| } |
| if ((offset->x + extent->width > image_extent->width) || (offset->x < 0) || |
| ((offset->x + static_cast<int32_t>(extent->width)) < 0)) { |
| result |= kXBit; |
| } |
| return result; |
| } |
| template <typename HandleT, typename RegionType> |
| bool CoreChecks::ValidateCopyImageCommon(HandleT handle, const IMAGE_STATE &src_image_state, const IMAGE_STATE &dst_image_state, |
| uint32_t regionCount, const RegionType *pRegions, const Location &loc) const { |
| bool skip = false; |
| const bool is_2 = loc.function == Func::vkCmdCopyImage2 || loc.function == Func::vkCmdCopyImage2KHR; |
| const bool is_host = loc.function == Func::vkCopyImageToImageEXT; |
| |
| const VkFormat src_format = src_image_state.createInfo.format; |
| const VkFormat dst_format = dst_image_state.createInfo.format; |
| auto src_image = src_image_state.image(); |
| auto dst_image = dst_image_state.image(); |
| const bool src_is_3d = (VK_IMAGE_TYPE_3D == src_image_state.createInfo.imageType); |
| const bool dst_is_3d = (VK_IMAGE_TYPE_3D == dst_image_state.createInfo.imageType); |
| |
| const LogObjectList src_objlist(handle, src_image); |
| const LogObjectList dst_objlist(handle, dst_image); |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location src_subresource_loc = region_loc.dot(Field::srcSubresource); |
| const Location dst_subresource_loc = region_loc.dot(Field::dstSubresource); |
| const RegionType region = pRegions[i]; |
| |
| // For comp/uncomp copies, the copy extent for the dest image must be adjusted |
| VkExtent3D src_copy_extent = region.extent; |
| VkExtent3D dst_copy_extent = GetAdjustedDestImageExtent(src_format, dst_format, region.extent); |
| |
| bool slice_override = false; |
| uint32_t depth_slices = 0; |
| |
| // Special case for copying between a 1D/2D array and a 3D image |
| // TBD: This seems like the only way to reconcile 3 mutually-exclusive VU checks for 2D/3D copies. Heads up. |
| if (src_is_3d && !dst_is_3d) { |
| depth_slices = region.dstSubresource.layerCount; // Slice count from 2D subresource |
| slice_override = (depth_slices != 1); |
| } else if (dst_is_3d && !src_is_3d) { |
| depth_slices = region.srcSubresource.layerCount; // Slice count from 2D subresource |
| slice_override = (depth_slices != 1); |
| } |
| |
| skip |= ValidateImageSubresourceLayers(handle, ®ion.srcSubresource, src_subresource_loc); |
| skip |= ValidateImageSubresourceLayers(handle, ®ion.dstSubresource, dst_subresource_loc); |
| |
| skip |= ValidateImageMipLevel(handle, src_image_state, region.srcSubresource.mipLevel, |
| src_subresource_loc.dot(Field::mipLevel), GetImageCopyVUID("07967src", is_2, is_host)); |
| skip |= ValidateImageMipLevel(handle, dst_image_state, region.dstSubresource.mipLevel, |
| dst_subresource_loc.dot(Field::mipLevel), GetImageCopyVUID("07967dst", is_2, is_host)); |
| skip |= ValidateImageArrayLayerRange(handle, src_image_state, region.srcSubresource.baseArrayLayer, |
| region.srcSubresource.layerCount, src_subresource_loc, |
| GetImageCopyVUID("07968src", is_2, is_host)); |
| skip |= ValidateImageArrayLayerRange(handle, dst_image_state, region.dstSubresource.baseArrayLayer, |
| region.dstSubresource.layerCount, dst_subresource_loc, |
| GetImageCopyVUID("07968dst", is_2, is_host)); |
| |
| if (api_version < VK_API_VERSION_1_1) { |
| if (!IsExtEnabled(device_extensions.vk_khr_maintenance1)) { |
| // For each region the layerCount member of srcSubresource and dstSubresource must match |
| if (region.srcSubresource.layerCount != region.dstSubresource.layerCount) { |
| const LogObjectList objlist(handle, src_image, dst_image); |
| const char *vuid = |
| (is_2 || is_host) ? "VUID-VkImageCopy2-apiVersion-07941" : "VUID-VkImageCopy-apiVersion-07941"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::layerCount), |
| "(%" PRIu32 ") does not match %s (%" PRIu32 ").", region.srcSubresource.layerCount, |
| dst_subresource_loc.dot(Field::layerCount).Fields().c_str(), region.dstSubresource.layerCount); |
| } |
| } |
| if (!IsExtEnabled(device_extensions.vk_khr_sampler_ycbcr_conversion)) { |
| // For each region the aspectMask member of srcSubresource and dstSubresource must match |
| if (region.srcSubresource.aspectMask != region.dstSubresource.aspectMask) { |
| const LogObjectList objlist(handle, src_image, dst_image); |
| const char *vuid = |
| (is_2 || is_host) ? "VUID-VkImageCopy2-apiVersion-07940" : "VUID-VkImageCopy-apiVersion-07940"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::aspectMask), "(%s) does not match %s (%s).", |
| string_VkImageAspectFlags(region.srcSubresource.aspectMask).c_str(), |
| dst_subresource_loc.dot(Field::aspectMask).Fields().c_str(), |
| string_VkImageAspectFlags(region.dstSubresource.aspectMask).c_str()); |
| } |
| } |
| } |
| |
| // For each region, the aspectMask member of srcSubresource must be present in the source image |
| if (!VerifyAspectsPresent(region.srcSubresource.aspectMask, src_format)) { |
| skip |= LogError(GetImageCopyVUID("00142", is_2, is_host), src_objlist, src_subresource_loc.dot(Field::aspectMask), |
| "(%s) cannot specify aspects not present in source image (%s).", |
| string_VkImageAspectFlags(region.srcSubresource.aspectMask).c_str(), string_VkFormat(src_format)); |
| } |
| // For each region, the aspectMask member of dstSubresource must be present in the destination image |
| if (!VerifyAspectsPresent(region.dstSubresource.aspectMask, dst_format)) { |
| skip |= LogError(GetImageCopyVUID("00143", is_2, is_host), dst_objlist, dst_subresource_loc.dot(Field::aspectMask), |
| "(%s) cannot specify aspects not present in destination image (%s).", |
| string_VkImageAspectFlags(region.dstSubresource.aspectMask).c_str(), string_VkFormat(dst_format)); |
| } |
| |
| // Make sure not a empty region |
| if (src_copy_extent.width == 0) { |
| const LogObjectList objlist(handle, src_image); |
| const char *vuid = (is_2 || is_host) ? "VUID-VkImageCopy2-extent-06668" : "VUID-VkImageCopy-extent-06668"; |
| skip |= LogError(vuid, objlist, region_loc.dot(Field::extent).dot(Field::width), |
| "is zero. (empty copies are not allowed)."); |
| } |
| if (src_copy_extent.height == 0) { |
| const LogObjectList objlist(handle, src_image); |
| const char *vuid = (is_2 || is_host) ? "VUID-VkImageCopy2-extent-06669" : "VUID-VkImageCopy-extent-06669"; |
| skip |= LogError(vuid, objlist, region_loc.dot(Field::extent).dot(Field::height), |
| "is zero. (empty copies are not allowed)."); |
| } |
| if (src_copy_extent.depth == 0) { |
| const LogObjectList objlist(handle, src_image); |
| const char *vuid = (is_2 || is_host) ? "VUID-VkImageCopy2-extent-06670" : "VUID-VkImageCopy-extent-06670"; |
| skip |= LogError(vuid, objlist, region_loc.dot(Field::extent).dot(Field::depth), |
| "is zero. (empty copies are not allowed)."); |
| } |
| |
| // Each dimension offset + extent limits must fall with image subresource extent |
| VkExtent3D subresource_extent = src_image_state.GetEffectiveSubresourceExtent(region.srcSubresource); |
| if (slice_override) src_copy_extent.depth = depth_slices; |
| uint32_t extent_check = ExceedsBounds(&(region.srcOffset), &src_copy_extent, &subresource_extent); |
| if (extent_check & kXBit) { |
| skip |= LogError(GetImageCopyVUID("00144", is_2, is_host), src_objlist, region_loc.dot(Field::srcOffset).dot(Field::x), |
| "(%" PRId32 ") + extent (%" PRIu32 ") exceeds subResource width (%" PRIu32 ").", region.srcOffset.x, |
| src_copy_extent.width, subresource_extent.width); |
| } |
| |
| if (extent_check & kYBit) { |
| skip |= LogError(GetImageCopyVUID("00145", is_2, is_host), src_objlist, region_loc.dot(Field::srcOffset).dot(Field::y), |
| "(%" PRId32 ") + extent (%" PRIu32 ") exceeds subResource height (%" PRIu32 ").", region.srcOffset.y, |
| src_copy_extent.height, subresource_extent.height); |
| } |
| if (extent_check & kZBit) { |
| skip |= LogError(GetImageCopyVUID("00147", is_2, is_host), src_objlist, region_loc.dot(Field::srcOffset).dot(Field::z), |
| "(%" PRId32 ") + extent (%" PRIu32 ") exceeds subResource depth (%" PRIu32 ").", region.srcOffset.z, |
| src_copy_extent.depth, subresource_extent.depth); |
| } |
| |
| // Adjust dest extent if necessary |
| subresource_extent = dst_image_state.GetEffectiveSubresourceExtent(region.dstSubresource); |
| if (slice_override) dst_copy_extent.depth = depth_slices; |
| |
| extent_check = ExceedsBounds(&(region.dstOffset), &dst_copy_extent, &subresource_extent); |
| if (extent_check & kXBit) { |
| skip |= LogError(GetImageCopyVUID("00150", is_2, is_host), dst_objlist, region_loc.dot(Field::dstOffset).dot(Field::x), |
| "(%" PRId32 ") + extent (%" PRIu32 ") exceeds subResource width (%" PRIu32 ").", region.dstOffset.x, |
| dst_copy_extent.width, subresource_extent.width); |
| } |
| if (extent_check & kYBit) { |
| skip |= LogError(GetImageCopyVUID("00151", is_2, is_host), dst_objlist, region_loc.dot(Field::dstOffset).dot(Field::y), |
| "(%" PRId32 ") + extent (%" PRIu32 ") exceeds subResource height (%" PRIu32 ").", region.dstOffset.y, |
| dst_copy_extent.height, subresource_extent.height); |
| } |
| if (extent_check & kZBit) { |
| skip |= LogError(GetImageCopyVUID("00153", is_2, is_host), dst_objlist, region_loc.dot(Field::dstOffset).dot(Field::z), |
| "(%" PRId32 ") + extent (%" PRIu32 ") exceeds subResource depth (%" PRIu32 ").", region.dstOffset.z, |
| dst_copy_extent.depth, subresource_extent.depth); |
| } |
| } |
| |
| skip |= ValidateMemoryIsBoundToImage(src_objlist, src_image_state, loc.dot(Field::srcImage), |
| GetImageCopyVUID("07966src", is_2, is_host)); |
| skip |= ValidateMemoryIsBoundToImage(dst_objlist, dst_image_state, loc.dot(Field::dstImage), |
| GetImageCopyVUID("07966dst", is_2, is_host)); |
| |
| if (src_image_state.createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| skip |= LogError(GetImageCopyVUID("07969src", is_2, is_host), src_objlist, loc.dot(Field::srcImage), |
| "was created with flags including VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT"); |
| } |
| if (dst_image_state.createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| skip |= LogError(GetImageCopyVUID("07969dst", is_2, is_host), dst_objlist, loc.dot(Field::dstImage), |
| "was created with flags including VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT"); |
| } |
| |
| return skip; |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const RegionType *pRegions, const Location &loc) const { |
| bool skip = false; |
| auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (!cb_state_ptr || !src_image_state || !dst_image_state) { |
| return skip; |
| } |
| const CMD_BUFFER_STATE &cb_state = *cb_state_ptr; |
| const VkFormat src_format = src_image_state->createInfo.format; |
| const VkFormat dst_format = dst_image_state->createInfo.format; |
| const VkImageType src_image_type = src_image_state->createInfo.imageType; |
| const VkImageType dst_image_type = dst_image_state->createInfo.imageType; |
| const bool src_is_2d = (VK_IMAGE_TYPE_2D == src_image_type); |
| const bool src_is_3d = (VK_IMAGE_TYPE_3D == src_image_type); |
| const bool dst_is_2d = (VK_IMAGE_TYPE_2D == dst_image_type); |
| const bool dst_is_3d = (VK_IMAGE_TYPE_3D == dst_image_type); |
| const bool is_2 = loc.function == Func::vkCmdCopyImage2 || loc.function == Func::vkCmdCopyImage2KHR; |
| |
| const char *vuid; |
| const Location src_image_loc = loc.dot(Field::srcImage); |
| const Location dst_image_loc = loc.dot(Field::dstImage); |
| |
| skip = ValidateImageCopyData(commandBuffer, regionCount, pRegions, *src_image_state, *dst_image_state, false, loc); |
| skip = ValidateCopyImageCommon(commandBuffer, *src_image_state, *dst_image_state, regionCount, pRegions, loc); |
| |
| bool has_stencil_aspect = false; |
| bool has_non_stencil_aspect = false; |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location src_subresource_loc = region_loc.dot(Field::srcSubresource); |
| const Location dst_subresource_loc = region_loc.dot(Field::dstSubresource); |
| const RegionType ®ion = pRegions[i]; |
| |
| // For comp/uncomp copies, the copy extent for the dest image must be adjusted |
| VkExtent3D src_copy_extent = region.extent; |
| VkExtent3D dst_copy_extent = GetAdjustedDestImageExtent(src_format, dst_format, region.extent); |
| |
| bool slice_override = false; |
| uint32_t depth_slices = 0; |
| |
| // Special case for copying between a 1D/2D array and a 3D image |
| // TBD: This seems like the only way to reconcile 3 mutually-exclusive VU checks for 2D/3D copies. Heads up. |
| if (src_is_3d && !dst_is_3d) { |
| depth_slices = region.dstSubresource.layerCount; // Slice count from 2D subresource |
| slice_override = (depth_slices != 1); |
| } else if (dst_is_3d && !src_is_3d) { |
| depth_slices = region.srcSubresource.layerCount; // Slice count from 2D subresource |
| slice_override = (depth_slices != 1); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_maintenance1)) { |
| // No chance of mismatch if we're overriding depth slice count |
| if (!slice_override) { |
| // The number of depth slices in srcSubresource and dstSubresource must match |
| // Depth comes from layerCount for 1D,2D resources, from extent.depth for 3D |
| uint32_t src_slices = (src_is_3d ? src_copy_extent.depth : region.srcSubresource.layerCount); |
| uint32_t dst_slices = (dst_is_3d ? dst_copy_extent.depth : region.dstSubresource.layerCount); |
| if (src_slices != dst_slices && src_slices != VK_REMAINING_ARRAY_LAYERS && |
| dst_slices != VK_REMAINING_ARRAY_LAYERS) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-08793" : "VUID-vkCmdCopyImage-srcImage-08793"; |
| skip |= LogError(vuid, objlist, region_loc, "%s (%" PRIu32 ") is different from %s (%" PRIu32 ").", |
| src_is_3d ? "extent.depth" : "srcSubresource.layerCount", src_slices, |
| dst_is_3d ? "extent.depth" : "dstSubresource.layerCount", dst_slices); |
| } |
| } |
| // Maintenance 1 requires both while prior only required one to be 2D |
| if ((src_is_2d && dst_is_2d) && (src_copy_extent.depth != 1)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01790" : "VUID-vkCmdCopyImage-srcImage-01790"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "both srcImage and dstImage are 2D and extent.depth is %" PRIu32 " and has to be 1", |
| src_copy_extent.depth); |
| } |
| |
| if (src_image_type != dst_image_type) { |
| // if different, one must be 3D and the other 2D |
| const bool valid = |
| (src_is_2d && dst_is_3d) || (src_is_3d && dst_is_2d) || enabled_features.maintenance5_features.maintenance5; |
| if (!valid) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-07743" : "VUID-vkCmdCopyImage-srcImage-07743"; |
| skip |= |
| LogError(vuid, objlist, region_loc, |
| "srcImage type (%s) must be equal to dstImage type (%s) or else one must be 2D and the other 3D", |
| string_VkImageType(src_image_type), string_VkImageType(dst_image_type)); |
| } |
| } |
| |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01995" : "VUID-vkCmdCopyImage-srcImage-01995"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *src_image_state, VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT, |
| src_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstImage-01996" : "VUID-vkCmdCopyImage-dstImage-01996"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *dst_image_state, VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT, |
| dst_image_loc, vuid); |
| |
| // Check if 2D with 3D and depth not equal to 2D layerCount |
| if (src_is_2d && dst_is_3d && (src_copy_extent.depth != region.srcSubresource.layerCount)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01791" : "VUID-vkCmdCopyImage-srcImage-01791"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcImage is 2D, dstImage is 3D and extent.depth is %" PRIu32 |
| " and has to be " |
| "srcSubresource.layerCount (%" PRIu32 ")", |
| src_copy_extent.depth, region.srcSubresource.layerCount); |
| } else if (src_is_3d && dst_is_2d && (src_copy_extent.depth != region.dstSubresource.layerCount)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstImage-01792" : "VUID-vkCmdCopyImage-dstImage-01792"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcImage is 3D, dstImage is 2D and extent.depth is %" PRIu32 |
| " and has to be " |
| "dstSubresource.layerCount (%" PRIu32 ")", |
| src_copy_extent.depth, region.dstSubresource.layerCount); |
| } |
| } else { // !vk_khr_maintenance1 |
| if ((src_is_2d || dst_is_2d) && (src_copy_extent.depth != 1)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-apiVersion-08969" : "VUID-vkCmdCopyImage-apiVersion-08969"; |
| skip |= LogError(vuid, objlist, region_loc, "srcImage is %s is dstImage is %s but extent.depth is %" PRIu32 ".", |
| string_VkImageType(src_image_type), string_VkImageType(dst_image_type), src_copy_extent.depth); |
| } |
| |
| if (src_image_type != dst_image_type) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-apiVersion-07933" : "VUID-vkCmdCopyImage-apiVersion-07933"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcImage (%s) must be equal to dstImage (%s) without VK_KHR_maintenance1 enabled", |
| string_VkImageType(src_image_type), string_VkImageType(dst_image_type)); |
| } |
| } |
| |
| if ((!vkuFormatIsMultiplane(src_format)) && (!vkuFormatIsMultiplane(dst_format))) { |
| // If neither image is multi-plane the aspectMask member of src and dst must match |
| if (region.srcSubresource.aspectMask != region.dstSubresource.aspectMask) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01551" : "VUID-vkCmdCopyImage-srcImage-01551"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::aspectMask), "(%s) does not match %s (%s).", |
| string_VkImageAspectFlags(region.srcSubresource.aspectMask).c_str(), |
| dst_subresource_loc.dot(Field::aspectMask).Fields().c_str(), |
| string_VkImageAspectFlags(region.dstSubresource.aspectMask).c_str()); |
| } |
| } else { |
| // Source image multiplane checks |
| VkImageAspectFlags aspect = region.srcSubresource.aspectMask; |
| if (vkuFormatIsMultiplane(src_format) && !IsOnlyOneValidPlaneAspect(src_format, aspect)) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-08713" : "VUID-vkCmdCopyImage-srcImage-08713"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::aspectMask), |
| "(%s) is invalid for multi-planar format %s.", string_VkImageAspectFlags(aspect).c_str(), |
| string_VkFormat(src_format)); |
| } |
| // Single-plane to multi-plane |
| if ((!vkuFormatIsMultiplane(src_format)) && (vkuFormatIsMultiplane(dst_format)) && (VK_IMAGE_ASPECT_COLOR_BIT != aspect)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstImage-01557" : "VUID-vkCmdCopyImage-dstImage-01557"; |
| skip |= |
| LogError(vuid, objlist, src_subresource_loc.dot(Field::aspectMask), |
| "(%s) needs VK_IMAGE_ASPECT_COLOR_BIT\nsrcImage format %s\ndstImage format %s\n.", |
| string_VkImageAspectFlags(aspect).c_str(), string_VkFormat(src_format), string_VkFormat(dst_format)); |
| } |
| |
| // Dest image multiplane checks |
| aspect = region.dstSubresource.aspectMask; |
| if (vkuFormatIsMultiplane(dst_format) && !IsOnlyOneValidPlaneAspect(dst_format, aspect)) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstImage-08714" : "VUID-vkCmdCopyImage-dstImage-08714"; |
| skip |= LogError(vuid, objlist, dst_subresource_loc.dot(Field::aspectMask), |
| "(%s) is invalid for multi-planar format %s.", string_VkImageAspectFlags(aspect).c_str(), |
| string_VkFormat(dst_format)); |
| } |
| // Multi-plane to single-plane |
| if ((vkuFormatIsMultiplane(src_format)) && (!vkuFormatIsMultiplane(dst_format)) && (VK_IMAGE_ASPECT_COLOR_BIT != aspect)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01556" : "VUID-vkCmdCopyImage-srcImage-01556"; |
| skip |= |
| LogError(vuid, objlist, dst_subresource_loc.dot(Field::aspectMask), |
| "(%s) needs VK_IMAGE_ASPECT_COLOR_BIT\nsrcImage format %s\ndstImage format %s\n.", |
| string_VkImageAspectFlags(aspect).c_str(), string_VkFormat(src_format), string_VkFormat(dst_format)); |
| } |
| } |
| |
| // The union of all source regions, and the union of all destination regions, specified by the elements of regions, |
| // must not overlap in memory |
| // Validation is only performed when source image is the same as destination image. |
| // In the general case, the mapping between an image and its underlying memory is undefined, |
| // so checking for memory overlaps is not possible. |
| if (src_image_state->image() == dst_image_state->image()) { |
| for (uint32_t j = 0; j < regionCount; j++) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| if (auto intersection = GetRegionIntersection(region, pRegions[j], src_image_type, vkuFormatIsMultiplane(src_format)); |
| intersection.has_instersection) { |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-pRegions-00124" : "VUID-vkCmdCopyImage-pRegions-00124"; |
| skip |= LogError(vuid, objlist, loc, |
| "pRegion[%" PRIu32 "] copy source overlaps with pRegions[%" PRIu32 |
| "] copy destination. Overlap info, with respect to image (%s): %s.", |
| i, j, FormatHandle(srcImage).c_str(), intersection.String().c_str()); |
| } |
| } |
| } |
| |
| // Check for multi-plane format compatiblity |
| if (vkuFormatIsMultiplane(src_format) || vkuFormatIsMultiplane(dst_format)) { |
| const VkFormat src_plane_format = vkuFormatIsMultiplane(src_format) |
| ? vkuFindMultiplaneCompatibleFormat(src_format, static_cast<VkImageAspectFlagBits>(region.srcSubresource.aspectMask)) |
| : src_format; |
| const VkFormat dst_plane_format = vkuFormatIsMultiplane(dst_format) |
| ? vkuFindMultiplaneCompatibleFormat(dst_format, static_cast<VkImageAspectFlagBits>(region.dstSubresource.aspectMask)) |
| : dst_format; |
| const size_t src_format_size = vkuFormatElementSize(src_plane_format); |
| const size_t dst_format_size = vkuFormatElementSize(dst_plane_format); |
| |
| // If size is still zero, then format is invalid and will be caught in another VU |
| if ((src_format_size != dst_format_size) && (src_format_size != 0) && (dst_format_size != 0)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-None-01549" : "VUID-vkCmdCopyImage-None-01549"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcImage format %s with aspectMask %s is not compatible with dstImage format %s aspectMask %s.", |
| string_VkFormat(src_format), string_VkImageAspectFlags(region.srcSubresource.aspectMask).c_str(), |
| string_VkFormat(dst_format), string_VkImageAspectFlags(region.dstSubresource.aspectMask).c_str()); |
| } |
| } |
| |
| // track aspect mask in loop through regions |
| if ((region.srcSubresource.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) { |
| has_stencil_aspect = true; |
| } |
| if ((region.srcSubresource.aspectMask & (~VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { |
| has_non_stencil_aspect = true; |
| } |
| } |
| |
| // The formats of non-multiplane src_image and dst_image must be compatible. Formats are considered compatible if their texel |
| // size in bytes is the same between both formats. For example, VK_FORMAT_R8G8B8A8_UNORM is compatible with VK_FORMAT_R32_UINT |
| // because because both texels are 4 bytes in size. |
| if (!vkuFormatIsMultiplane(src_format) && !vkuFormatIsMultiplane(dst_format)) { |
| const char *compatible_vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-01548" : "VUID-vkCmdCopyImage-srcImage-01548"; |
| // Depth/stencil formats must match exactly. |
| if (vkuFormatIsDepthOrStencil(src_format) || vkuFormatIsDepthOrStencil(dst_format)) { |
| if (src_format != dst_format) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| skip |= LogError(compatible_vuid, objlist, loc, "srcImage format (%s) is different from dstImage format (%s).", |
| string_VkFormat(src_format), string_VkFormat(dst_format)); |
| } |
| } else { |
| if (vkuFormatElementSize(src_format) != vkuFormatElementSize(dst_format)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| skip |= LogError(compatible_vuid, objlist, loc, |
| "srcImage format %s has size of %" PRIu32 " and dstImage format %s has size of %" PRIu32 ".", |
| string_VkFormat(src_format), vkuFormatElementSize(src_format), string_VkFormat(dst_format), |
| vkuFormatElementSize(dst_format)); |
| } |
| } |
| } |
| |
| if (vkuFormatIsCompressed(src_format) && vkuFormatIsCompressed(dst_format)) { |
| auto src_block_extent = vkuFormatTexelBlockExtent(src_format); |
| auto dst_block_extent = vkuFormatTexelBlockExtent(dst_format); |
| if (src_block_extent.width != dst_block_extent.width || src_block_extent.height != dst_block_extent.height || |
| src_block_extent.depth != dst_block_extent.depth) { |
| const char *compatible_vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-09247" : "VUID-vkCmdCopyImage-srcImage-09247"; |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| skip |= LogError(compatible_vuid, objlist, loc, |
| "srcImage format %s has texel block extent (w = %" PRIu32 ", h = %" PRIu32 ", d = %" PRIu32 |
| ") and dstImage format %s has texel block extent (w = %" PRIu32 ", h = %" PRIu32 ", d = %" PRIu32 ").", |
| string_VkFormat(src_format), src_block_extent.width, src_block_extent.height, src_block_extent.depth, |
| string_VkFormat(dst_format), dst_block_extent.width, dst_block_extent.height, dst_block_extent.depth); |
| } |
| } |
| |
| // Validate that SRC & DST images have correct usage flags set |
| if (!IsExtEnabled(device_extensions.vk_ext_separate_stencil_usage)) { |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-aspect-06662" : "VUID-vkCmdCopyImage-aspect-06662"; |
| skip |= |
| ValidateImageUsageFlags(commandBuffer, *src_image_state, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, false, vuid, src_image_loc); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-aspect-06663" : "VUID-vkCmdCopyImage-aspect-06663"; |
| skip |= |
| ValidateImageUsageFlags(commandBuffer, *dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, false, vuid, dst_image_loc); |
| } else { |
| auto src_separate_stencil = vku::FindStructInPNextChain<VkImageStencilUsageCreateInfo>(src_image_state->createInfo.pNext); |
| if (src_separate_stencil && has_stencil_aspect && |
| ((src_separate_stencil->stencilUsage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) == 0)) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-aspect-06664" : "VUID-vkCmdCopyImage-aspect-06664"; |
| skip = LogError(vuid, objlist, src_image_loc, "(%s) was created with %s but requires VK_IMAGE_USAGE_TRANSFER_SRC_BIT.", |
| FormatHandle(src_image_state->Handle()).c_str(), |
| string_VkImageUsageFlags(src_separate_stencil->stencilUsage).c_str()); |
| } |
| if (!src_separate_stencil || has_non_stencil_aspect) { |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-aspect-06662" : "VUID-vkCmdCopyImage-aspect-06662"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *src_image_state, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, false, vuid, |
| src_image_loc); |
| } |
| |
| auto dst_separate_stencil = vku::FindStructInPNextChain<VkImageStencilUsageCreateInfo>(dst_image_state->createInfo.pNext); |
| if (dst_separate_stencil && has_stencil_aspect && |
| ((dst_separate_stencil->stencilUsage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0)) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-aspect-06665" : "VUID-vkCmdCopyImage-aspect-06665"; |
| skip = LogError(vuid, objlist, dst_image_loc, "(%s) was created with %s but requires VK_IMAGE_USAGE_TRANSFER_DST_BIT.", |
| FormatHandle(dst_image_state->Handle()).c_str(), |
| string_VkImageUsageFlags(dst_separate_stencil->stencilUsage).c_str()); |
| } |
| if (!dst_separate_stencil || has_non_stencil_aspect) { |
| vuid = is_2 ? "VUID-vkCmdCopyImage-aspect-06663" : "VUID-vkCmdCopyImage-aspect-06663"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, false, vuid, |
| dst_image_loc); |
| } |
| } |
| |
| // Source and dest image sample counts must match |
| if (src_image_state->createInfo.samples != dst_image_state->createInfo.samples) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImage-00136" : "VUID-vkCmdCopyImage-srcImage-00136"; |
| skip |= LogError(vuid, objlist, src_image_loc, "was created with (%s) but the dstImage was created with (%s).", |
| string_VkSampleCountFlagBits(src_image_state->createInfo.samples), |
| string_VkSampleCountFlagBits(dst_image_state->createInfo.samples)); |
| } |
| |
| vuid = is_2 ? "VUID-vkCmdCopyImage2-commandBuffer-01825" : "VUID-vkCmdCopyImage-commandBuffer-01825"; |
| skip |= ValidateProtectedImage(cb_state, *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyImage2-commandBuffer-01826" : "VUID-vkCmdCopyImage-commandBuffer-01826"; |
| skip |= ValidateProtectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyImage2-commandBuffer-01827" : "VUID-vkCmdCopyImage-commandBuffer-01827"; |
| skip |= ValidateUnprotectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| |
| skip |= ValidateCmd(cb_state, loc); |
| |
| const char *invalid_src_layout_vuid = |
| is_2 ? "VUID-VkCopyImageInfo2-srcImageLayout-01917" : "VUID-vkCmdCopyImage-srcImageLayout-01917"; |
| const char *invalid_dst_layout_vuid = |
| is_2 ? "VUID-VkCopyImageInfo2-dstImageLayout-01395" : "VUID-vkCmdCopyImage-dstImageLayout-01395"; |
| const bool same_image = (src_image_state == dst_image_state); |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| // When performing copy from and to same subresource, VK_IMAGE_LAYOUT_GENERAL is the only option |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const RegionType region = pRegions[i]; |
| const VkImageSubresourceLayers &src_subresource = region.srcSubresource; |
| const VkImageSubresourceLayers &dst_subresource = region.dstSubresource; |
| bool same_subresource = (same_image && (src_subresource.mipLevel == dst_subresource.mipLevel) && |
| (src_subresource.baseArrayLayer == dst_subresource.baseArrayLayer)); |
| VkImageLayout source_optimal = (same_subresource ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); |
| VkImageLayout destination_optimal = (same_subresource ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-srcImageLayout-00128" : "VUID-vkCmdCopyImage-srcImageLayout-00128"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *src_image_state, region.srcSubresource, srcImageLayout, source_optimal, |
| src_image_loc, invalid_src_layout_vuid, vuid); |
| vuid = is_2 ? "VUID-VkCopyImageInfo2-dstImageLayout-00133" : "VUID-vkCmdCopyImage-dstImageLayout-00133"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *dst_image_state, region.dstSubresource, dstImageLayout, destination_optimal, |
| dst_image_loc, invalid_dst_layout_vuid, vuid); |
| skip |= ValidateCopyImageTransferGranularityRequirements(cb_state, *src_image_state, *dst_image_state, ®ion, region_loc); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkImageCopy *pRegions, const ErrorObject &error_obj) const { |
| return ValidateCmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, |
| error_obj.location); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyImage2KHR(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR *pCopyImageInfo, |
| const ErrorObject &error_obj) const { |
| return PreCallValidateCmdCopyImage2(commandBuffer, pCopyImageInfo, error_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdCopyImage(commandBuffer, pCopyImageInfo->srcImage, pCopyImageInfo->srcImageLayout, pCopyImageInfo->dstImage, |
| pCopyImageInfo->dstImageLayout, pCopyImageInfo->regionCount, pCopyImageInfo->pRegions, |
| error_obj.location.dot(Field::pCopyImageInfo)); |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkImageCopy *pRegions) { |
| StateTracker::PreCallRecordCmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, |
| pRegions); |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (cb_state_ptr && src_image_state && dst_image_state) { |
| // Make sure that all image slices are updated to correct layout |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*src_image_state, pRegions[i].srcSubresource, srcImageLayout); |
| cb_state_ptr->SetImageInitialLayout(*dst_image_state, pRegions[i].dstSubresource, dstImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::RecordCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo) { |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(pCopyImageInfo->srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(pCopyImageInfo->dstImage); |
| if (cb_state_ptr && src_image_state && dst_image_state) { |
| // Make sure that all image slices are updated to correct layout |
| for (uint32_t i = 0; i < pCopyImageInfo->regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*src_image_state, pCopyImageInfo->pRegions[i].srcSubresource, |
| pCopyImageInfo->srcImageLayout); |
| cb_state_ptr->SetImageInitialLayout(*dst_image_state, pCopyImageInfo->pRegions[i].dstSubresource, |
| pCopyImageInfo->dstImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyImage2KHR(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR *pCopyImageInfo) { |
| StateTracker::PreCallRecordCmdCopyImage2KHR(commandBuffer, pCopyImageInfo); |
| RecordCmdCopyImage2(commandBuffer, pCopyImageInfo); |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 *pCopyImageInfo) { |
| StateTracker::PreCallRecordCmdCopyImage2(commandBuffer, pCopyImageInfo); |
| RecordCmdCopyImage2(commandBuffer, pCopyImageInfo); |
| } |
| |
| template <typename RegionType> |
| void CoreChecks::RecordCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, |
| const RegionType *pRegions, const Location &loc) { |
| const bool is_2 = loc.function == Func::vkCmdCopyBuffer2 || loc.function == Func::vkCmdCopyBuffer2KHR; |
| const char *vuid = is_2 ? "VUID-VkCopyBufferInfo2-pRegions-00117" : "VUID-vkCmdCopyBuffer-pRegions-00117"; |
| |
| auto src_buffer_state = Get<BUFFER_STATE>(srcBuffer); |
| auto dst_buffer_state = Get<BUFFER_STATE>(dstBuffer); |
| if (src_buffer_state->sparse || dst_buffer_state->sparse) { |
| auto cb_state_ptr = Get<CMD_BUFFER_STATE>(commandBuffer); |
| |
| std::vector<sparse_container::range<VkDeviceSize>> src_ranges; |
| std::vector<sparse_container::range<VkDeviceSize>> dst_ranges; |
| |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| const RegionType ®ion = pRegions[i]; |
| src_ranges.emplace_back(sparse_container::range<VkDeviceSize>{region.srcOffset, region.srcOffset + region.size}); |
| dst_ranges.emplace_back(sparse_container::range<VkDeviceSize>{region.dstOffset, region.dstOffset + region.size}); |
| } |
| |
| auto queue_submit_validation = [this, commandBuffer, src_buffer_state, dst_buffer_state, regionCount, src_ranges, |
| dst_ranges, loc, |
| vuid](const ValidationStateTracker &device_data, const class QUEUE_STATE &queue_state, |
| const CMD_BUFFER_STATE &cb_state) -> bool { |
| bool skip = false; |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| const auto &src = src_ranges[i]; |
| for (uint32_t j = 0; j < regionCount; ++j) { |
| const auto &dst = dst_ranges[j]; |
| if (const auto [memory, overlap_range] = |
| src_buffer_state->GetResourceMemoryOverlap(src, dst_buffer_state.get(), dst); |
| memory != VK_NULL_HANDLE) { |
| const LogObjectList objlist(commandBuffer, src_buffer_state->buffer(), dst_buffer_state->buffer(), memory); |
| skip |= this->LogError(vuid, objlist, loc, |
| "Memory (%s) has copy overlap on range %s. Source " |
| "buffer range is pRegions[%" PRIu32 |
| "] (%s), destination buffer range is pRegions[%" PRIu32 "] (%s).", |
| FormatHandle(memory).c_str(), string_range(overlap_range).c_str(), i, |
| string_range(src).c_str(), j, string_range(dst).c_str()); |
| } |
| } |
| } |
| |
| return skip; |
| }; |
| |
| cb_state_ptr->queue_submit_functions.emplace_back(queue_submit_validation); |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, |
| uint32_t regionCount, const VkBufferCopy *pRegions) { |
| const Location loc(Func::vkCmdCopyBuffer); |
| RecordCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions, loc); |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyBuffer2KHR(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR *pCopyBufferInfo) { |
| const Location loc(Func::vkCmdCopyBuffer2KHR); |
| RecordCmdCopyBuffer(commandBuffer, pCopyBufferInfo->srcBuffer, pCopyBufferInfo->dstBuffer, pCopyBufferInfo->regionCount, |
| pCopyBufferInfo->pRegions, loc); |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2 *pCopyBufferInfo) { |
| const Location loc(Func::vkCmdCopyBuffer2); |
| RecordCmdCopyBuffer(commandBuffer, pCopyBufferInfo->srcBuffer, pCopyBufferInfo->dstBuffer, pCopyBufferInfo->regionCount, |
| pCopyBufferInfo->pRegions, loc); |
| } |
| |
| template <typename T> |
| VkImageSubresourceLayers GetImageSubresource(T data, bool is_src) { |
| return data.imageSubresource; |
| } |
| template <> |
| VkImageSubresourceLayers GetImageSubresource<VkImageCopy2>(VkImageCopy2 data, bool is_src) { |
| return is_src ? data.srcSubresource : data.dstSubresource; |
| } |
| template <typename T> |
| VkOffset3D GetOffset(T data, bool is_src) { |
| return data.imageOffset; |
| } |
| template <> |
| VkOffset3D GetOffset<VkImageCopy2>(VkImageCopy2 data, bool is_src) { |
| return is_src ? data.srcOffset : data.dstOffset; |
| } |
| template <typename T> |
| VkExtent3D GetExtent(T data) { |
| return data.imageExtent; |
| } |
| template <> |
| VkExtent3D GetExtent<VkImageCopy2>(VkImageCopy2 data) { |
| return data.extent; |
| } |
| template <typename HandleT, typename RegionType> |
| bool CoreChecks::ValidateImageBounds(const HandleT handle, const IMAGE_STATE &image_state, const uint32_t regionCount, |
| const RegionType *pRegions, const Location &loc, const char *vuid, bool is_src) const { |
| bool skip = false; |
| const VkImageCreateInfo *image_info = &(image_state.createInfo); |
| |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const RegionType region = pRegions[i]; |
| VkExtent3D extent = GetExtent(region); |
| VkOffset3D offset = GetOffset(region, is_src); |
| VkImageSubresourceLayers subresource_layout = GetImageSubresource(region, is_src); |
| |
| VkExtent3D image_extent = image_state.GetEffectiveSubresourceExtent(subresource_layout); |
| |
| // If we're using a blocked image format, valid extent is rounded up to multiple of block size (per |
| // vkspec.html#_common_operation) |
| if (vkuFormatIsBlockedImage(image_info->format)) { |
| auto block_extent = vkuFormatTexelBlockExtent(image_info->format); |
| if (image_extent.width % block_extent.width) { |
| image_extent.width += (block_extent.width - (image_extent.width % block_extent.width)); |
| } |
| if (image_extent.height % block_extent.height) { |
| image_extent.height += (block_extent.height - (image_extent.height % block_extent.height)); |
| } |
| if (image_extent.depth % block_extent.depth) { |
| image_extent.depth += (block_extent.depth - (image_extent.depth % block_extent.depth)); |
| } |
| } |
| |
| if (0 != ExceedsBounds(&offset, &extent, &image_extent)) { |
| const LogObjectList objlist(handle, image_state.Handle()); |
| skip |= LogError(vuid, objlist, region_loc, |
| "exceeds image bounds\n" |
| "region extent (w = %" PRIu32 ", h = %" PRIu32 ", d = %" PRIu32 |
| ")\n" |
| "region offset (x = %" PRId32 ", y = %" PRId32 ", z = %" PRId32 |
| ")\n" |
| "image extent (w = %" PRIu32 ", h = %" PRIu32 ", d = %" PRIu32 ")\n", |
| extent.width, extent.height, extent.depth, offset.x, offset.y, offset.z, image_extent.width, |
| image_extent.height, image_extent.depth); |
| } |
| } |
| |
| return skip; |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateBufferBounds(VkCommandBuffer cb, const IMAGE_STATE &image_state, const BUFFER_STATE &buff_state, |
| uint32_t regionCount, const RegionType *pRegions, const Location &loc, |
| const char *vuid) const { |
| bool skip = false; |
| |
| const VkDeviceSize buffer_size = buff_state.createInfo.size; |
| |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const RegionType region = pRegions[i]; |
| const VkDeviceSize buffer_copy_size = |
| GetBufferSizeFromCopyImage(region, image_state.createInfo.format, image_state.createInfo.arrayLayers); |
| // This blocks against invalid VkBufferCopyImage that already have been caught elsewhere |
| if (buffer_copy_size != 0) { |
| const VkDeviceSize max_buffer_copy = buffer_copy_size + region.bufferOffset; |
| if (buffer_size < max_buffer_copy) { |
| const LogObjectList objlist(cb, buff_state.Handle()); |
| skip |= LogError(vuid, objlist, region_loc, |
| "is trying to copy %" PRIu64 " bytes plus %" PRIu64 |
| " offset to/from the VkBuffer (%s) which exceeds the VkBuffer total size of %" PRIu64 " bytes.", |
| buffer_copy_size, region.bufferOffset, FormatHandle(buff_state).c_str(), buffer_size); |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| template <typename HandleT> |
| // Validate that an image's sampleCount matches the requirement for a specific API call |
| bool CoreChecks::ValidateImageSampleCount(const HandleT handle, const IMAGE_STATE &image_state, VkSampleCountFlagBits sample_count, |
| const Location &loc, const std::string &vuid) const { |
| bool skip = false; |
| if (image_state.createInfo.samples != sample_count) { |
| const LogObjectList objlist(handle, image_state.Handle()); |
| skip = LogError(vuid, objlist, loc, "%s was created with a sample count of %s but must be %s.", |
| FormatHandle(image_state).c_str(), string_VkSampleCountFlagBits(image_state.createInfo.samples), |
| string_VkSampleCountFlagBits(sample_count)); |
| } |
| return skip; |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkBuffer dstBuffer, uint32_t regionCount, const RegionType *pRegions, |
| const Location &loc) const { |
| bool skip = false; |
| auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| auto dst_buffer_state = Get<BUFFER_STATE>(dstBuffer); |
| if (!cb_state_ptr || !src_image_state || !dst_buffer_state) { |
| return skip; |
| } |
| const CMD_BUFFER_STATE &cb_state = *cb_state_ptr; |
| |
| const bool is_2 = loc.function == Func::vkCmdCopyImageToBuffer2 || loc.function == Func::vkCmdCopyImageToBuffer2KHR; |
| const char *vuid; |
| const Location src_image_loc = loc.dot(Field::srcImage); |
| const Location dst_buffer_loc = loc.dot(Field::dstBuffer); |
| |
| skip |= ValidateBufferImageCopyData(cb_state, regionCount, pRegions, *src_image_state, loc); |
| |
| skip |= ValidateCmd(cb_state, loc); |
| |
| // Command pool must support graphics, compute, or transfer operations |
| const auto pool = cb_state.command_pool; |
| |
| VkQueueFlags queue_flags = physical_device_state->queue_family_properties[pool->queueFamilyIndex].queueFlags; |
| |
| if (0 == (queue_flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT))) { |
| const LogObjectList objlist(cb_state.createInfo.commandPool, commandBuffer, srcImage, dstBuffer); |
| vuid = is_2 ? "VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool" : "VUID-vkCmdCopyImageToBuffer-commandBuffer-cmdpool"; |
| skip |= LogError(vuid, objlist, loc, "command buffer allocated from a pool with queue type %s.", |
| string_VkQueueFlags(queue_flags).c_str()); |
| } |
| |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-pRegions-04566" : "VUID-vkCmdCopyImageToBuffer-imageSubresource-07970"; |
| skip |= ValidateImageBounds(commandBuffer, *src_image_state, regionCount, pRegions, loc, vuid, true); |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-pRegions-00183" : "VUID-vkCmdCopyImageToBuffer-pRegions-00183"; |
| skip |= ValidateBufferBounds(commandBuffer, *src_image_state, *dst_buffer_state, regionCount, pRegions, loc, vuid); |
| |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-srcImage-07973" : "VUID-vkCmdCopyImageToBuffer-srcImage-07973"; |
| skip |= ValidateImageSampleCount(commandBuffer, *src_image_state, VK_SAMPLE_COUNT_1_BIT, src_image_loc, vuid); |
| |
| vuid = is_2 ? "VUID-vkCmdCopyImageToBuffer-srcImage-07966" : "VUID-vkCmdCopyImageToBuffer-srcImage-07966"; |
| skip |= ValidateMemoryIsBoundToImage(LogObjectList(commandBuffer, srcImage), *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "vkCmdCopyImageToBuffer-dstBuffer2-00192" : "vkCmdCopyImageToBuffer dstBuffer-00192"; |
| skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_buffer_state, dst_buffer_loc, vuid); |
| |
| // Validate that SRC image & DST buffer have correct usage flags set |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-srcImage-00186" : "VUID-vkCmdCopyImageToBuffer-srcImage-00186"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *src_image_state, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, true, vuid, src_image_loc); |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-dstBuffer-00191" : "VUID-vkCmdCopyImageToBuffer-dstBuffer-00191"; |
| skip |= ValidateBufferUsageFlags(LogObjectList(commandBuffer, dstBuffer), *dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, |
| true, vuid, dst_buffer_loc); |
| vuid = is_2 ? "VUID-vkCmdCopyImageToBuffer2-commandBuffer-01831" : "VUID-vkCmdCopyImageToBuffer-commandBuffer-01831"; |
| skip |= ValidateProtectedImage(cb_state, *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyImageToBuffer2-commandBuffer-01832" : "VUID-vkCmdCopyImageToBuffer-commandBuffer-01832"; |
| skip |= ValidateProtectedBuffer(cb_state, *dst_buffer_state, dst_buffer_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyImageToBuffer2-commandBuffer-01833" : "VUID-vkCmdCopyImageToBuffer-commandBuffer-01833"; |
| skip |= ValidateUnprotectedBuffer(cb_state, *dst_buffer_state, dst_buffer_loc, vuid); |
| |
| // Validation for VK_EXT_fragment_density_map |
| if (src_image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstBuffer); |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-srcImage-07969" : "VUID-vkCmdCopyImageToBuffer-srcImage-07969"; |
| skip |= LogError(vuid, objlist, src_image_loc, "was created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT."); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_maintenance1)) { |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-srcImage-01998" : "VUID-vkCmdCopyImageToBuffer-srcImage-01998"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *src_image_state, VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT, |
| src_image_loc, vuid); |
| } |
| |
| const char *src_invalid_layout_vuid = |
| is_2 ? "VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397" : "VUID-vkCmdCopyImageToBuffer-srcImageLayout-01397"; |
| |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location subresource_loc = region_loc.dot(Field::imageSubresource); |
| const RegionType region = pRegions[i]; |
| skip |= ValidateImageSubresourceLayers(cb_state.commandBuffer(), ®ion.imageSubresource, subresource_loc); |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-srcImageLayout-00189" : "VUID-vkCmdCopyImageToBuffer-srcImageLayout-00189"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *src_image_state, region.imageSubresource, srcImageLayout, |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_image_loc, src_invalid_layout_vuid, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyImageToBuffer2-imageOffset-07747" : "VUID-vkCmdCopyImageToBuffer-imageOffset-07747"; |
| skip |= ValidateCopyBufferImageTransferGranularityRequirements(cb_state, *src_image_state, ®ion, region_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-imageSubresource-07967" : "VUID-vkCmdCopyImageToBuffer-imageSubresource-07967"; |
| skip |= ValidateImageMipLevel(commandBuffer, *src_image_state, region.imageSubresource.mipLevel, |
| subresource_loc.dot(Field::mipLevel), vuid); |
| vuid = is_2 ? "VUID-VkCopyImageToBufferInfo2-imageSubresource-07968" : "VUID-vkCmdCopyImageToBuffer-imageSubresource-07968"; |
| skip |= ValidateImageArrayLayerRange(commandBuffer, *src_image_state, region.imageSubresource.baseArrayLayer, |
| region.imageSubresource.layerCount, subresource_loc, vuid); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions, |
| error_obj.location); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyImageToBuffer2KHR(VkCommandBuffer commandBuffer, |
| const VkCopyImageToBufferInfo2KHR *pCopyImageToBufferInfo, |
| const ErrorObject &error_obj) const { |
| return PreCallValidateCmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo, error_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, |
| const VkCopyImageToBufferInfo2 *pCopyImageToBufferInfo, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdCopyImageToBuffer(commandBuffer, pCopyImageToBufferInfo->srcImage, pCopyImageToBufferInfo->srcImageLayout, |
| pCopyImageToBufferInfo->dstBuffer, pCopyImageToBufferInfo->regionCount, |
| pCopyImageToBufferInfo->pRegions, error_obj.location.dot(Field::pCopyImageToBufferInfo)); |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions) { |
| StateTracker::PreCallRecordCmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions); |
| |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| if (cb_state_ptr && src_image_state) { |
| // Make sure that all image slices record referenced layout |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*src_image_state, pRegions[i].imageSubresource, srcImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyImageToBuffer2KHR(VkCommandBuffer commandBuffer, |
| const VkCopyImageToBufferInfo2KHR *pCopyImageToBufferInfo) { |
| StateTracker::PreCallRecordCmdCopyImageToBuffer2KHR(commandBuffer, pCopyImageToBufferInfo); |
| |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(pCopyImageToBufferInfo->srcImage); |
| if (cb_state_ptr && src_image_state) { |
| // Make sure that all image slices record referenced layout |
| for (uint32_t i = 0; i < pCopyImageToBufferInfo->regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*src_image_state, pCopyImageToBufferInfo->pRegions[i].imageSubresource, |
| pCopyImageToBufferInfo->srcImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, |
| const VkCopyImageToBufferInfo2 *pCopyImageToBufferInfo) { |
| StateTracker::PreCallRecordCmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo); |
| |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(pCopyImageToBufferInfo->srcImage); |
| if (cb_state_ptr && src_image_state) { |
| // Make sure that all image slices record referenced layout |
| for (uint32_t i = 0; i < pCopyImageToBufferInfo->regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*src_image_state, pCopyImageToBufferInfo->pRegions[i].imageSubresource, |
| pCopyImageToBufferInfo->srcImageLayout); |
| } |
| } |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, |
| VkImageLayout dstImageLayout, uint32_t regionCount, const RegionType *pRegions, |
| const Location &loc) const { |
| bool skip = false; |
| auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_buffer_state = Get<BUFFER_STATE>(srcBuffer); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (!cb_state_ptr || !src_buffer_state || !dst_image_state) { |
| return skip; |
| } |
| const CMD_BUFFER_STATE &cb_state = *cb_state_ptr; |
| |
| const bool is_2 = loc.function == Func::vkCmdCopyBufferToImage2 || loc.function == Func::vkCmdCopyBufferToImage2KHR; |
| const char *vuid; |
| const Location src_buffer_loc = loc.dot(Field::srcBuffer); |
| const Location dst_image_loc = loc.dot(Field::dstImage); |
| |
| skip |= ValidateBufferImageCopyData(cb_state, regionCount, pRegions, *dst_image_state, loc); |
| |
| skip |= ValidateCmd(cb_state, loc); |
| |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-pRegions-04565" : "VUID-vkCmdCopyBufferToImage-imageSubresource-07970"; |
| skip |= ValidateImageBounds(commandBuffer, *dst_image_state, regionCount, pRegions, loc, vuid, false); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-pRegions-00171" : "VUID-vkCmdCopyBufferToImage-pRegions-00171"; |
| skip |= ValidateBufferBounds(commandBuffer, *dst_image_state, *src_buffer_state, regionCount, pRegions, loc, vuid); |
| |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImage-07973" : "VUID-vkCmdCopyBufferToImage-dstImage-07973"; |
| skip |= ValidateImageSampleCount(cb_state.commandBuffer(), *dst_image_state, VK_SAMPLE_COUNT_1_BIT, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-srcBuffer-00176" : "VUID-vkCmdCopyBufferToImage-srcBuffer-00176"; |
| skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *src_buffer_state, src_buffer_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImage-07966" : "VUID-vkCmdCopyBufferToImage-dstImage-07966"; |
| skip |= ValidateMemoryIsBoundToImage(LogObjectList(commandBuffer, dstImage), *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-srcBuffer-00174" : "VUID-vkCmdCopyBufferToImage-srcBuffer-00174"; |
| skip |= ValidateBufferUsageFlags(LogObjectList(commandBuffer, srcBuffer), *src_buffer_state, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, |
| true, vuid, src_buffer_loc); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImage-00177" : "VUID-vkCmdCopyBufferToImage-dstImage-00177"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, true, vuid, dst_image_loc); |
| vuid = is_2 ? "VUID-vkCmdCopyBufferToImage2-commandBuffer-01828" : "VUID-vkCmdCopyBufferToImage-commandBuffer-01828"; |
| skip |= ValidateProtectedBuffer(cb_state, *src_buffer_state, src_buffer_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyBufferToImage2-commandBuffer-01829" : "VUID-vkCmdCopyBufferToImage-commandBuffer-01829"; |
| skip |= ValidateProtectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyBufferToImage-commandBuffer-01830" : "VUID-vkCmdCopyBufferToImage-commandBuffer-01830"; |
| skip |= ValidateUnprotectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| |
| // Validation for VK_EXT_fragment_density_map |
| if (dst_image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| const LogObjectList objlist(commandBuffer, srcBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImage-07969" : "VUID-vkCmdCopyBufferToImage-dstImage-07969"; |
| skip |= LogError(vuid, objlist, dst_image_loc, "was created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT."); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_maintenance1)) { |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImage-01997" : "VUID-vkCmdCopyBufferToImage-dstImage-01997"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *dst_image_state, VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT, |
| dst_image_loc, vuid); |
| } |
| |
| const char *dst_invalid_layout_vuid = |
| is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396" : "VUID-vkCmdCopyBufferToImage-dstImageLayout-01396"; |
| |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location subresource_loc = region_loc.dot(Field::imageSubresource); |
| const RegionType region = pRegions[i]; |
| skip |= ValidateImageSubresourceLayers(cb_state.commandBuffer(), ®ion.imageSubresource, subresource_loc); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-dstImageLayout-00180" : "VUID-vkCmdCopyBufferToImage-dstImageLayout-00180"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *dst_image_state, region.imageSubresource, dstImageLayout, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_image_loc, dst_invalid_layout_vuid, vuid); |
| vuid = is_2 ? "VUID-vkCmdCopyBufferToImage2-imageOffset-07738" : "VUID-vkCmdCopyBufferToImage-imageOffset-07738"; |
| skip |= ValidateCopyBufferImageTransferGranularityRequirements(cb_state, *dst_image_state, ®ion, region_loc, vuid); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-imageSubresource-07967" : "VUID-vkCmdCopyBufferToImage-imageSubresource-07967"; |
| skip |= ValidateImageMipLevel(commandBuffer, *dst_image_state, region.imageSubresource.mipLevel, |
| subresource_loc.dot(Field::mipLevel), vuid); |
| vuid = is_2 ? "VUID-VkCopyBufferToImageInfo2-imageSubresource-07968" : "VUID-vkCmdCopyBufferToImage-imageSubresource-07968"; |
| skip |= ValidateImageArrayLayerRange(commandBuffer, *dst_image_state, region.imageSubresource.baseArrayLayer, |
| region.imageSubresource.layerCount, subresource_loc, vuid); |
| |
| // TODO - Don't use ValidateCmdQueueFlags due to currently not having way to add more descriptive message |
| const COMMAND_POOL_STATE *command_pool = cb_state.command_pool; |
| assert(command_pool != nullptr); |
| const uint32_t queue_family_index = command_pool->queueFamilyIndex; |
| const VkQueueFlags queue_flags = physical_device_state->queue_family_properties[queue_family_index].queueFlags; |
| const VkImageAspectFlags region_aspect_mask = region.imageSubresource.aspectMask; |
| if (((queue_flags & VK_QUEUE_GRAPHICS_BIT) == 0) && |
| ((region_aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0)) { |
| const LogObjectList objlist(commandBuffer, command_pool->commandPool(), srcBuffer, dstImage); |
| vuid = is_2 ? "VUID-vkCmdCopyBufferToImage2-commandBuffer-07739" : "VUID-vkCmdCopyBufferToImage-commandBuffer-07739"; |
| skip |= LogError(vuid, objlist, subresource_loc.dot(Field::aspectMask), |
| "is %s but the command buffer (%s) was allocated from the command pool (%s) " |
| "which was created with queueFamilyIndex %" PRIu32 ", which has queue type %s.", |
| string_VkImageAspectFlags(region_aspect_mask).c_str(), FormatHandle(cb_state).c_str(), |
| FormatHandle(command_pool->commandPool()).c_str(), queue_family_index, |
| string_VkQueueFlags(queue_flags).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, |
| VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkBufferImageCopy *pRegions, const ErrorObject &error_obj) const { |
| return ValidateCmdCopyBufferToImage(commandBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions, |
| error_obj.location); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyBufferToImage2KHR(VkCommandBuffer commandBuffer, |
| const VkCopyBufferToImageInfo2KHR *pCopyBufferToImageInfo, |
| const ErrorObject &error_obj) const { |
| return PreCallValidateCmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo, error_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, |
| const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdCopyBufferToImage(commandBuffer, pCopyBufferToImageInfo->srcBuffer, pCopyBufferToImageInfo->dstImage, |
| pCopyBufferToImageInfo->dstImageLayout, pCopyBufferToImageInfo->regionCount, |
| pCopyBufferToImageInfo->pRegions, error_obj.location.dot(Field::pCopyBufferToImageInfo)); |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, |
| VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkBufferImageCopy *pRegions) { |
| StateTracker::PreCallRecordCmdCopyBufferToImage(commandBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions); |
| |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (cb_state_ptr && dst_image_state) { |
| // Make sure that all image slices are record referenced layout |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*dst_image_state, pRegions[i].imageSubresource, dstImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyBufferToImage2KHR(VkCommandBuffer commandBuffer, |
| const VkCopyBufferToImageInfo2KHR *pCopyBufferToImageInfo2KHR) { |
| StateTracker::PreCallRecordCmdCopyBufferToImage2KHR(commandBuffer, pCopyBufferToImageInfo2KHR); |
| |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto dst_image_state = Get<IMAGE_STATE>(pCopyBufferToImageInfo2KHR->dstImage); |
| if (cb_state_ptr && dst_image_state) { |
| // Make sure that all image slices are record referenced layout |
| for (uint32_t i = 0; i < pCopyBufferToImageInfo2KHR->regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*dst_image_state, pCopyBufferToImageInfo2KHR->pRegions[i].imageSubresource, |
| pCopyBufferToImageInfo2KHR->dstImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, |
| const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo) { |
| StateTracker::PreCallRecordCmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo); |
| |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto dst_image_state = Get<IMAGE_STATE>(pCopyBufferToImageInfo->dstImage); |
| if (cb_state_ptr && dst_image_state) { |
| // Make sure that all image slices are record referenced layout |
| for (uint32_t i = 0; i < pCopyBufferToImageInfo->regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*dst_image_state, pCopyBufferToImageInfo->pRegions[i].imageSubresource, |
| pCopyBufferToImageInfo->dstImageLayout); |
| } |
| } |
| } |
| |
| bool CoreChecks::UsageHostTransferCheck(VkDevice device, const IMAGE_STATE &image_state, bool has_stencil, bool has_non_stencil, |
| const char *vuid_09111, const char *vuid_09112, const char *vuid_09113, |
| const Location &loc) const { |
| bool skip = false; |
| if (has_stencil) { |
| const auto image_stencil_struct = vku::FindStructInPNextChain<VkImageStencilUsageCreateInfo>(image_state.createInfo.pNext); |
| if (image_stencil_struct != nullptr) { |
| if ((image_stencil_struct->stencilUsage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) == 0) { |
| LogObjectList objlist(device, image_state.image()); |
| skip |= LogError( |
| vuid_09112, objlist, loc, |
| "An element of pRegions has an aspectMask that includes " |
| "VK_IMAGE_ASPECT_STENCIL_BIT " |
| "and the image was created with separate stencil usage, but VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT was not " |
| "included in VkImageStencilUsageCreateInfo::stencilUsage used to create image"); |
| } |
| } else { |
| if ((image_state.createInfo.usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) == 0) { |
| LogObjectList objlist(device, image_state.image()); |
| skip |= LogError( |
| vuid_09111, objlist, loc, |
| "An element of pRegions has an aspectMask that includes " |
| "VK_IMAGE_ASPECT_STENCIL_BIT and the " |
| "image was not created with separate stencil usage, but VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT was not included " |
| "in VkImageCreateInfo::usage used to create image"); |
| } |
| } |
| } |
| if (has_non_stencil) { |
| if ((image_state.createInfo.usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) == 0) { |
| LogObjectList objlist(device, image_state.image()); |
| skip |= LogError( |
| vuid_09113, objlist, loc, |
| "An element of pRegions has an aspectMask that includes " |
| "aspects other than VK_IMAGE_ASPECT_STENCIL_BIT, but VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT was not included " |
| "in VkImageCreateInfo::usage used to create image"); |
| } |
| } |
| return skip; |
| } |
| |
| template <typename T> |
| VkImageLayout GetImageLayout(T data) { |
| return VK_IMAGE_LAYOUT_UNDEFINED; |
| } |
| template <> |
| VkImageLayout GetImageLayout<VkCopyMemoryToImageInfoEXT>(VkCopyMemoryToImageInfoEXT data) { |
| return data.dstImageLayout; |
| } |
| template <> |
| VkImageLayout GetImageLayout<VkCopyImageToMemoryInfoEXT>(VkCopyImageToMemoryInfoEXT data) { |
| return data.srcImageLayout; |
| } |
| template <typename T> |
| VkImage GetImage(T data) { |
| return VK_NULL_HANDLE; |
| } |
| template <> |
| VkImage GetImage<VkCopyMemoryToImageInfoEXT>(VkCopyMemoryToImageInfoEXT data) { |
| return data.dstImage; |
| } |
| template <> |
| VkImage GetImage<VkCopyImageToMemoryInfoEXT>(VkCopyImageToMemoryInfoEXT data) { |
| return data.srcImage; |
| } |
| template <typename InfoPointer> |
| bool CoreChecks::ValidateMemoryImageCopyCommon(VkDevice device, InfoPointer info_ptr, const Location &loc) const { |
| bool skip = false; |
| VkImage image = GetImage(*info_ptr); |
| auto image_state = Get<IMAGE_STATE>(image); |
| auto image_layout = GetImageLayout(*info_ptr); |
| auto regionCount = info_ptr->regionCount; |
| const bool from_image = loc.function == Func::vkCopyImageToMemoryEXT; |
| const Location image_loc = loc.dot(from_image ? Field::srcImage : Field::dstImage); |
| const char *info_type = from_image ? "pCopyImageToMemoryInfo" : "pCopyMemoryToImageInfo"; |
| const char *source_or_destination = from_image ? "source" : "destination"; |
| const char *image_layout_vuid = from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImageLayout-09064" |
| : "VUID-VkCopyMemoryToImageInfoEXT-dstImageLayout-09059"; |
| |
| if (!(enabled_features.host_image_copy_features.hostImageCopy)) { |
| const char *vuid = |
| from_image ? "VUID-vkCopyImageToMemoryEXT-hostImageCopy-09063" : "VUID-vkCopyMemoryToImageEXT-hostImageCopy-09058"; |
| skip |= LogError(vuid, device, loc, "the hostImageCopy feature was not enabled"); |
| } |
| |
| skip |= ValidateHeterogeneousCopyData(device, regionCount, info_ptr->pRegions, *image_state, loc); |
| skip |= ValidateMemoryIsBoundToImage( |
| LogObjectList(device, image), *image_state, image_loc, |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07966" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07966"); |
| |
| if (image_state->sparse && (!image_state->HasFullRangeBound())) { |
| const char *vuid = |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-09109" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-09109"; |
| LogObjectList objlist(device, image_state->image()); |
| skip |= LogError(vuid, objlist, image_loc, "is a sparse image with no memory bound"); |
| } |
| |
| if (image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| const char *vuid = |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07969" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07969"; |
| const LogObjectList objlist(device, image); |
| skip |= LogError(vuid, objlist, image_loc, |
| "must not have been created with flags containing " |
| "VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT"); |
| } |
| skip |= ValidateImageBounds(device, *image_state, regionCount, info_ptr->pRegions, loc, |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-imageSubresource-07970" |
| : "VUID-VkCopyMemoryToImageInfoEXT-imageSubresource-07970", |
| from_image); |
| |
| skip |= ValidateImageSampleCount( |
| device, *image_state, VK_SAMPLE_COUNT_1_BIT, image_loc, |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-07973" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-07973"); |
| |
| bool check_memcpy = (info_ptr->flags & VK_HOST_IMAGE_COPY_MEMCPY_EXT); |
| bool has_stencil = false; |
| bool has_non_stencil = false; |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location subresource_loc = region_loc.dot(Field::imageSubresource); |
| const auto region = info_ptr->pRegions[i]; |
| |
| skip |= ValidateImageMipLevel(device, *image_state, region.imageSubresource.mipLevel, subresource_loc.dot(Field::mipLevel), |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-imageSubresource-07967" |
| : "VUID-VkCopyMemoryToImageInfoEXT-imageSubresource-07967"); |
| skip |= ValidateImageArrayLayerRange(device, *image_state, region.imageSubresource.baseArrayLayer, |
| region.imageSubresource.layerCount, subresource_loc, |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-imageSubresource-07968" |
| : "VUID-VkCopyMemoryToImageInfoEXT-imageSubresource-07968"); |
| skip |= ValidateImageSubresourceLayers(device, ®ion.imageSubresource, subresource_loc); |
| if (region.imageSubresource.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) has_stencil = true; |
| if (region.imageSubresource.aspectMask & ~VK_IMAGE_ASPECT_STENCIL_BIT) has_non_stencil = true; |
| |
| if (check_memcpy) { |
| if (region.imageOffset.x != 0 || region.imageOffset.y != 0 || region.imageOffset.z != 0) { |
| const char *vuid = from_image ? "VUID-VkCopyImageToMemoryInfoEXT-imageOffset-09114" |
| : "VUID-VkCopyMemoryToImageInfoEXT-imageOffset-09114"; |
| LogObjectList objlist(device); |
| LogError(vuid, objlist, loc, |
| "%s->flags contains VK_HOST_IMAGE_COPY_MEMCPY_EXT which " |
| "means that pRegions[%" PRIu32 "].imageOffset x(%" PRIu32 "), y(%" PRIu32 ") and z(%" PRIu32 |
| ") must all be zero", |
| info_type, i, region.imageOffset.x, region.imageOffset.y, region.imageOffset.z); |
| } |
| const VkExtent3D subresource_extent = image_state->GetEffectiveSubresourceExtent(region.imageSubresource); |
| if (!IsExtentEqual(region.imageExtent, subresource_extent)) { |
| const char *vuid = from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-09115" |
| : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-09115"; |
| LogObjectList objlist(device, image_state->image()); |
| skip |= LogError(vuid, objlist, loc, |
| "pRegion[%" PRIu32 "].imageExtent (w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 |
| ") must match the image's subresource " |
| "extents (w=%" PRIu32 ", h=%" PRIu32 ", d=%" PRIu32 |
| ") %s->flags contains VK_HOST_IMAGE_COPY_MEMCPY_EXT", |
| i, region.imageExtent.width, region.imageExtent.height, region.imageExtent.depth, |
| subresource_extent.width, subresource_extent.height, subresource_extent.depth, info_type); |
| } |
| } |
| |
| Field field = from_image ? Field::srcImageLayout : Field::dstImageLayout; |
| skip |= ValidateHostCopyCurrentLayout(device, image_layout, region.imageSubresource, i, *image_state, region_loc.dot(field), |
| source_or_destination, image_layout_vuid); |
| } |
| |
| const char *vuid_09111 = |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-09111" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-09111"; |
| const char *vuid_09112 = |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-09112" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-09112"; |
| const char *vuid_09113 = |
| from_image ? "VUID-VkCopyImageToMemoryInfoEXT-srcImage-09113" : "VUID-VkCopyMemoryToImageInfoEXT-dstImage-09113"; |
| skip |= UsageHostTransferCheck(device, *image_state, has_stencil, has_non_stencil, vuid_09111, vuid_09112, vuid_09113, loc); |
| |
| const auto &memory_states = image_state->GetBoundMemoryStates(); |
| for (const auto &state : memory_states) { |
| // Image and host memory can't overlap unless the image memory is mapped |
| if (state->mapped_range.size != 0) { |
| const uint64_t mapped_size = (state->mapped_range.size == VK_WHOLE_SIZE) |
| ? state->alloc_info.allocationSize |
| : (state->mapped_range.offset + state->mapped_range.size); |
| const void *mapped_end = static_cast<char *>(state->p_driver_data) + mapped_size; |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const auto region = info_ptr->pRegions[i]; |
| auto element_size = vkuFormatElementSize(image_state->createInfo.format); |
| uint64_t copy_size; |
| if (region.memoryRowLength != 0 && region.memoryImageHeight != 0) { |
| copy_size = ((region.memoryRowLength * region.memoryImageHeight) * element_size); |
| } else { |
| copy_size = ((region.imageExtent.width * region.imageExtent.height * region.imageExtent.depth) * element_size); |
| } |
| const void *copy_end = static_cast<const char *>(region.pHostPointer) + copy_size; |
| |
| if ((region.pHostPointer >= state->p_driver_data && region.pHostPointer < mapped_end) || |
| (copy_end >= state->p_driver_data && copy_end < mapped_end) || |
| (region.pHostPointer <= state->p_driver_data && copy_end > mapped_end)) { |
| const char *vuid = |
| from_image ? "VUID-VkImageToMemoryCopyEXT-pRegions-09067" : "VUID-VkMemoryToImageCopyEXT-pRegions-09062"; |
| LogObjectList objlist(device, image_state->image()); |
| skip |= LogError(vuid, objlist, loc.dot(Field::pRegions, i).dot(Field::pHostPointer), |
| "points to memory spanning %p through %p, which overlaps with image memory" |
| "mapped %p through %p", |
| region.pHostPointer, copy_end, state->p_driver_data, mapped_end); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateHostCopyImageCreateInfos(VkDevice device, const IMAGE_STATE &src_image_state, |
| const IMAGE_STATE &dst_image_state, const Location &loc) const { |
| bool skip = false; |
| std::stringstream mismatch_stream{}; |
| const VkImageCreateInfo src_info = src_image_state.createInfo; |
| const VkImageCreateInfo dst_info = dst_image_state.createInfo; |
| |
| if (src_info.flags != dst_info.flags) { |
| mismatch_stream << "srcImage flags = " << string_VkImageCreateFlags(src_info.flags) |
| << " and dstImage flags = " << string_VkImageCreateFlags(dst_info.flags) << "\n"; |
| } |
| if (src_info.imageType != dst_info.imageType) { |
| mismatch_stream << "srcImage imageType = " << string_VkImageType(src_info.imageType) |
| << " and dstImage imageType = " << string_VkImageType(dst_info.imageType) << "\n"; |
| } |
| if (src_info.format != dst_info.format) { |
| mismatch_stream << "srcImage format = " << string_VkFormat(src_info.format) |
| << " and dstImage format = " << string_VkFormat(dst_info.format) << "\n"; |
| } |
| if ((src_info.extent.width != dst_info.extent.width) || (src_info.extent.height != dst_info.extent.height) || |
| (src_info.extent.depth != dst_info.extent.depth)) { |
| mismatch_stream << "srcImage extent.width = " << src_info.extent.width << " extent.height = " << src_info.extent.height |
| << " extent.depth = " << src_info.extent.depth << " but dstImage extent.width = " << dst_info.extent.width |
| << " extent.height = " << dst_info.extent.height << " extent.depth = " << dst_info.extent.depth << "\n"; |
| } |
| if (src_info.mipLevels != dst_info.mipLevels) { |
| mismatch_stream << "srcImage mipLevels = " << src_info.mipLevels << "and dstImage mipLevels = " << dst_info.mipLevels |
| << "\n"; |
| } |
| if (src_info.arrayLayers != dst_info.arrayLayers) { |
| mismatch_stream << "srcImage arrayLayers = " << src_info.arrayLayers |
| << " and dstImage arrayLayers = " << dst_info.arrayLayers << "\n"; |
| } |
| if (src_info.samples != dst_info.samples) { |
| mismatch_stream << "srcImage samples = " << string_VkSampleCountFlagBits(src_info.samples) |
| << " and dstImage samples = " << string_VkSampleCountFlagBits(dst_info.samples) << "\n"; |
| } |
| if (src_info.tiling != dst_info.tiling) { |
| mismatch_stream << "srcImage tiling = " << string_VkImageTiling(src_info.tiling) |
| << " and dstImage tiling = " << string_VkImageTiling(dst_info.tiling) << "\n"; |
| } |
| if (src_info.usage != dst_info.usage) { |
| mismatch_stream << "srcImage usage = " << string_VkImageUsageFlags(src_info.usage) |
| << " and dstImage usage = " << string_VkImageUsageFlags(dst_info.usage) << "\n"; |
| } |
| if (src_info.sharingMode != dst_info.sharingMode) { |
| mismatch_stream << "srcImage sharingMode = " << string_VkSharingMode(src_info.sharingMode) |
| << " and dstImage sharingMode = " << string_VkSharingMode(dst_info.sharingMode) << "\n"; |
| } |
| if (src_info.initialLayout != dst_info.initialLayout) { |
| mismatch_stream << "srcImage initialLayout = " << string_VkImageLayout(src_info.initialLayout) |
| << " and dstImage initialLayout = " << string_VkImageLayout(dst_info.initialLayout) << "\n"; |
| } |
| |
| if (mismatch_stream.str().length() > 0) { |
| std::stringstream ss; |
| ss << "The creation parameters for srcImage and dstImage differ:\n" << mismatch_stream.str(); |
| LogObjectList objlist(device, src_image_state.image(), dst_image_state.image()); |
| skip |= LogError("VUID-VkCopyImageToImageInfoEXT-srcImage-09069", objlist, loc, "%s.", ss.str().c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateHostCopyImageLayout(const VkDevice device, const VkImage image, const uint32_t layout_count, |
| const VkImageLayout *supported_image_layouts, const VkImageLayout image_layout, |
| const Location &loc, const char *supported_name, const char *vuid) const { |
| for (uint32_t i = 0; i < layout_count; ++i) { |
| if (supported_image_layouts[i] == image_layout) { |
| return false; |
| } |
| } |
| |
| LogObjectList objlist(device, image); |
| bool skip = LogError(vuid, objlist, loc, |
| "is %s which is not one of the layouts returned in " |
| "VkPhysicalDeviceHostImageCopyPropertiesEXT::%s", |
| string_VkImageLayout(image_layout), supported_name); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCopyMemoryToImageEXT(VkDevice device, const VkCopyMemoryToImageInfoEXT *pCopyMemoryToImageInfo, |
| const ErrorObject &error_obj) const { |
| bool skip = false; |
| const Location copy_loc = error_obj.location.dot(Field::pCopyMemoryToImageInfo); |
| auto dst_image = pCopyMemoryToImageInfo->dstImage; |
| auto image_state = Get<IMAGE_STATE>(dst_image); |
| |
| skip |= ValidateMemoryImageCopyCommon(device, pCopyMemoryToImageInfo, copy_loc); |
| auto *props = &phys_dev_ext_props.host_image_copy_properties; |
| skip |= ValidateHostCopyImageLayout(device, dst_image, props->copyDstLayoutCount, props->pCopyDstLayouts, |
| pCopyMemoryToImageInfo->dstImageLayout, copy_loc.dot(Field::dstImageLayout), |
| "pCopyDstLayouts", "VUID-VkCopyMemoryToImageInfoEXT-dstImageLayout-09060"); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCopyImageToMemoryEXT(VkDevice device, const VkCopyImageToMemoryInfoEXT *pCopyImageToMemoryInfo, |
| const ErrorObject &error_obj) const { |
| bool skip = false; |
| const Location copy_loc = error_obj.location.dot(Field::pCopyImageToMemoryInfo); |
| auto src_image = pCopyImageToMemoryInfo->srcImage; |
| auto image_state = Get<IMAGE_STATE>(src_image); |
| |
| skip |= ValidateMemoryImageCopyCommon(device, pCopyImageToMemoryInfo, copy_loc); |
| auto *props = &phys_dev_ext_props.host_image_copy_properties; |
| skip |= ValidateHostCopyImageLayout(device, src_image, props->copySrcLayoutCount, props->pCopySrcLayouts, |
| pCopyImageToMemoryInfo->srcImageLayout, copy_loc.dot(Field::srcImageLayout), |
| "pCopySrcLayouts", "VUID-VkCopyImageToMemoryInfoEXT-srcImageLayout-09065"); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateMemcpyExtents(VkDevice device, const VkImageCopy2 region, const IMAGE_STATE &image_state, bool is_src, |
| const Location ®ion_loc) const { |
| bool skip = false; |
| if (region.srcOffset.x != 0 || region.srcOffset.y != 0 || region.srcOffset.z != 0) { |
| const char *vuid = |
| is_src ? "VUID-VkCopyImageToImageInfoEXT-srcOffset-09114" : "VUID-VkCopyImageToImageInfoEXT-dstOffset-09114"; |
| Field field = is_src ? Field::srcOffset : Field::dstOffset; |
| const LogObjectList objlist(device); |
| skip |= LogError(vuid, objlist, region_loc.dot(field), |
| "is (x = %" PRIu32 ", y = %" PRIu32 ", z = %" PRIu32 ") but flags contains VK_HOST_IMAGE_COPY_MEMCPY_EXT.", |
| region.srcOffset.x, region.srcOffset.y, region.srcOffset.z); |
| } |
| if (!IsExtentEqual(region.extent, image_state.createInfo.extent)) { |
| const char *vuid = |
| is_src ? "VUID-VkCopyImageToImageInfoEXT-srcImage-09115" : "VUID-VkCopyImageToImageInfoEXT-dstImage-09115"; |
| const LogObjectList objlist(device, image_state.image()); |
| skip |= LogError(vuid, objlist, region_loc.dot(Field::imageExtent), |
| "(w = %" PRIu32 ", h = %" PRIu32 ", d = %" PRIu32 |
| ") must match the image's subresource " |
| "extents (w = %" PRIu32 ", h = %" PRIu32 ", d = %" PRIu32 |
| ") when VkCopyImageToImageInfoEXT->flags contains VK_HOST_IMAGE_COPY_MEMCPY_EXT", |
| region.extent.width, region.extent.height, region.extent.depth, image_state.createInfo.extent.width, |
| image_state.createInfo.extent.height, image_state.createInfo.extent.depth); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateHostCopyMultiplane(VkDevice device, VkImageCopy2 region, const IMAGE_STATE &image_state, bool is_src, |
| const Location ®ion_loc) const { |
| bool skip = false; |
| auto aspect_mask = is_src ? region.srcSubresource.aspectMask : region.dstSubresource.aspectMask; |
| if (vkuFormatPlaneCount(image_state.createInfo.format) == 2 && |
| (aspect_mask != VK_IMAGE_ASPECT_PLANE_0_BIT && aspect_mask != VK_IMAGE_ASPECT_PLANE_1_BIT)) { |
| const char *vuid = |
| is_src ? "VUID-VkCopyImageToImageInfoEXT-srcImage-07981" : "VUID-VkCopyImageToImageInfoEXT-dstImage-07981"; |
| Field field = is_src ? Field::srcSubresource : Field::dstSubresource; |
| LogObjectList objlist(device, image_state.image()); |
| skip |= LogError(vuid, objlist, region_loc.dot(field), "is %s but %s has 2-plane format (%s).", |
| string_VkImageAspectFlags(aspect_mask).c_str(), is_src ? "srcImage" : "dstImage", |
| string_VkFormat(image_state.createInfo.format)); |
| } |
| if (vkuFormatPlaneCount(image_state.createInfo.format) == 3 && |
| (aspect_mask != VK_IMAGE_ASPECT_PLANE_0_BIT && aspect_mask != VK_IMAGE_ASPECT_PLANE_1_BIT && |
| aspect_mask != VK_IMAGE_ASPECT_PLANE_2_BIT)) { |
| const char *vuid = |
| is_src ? "VUID-VkCopyImageToImageInfoEXT-srcImage-07981" : "VUID-VkCopyImageToImageInfoEXT-dstImage-07981"; |
| Field field = is_src ? Field::srcSubresource : Field::dstSubresource; |
| LogObjectList objlist(device, image_state.image()); |
| skip |= LogError(vuid, objlist, region_loc.dot(field), "is %s but %s has 3-plane format (%s).", |
| string_VkImageAspectFlags(aspect_mask).c_str(), is_src ? "srcImage" : "dstImage", |
| string_VkFormat(image_state.createInfo.format)); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCopyImageToImageEXT(VkDevice device, const VkCopyImageToImageInfoEXT *pCopyImageToImageInfo, |
| const ErrorObject &error_obj) const { |
| bool skip = false; |
| auto info_ptr = pCopyImageToImageInfo; |
| const Location loc = error_obj.location.dot(Field::pCopyImageToImageInfo); |
| auto src_image_state = Get<IMAGE_STATE>(info_ptr->srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(info_ptr->dstImage); |
| // Formats are required to match, but check each image anyway |
| auto src_plane_count = vkuFormatPlaneCount(src_image_state->createInfo.format); |
| auto dst_plane_count = vkuFormatPlaneCount(dst_image_state->createInfo.format); |
| bool check_multiplane = ((src_plane_count == 2 || src_plane_count == 3) || (dst_plane_count == 2 || dst_plane_count == 3)); |
| bool check_memcpy = (info_ptr->flags & VK_HOST_IMAGE_COPY_MEMCPY_EXT); |
| auto regionCount = info_ptr->regionCount; |
| auto pRegions = info_ptr->pRegions; |
| |
| if (!(enabled_features.host_image_copy_features.hostImageCopy)) { |
| skip |= LogError("VUID-vkCopyImageToImageEXT-hostImageCopy-09068", device, error_obj.location, |
| "the hostImageCopy feature was not enabled"); |
| } |
| |
| skip |= ValidateHostCopyImageCreateInfos(device, *src_image_state, *dst_image_state, error_obj.location); |
| skip |= ValidateImageCopyData(device, regionCount, pRegions, *src_image_state, *dst_image_state, true, error_obj.location); |
| skip |= ValidateCopyImageCommon(device, *src_image_state, *dst_image_state, regionCount, pRegions, error_obj.location); |
| skip |= ValidateImageBounds(device, *src_image_state, regionCount, pRegions, loc, |
| "VUID-VkCopyImageToImageInfoEXT-srcSubresource-07970", true); |
| skip |= ValidateImageBounds(device, *dst_image_state, regionCount, pRegions, loc, |
| "VUID-VkCopyImageToImageInfoEXT-dstSubresource-07970", false); |
| auto *props = &phys_dev_ext_props.host_image_copy_properties; |
| skip |= ValidateHostCopyImageLayout(device, info_ptr->srcImage, props->copySrcLayoutCount, props->pCopySrcLayouts, |
| info_ptr->srcImageLayout, loc.dot(Field::srcImageLayout), "pCopySrcLayouts", |
| "VUID-VkCopyImageToImageInfoEXT-srcImageLayout-09072"); |
| skip |= ValidateHostCopyImageLayout(device, info_ptr->dstImage, props->copyDstLayoutCount, props->pCopyDstLayouts, |
| info_ptr->dstImageLayout, loc.dot(Field::dstImageLayout), "pCopyDstLayouts", |
| "VUID-VkCopyImageToImageInfoEXT-dstImageLayout-09073"); |
| |
| if (src_image_state->sparse && (!src_image_state->HasFullRangeBound())) { |
| LogObjectList objlist(device, src_image_state->image()); |
| skip |= LogError("VUID-VkCopyImageToImageInfoEXT-srcImage-09109", objlist, loc.dot(Field::srcImage), |
| "is a sparse image with no memory bound"); |
| } |
| if (dst_image_state->sparse && (!dst_image_state->HasFullRangeBound())) { |
| LogObjectList objlist(device, dst_image_state->image()); |
| skip |= LogError("VUID-VkCopyImageToImageInfoEXT-dstImage-09109", objlist, loc.dot(Field::dstImage), |
| "is a sparse image with no memory bound"); |
| } |
| |
| bool has_stencil = false; |
| bool has_non_stencil = false; |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const auto ®ion = info_ptr->pRegions[i]; |
| if (check_memcpy) { |
| skip |= ValidateMemcpyExtents(device, region, *src_image_state, true, region_loc); |
| skip |= ValidateMemcpyExtents(device, region, *dst_image_state, false, region_loc); |
| } |
| if (check_multiplane) { |
| skip |= ValidateHostCopyMultiplane(device, region, *src_image_state, true, region_loc); |
| skip |= ValidateHostCopyMultiplane(device, region, *dst_image_state, false, region_loc); |
| } |
| |
| if ((region.srcSubresource.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) { |
| has_stencil = true; |
| } |
| if ((region.srcSubresource.aspectMask & (~VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { |
| has_non_stencil = true; |
| } |
| |
| skip |= ValidateHostCopyCurrentLayout(device, info_ptr->srcImageLayout, region.srcSubresource, i, *src_image_state, |
| region_loc.dot(Field::srcImageLayout), "source", |
| "VUID-VkCopyImageToImageInfoEXT-srcImageLayout-09070"); |
| skip |= ValidateHostCopyCurrentLayout(device, info_ptr->dstImageLayout, region.dstSubresource, i, *dst_image_state, |
| region_loc.dot(Field::dstImageLayout), "destination", |
| "VUID-VkCopyImageToImageInfoEXT-dstImageLayout-09071"); |
| } |
| |
| skip |= UsageHostTransferCheck(device, *src_image_state, has_stencil, has_non_stencil, |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-09111", "VUID-VkCopyImageToImageInfoEXT-srcImage-09112", |
| "VUID-VkCopyImageToImageInfoEXT-srcImage-09113", error_obj.location); |
| skip |= UsageHostTransferCheck(device, *dst_image_state, has_stencil, has_non_stencil, |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-09111", "VUID-VkCopyImageToImageInfoEXT-dstImage-09112", |
| "VUID-VkCopyImageToImageInfoEXT-dstImage-09113", error_obj.location); |
| return skip; |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const RegionType *pRegions, VkFilter filter, const Location &loc) const { |
| bool skip = false; |
| auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (!cb_state_ptr || !src_image_state || !src_image_state) { |
| return skip; |
| } |
| |
| const bool is_2 = loc.function == Func::vkCmdBlitImage2 || loc.function == Func::vkCmdBlitImage2KHR; |
| const Location src_image_loc = loc.dot(Field::srcImage); |
| const Location dst_image_loc = loc.dot(Field::dstImage); |
| |
| const CMD_BUFFER_STATE &cb_state = *cb_state_ptr; |
| skip |= ValidateCmd(cb_state, loc); |
| |
| const char *vuid; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00233" : "VUID-vkCmdBlitImage-srcImage-00233"; |
| skip |= ValidateImageSampleCount(commandBuffer, *src_image_state, VK_SAMPLE_COUNT_1_BIT, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-00234" : "VUID-vkCmdBlitImage-dstImage-00234"; |
| skip |= ValidateImageSampleCount(commandBuffer, *dst_image_state, VK_SAMPLE_COUNT_1_BIT, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00220" : "VUID-vkCmdBlitImage-srcImage-00220"; |
| skip |= ValidateMemoryIsBoundToImage(LogObjectList(device, srcImage), *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-00225" : "VUID-vkCmdBlitImage-dstImage-00225"; |
| skip |= ValidateMemoryIsBoundToImage(LogObjectList(device, dstImage), *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00219" : "VUID-vkCmdBlitImage-srcImage-00219"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *src_image_state, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, true, vuid, src_image_loc); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-00224" : "VUID-vkCmdBlitImage-dstImage-00224"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, true, vuid, dst_image_loc); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-01999" : "VUID-vkCmdBlitImage-srcImage-01999"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *src_image_state, VK_FORMAT_FEATURE_2_BLIT_SRC_BIT, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-02000" : "VUID-vkCmdBlitImage-dstImage-02000"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *dst_image_state, VK_FORMAT_FEATURE_2_BLIT_DST_BIT, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdBlitImage2-commandBuffer-01834" : "VUID-vkCmdBlitImage-commandBuffer-01834"; |
| skip |= ValidateProtectedImage(cb_state, *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdBlitImage2-commandBuffer-01835" : "VUID-vkCmdBlitImage-commandBuffer-01835"; |
| skip |= ValidateProtectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdBlitImage2-commandBuffer-01836" : "VUID-vkCmdBlitImage-commandBuffer-01836"; |
| skip |= ValidateUnprotectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| |
| const LogObjectList src_objlist(commandBuffer, srcImage); |
| const LogObjectList dst_objlist(commandBuffer, dstImage); |
| const LogObjectList all_objlist(commandBuffer, srcImage, dstImage); |
| // Validation for VK_EXT_fragment_density_map |
| if (src_image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-02545" : "VUID-vkCmdBlitImage-dstImage-02545"; |
| skip |= LogError(vuid, src_objlist, src_image_loc, "was created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT."); |
| } |
| if (dst_image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-02545" : "VUID-vkCmdBlitImage-dstImage-02545"; |
| skip |= LogError(vuid, dst_objlist, dst_image_loc, "was created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT."); |
| } |
| |
| // TODO: Need to validate image layouts, which will include layout validation for shared presentable images |
| |
| VkFormat src_format = src_image_state->createInfo.format; |
| VkFormat dst_format = dst_image_state->createInfo.format; |
| VkImageType src_type = src_image_state->createInfo.imageType; |
| VkImageType dst_type = dst_image_state->createInfo.imageType; |
| |
| if (VK_FILTER_LINEAR == filter) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-filter-02001" : "VUID-vkCmdBlitImage-filter-02001"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *src_image_state, |
| VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT, src_image_loc, vuid); |
| } else if (VK_FILTER_CUBIC_IMG == filter) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-filter-02002" : "VUID-vkCmdBlitImage-filter-02002"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *src_image_state, VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT, |
| src_image_loc, vuid); |
| } |
| |
| if (FormatRequiresYcbcrConversionExplicitly(src_format)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-06421" : "VUID-vkCmdBlitImage-srcImage-06421"; |
| skip |= LogError(vuid, src_objlist, src_image_loc, |
| "format (%s) must not be one of the formats requiring sampler YCBCR " |
| "conversion for VK_IMAGE_ASPECT_COLOR_BIT image views", |
| string_VkFormat(src_format)); |
| } |
| |
| if (FormatRequiresYcbcrConversionExplicitly(dst_format)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-06422" : "VUID-vkCmdBlitImage-dstImage-06422"; |
| skip |= LogError(vuid, dst_objlist, dst_image_loc, |
| "format (%s) must not be one of the formats requiring sampler YCBCR " |
| "conversion for VK_IMAGE_ASPECT_COLOR_BIT image views", |
| string_VkFormat(dst_format)); |
| } |
| |
| if ((VK_FILTER_CUBIC_IMG == filter) && (VK_IMAGE_TYPE_2D != src_type)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-filter-00237" : "VUID-vkCmdBlitImage-filter-00237"; |
| skip |= LogError(vuid, src_objlist, loc.dot(Field::filter), "is VK_FILTER_CUBIC_IMG but srcImage was created with %s.", |
| string_VkImageType(src_type)); |
| } |
| |
| // Validate consistency for unsigned formats |
| if (vkuFormatIsUINT(src_format) != vkuFormatIsUINT(dst_format)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00230" : "VUID-vkCmdBlitImage-srcImage-00230"; |
| skip |= LogError(vuid, all_objlist, loc, "srcImage format %s is different than dstImage format %s.", |
| string_VkFormat(src_format), string_VkFormat(dst_format)); |
| } |
| |
| // Validate consistency for signed formats |
| if (vkuFormatIsSINT(src_format) != vkuFormatIsSINT(dst_format)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00229" : "VUID-vkCmdBlitImage-srcImage-00229"; |
| skip |= LogError(vuid, all_objlist, loc, "srcImage format %s is different than dstImage format %s.", |
| string_VkFormat(src_format), string_VkFormat(dst_format)); |
| } |
| |
| // Validate filter for Depth/Stencil formats |
| if (vkuFormatIsDepthOrStencil(src_format) && (filter != VK_FILTER_NEAREST)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00232" : "VUID-vkCmdBlitImage-srcImage-00232"; |
| skip |= LogError(vuid, src_objlist, src_image_loc, "has depth-stencil format %s but filter is %s.", |
| string_VkFormat(src_format), string_VkFilter(filter)); |
| } |
| |
| // Validate aspect bits and formats for depth/stencil images |
| if (vkuFormatIsDepthOrStencil(src_format) || vkuFormatIsDepthOrStencil(dst_format)) { |
| if (src_format != dst_format) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00231" : "VUID-vkCmdBlitImage-srcImage-00231"; |
| skip |= LogError(vuid, all_objlist, loc, "srcImage format %s is different than dstImage format %s.", |
| string_VkFormat(src_format), string_VkFormat(dst_format)); |
| } |
| } |
| |
| // Do per-region checks |
| const char *invalid_src_layout_vuid = |
| is_2 ? "VUID-VkBlitImageInfo2-srcImageLayout-01398" : "VUID-vkCmdBlitImage-srcImageLayout-01398"; |
| const char *invalid_dst_layout_vuid = |
| is_2 ? "VUID-VkBlitImageInfo2-dstImageLayout-01399" : "VUID-vkCmdBlitImage-dstImageLayout-01399"; |
| |
| const bool same_image = (src_image_state == dst_image_state); |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location src_subresource_loc = region_loc.dot(Field::srcSubresource); |
| const Location dst_subresource_loc = region_loc.dot(Field::dstSubresource); |
| const RegionType region = pRegions[i]; |
| |
| // When performing blit from and to same subresource, VK_IMAGE_LAYOUT_GENERAL is the only option |
| const VkImageSubresourceLayers &src_subresource = region.srcSubresource; |
| const VkImageSubresourceLayers &dst_subresource = region.dstSubresource; |
| |
| bool same_subresource = (same_image && (src_subresource.mipLevel == dst_subresource.mipLevel) && |
| (src_subresource.baseArrayLayer == dst_subresource.baseArrayLayer)); |
| VkImageLayout source_optimal = (same_subresource ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); |
| VkImageLayout destination_optimal = (same_subresource ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); |
| |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImageLayout-00221" : "VUID-vkCmdBlitImage-srcImageLayout-00221"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *src_image_state, src_subresource, srcImageLayout, source_optimal, |
| src_image_loc, invalid_src_layout_vuid, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImageLayout-00226" : "VUID-vkCmdBlitImage-dstImageLayout-00226"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *dst_image_state, dst_subresource, dstImageLayout, destination_optimal, |
| dst_image_loc, invalid_dst_layout_vuid, vuid); |
| skip |= ValidateImageSubresourceLayers(cb_state.commandBuffer(), &src_subresource, src_subresource_loc); |
| skip |= ValidateImageSubresourceLayers(cb_state.commandBuffer(), &dst_subresource, dst_subresource_loc); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcSubresource-01705" : "VUID-vkCmdBlitImage-srcSubresource-01705"; |
| skip |= ValidateImageMipLevel(commandBuffer, *src_image_state, src_subresource.mipLevel, |
| src_subresource_loc.dot(Field::mipLevel), vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstSubresource-01706" : "VUID-vkCmdBlitImage-dstSubresource-01706"; |
| skip |= ValidateImageMipLevel(commandBuffer, *dst_image_state, dst_subresource.mipLevel, |
| dst_subresource_loc.dot(Field::mipLevel), vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcSubresource-01707" : "VUID-vkCmdBlitImage-srcSubresource-01707"; |
| skip |= ValidateImageArrayLayerRange(commandBuffer, *src_image_state, src_subresource.baseArrayLayer, |
| src_subresource.layerCount, src_subresource_loc, vuid); |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstSubresource-01708" : "VUID-vkCmdBlitImage-dstSubresource-01708"; |
| skip |= ValidateImageArrayLayerRange(commandBuffer, *dst_image_state, dst_subresource.baseArrayLayer, |
| dst_subresource.layerCount, dst_subresource_loc, vuid); |
| // Check that src/dst layercounts match |
| if (src_subresource.layerCount != dst_subresource.layerCount && src_subresource.layerCount != VK_REMAINING_ARRAY_LAYERS && |
| dst_subresource.layerCount != VK_REMAINING_ARRAY_LAYERS) { |
| vuid = is_2 ? "VUID-VkImageBlit2-layerCount-08800" : "VUID-VkImageBlit-layerCount-08800"; |
| skip |= LogError(vuid, all_objlist, src_subresource_loc.dot(Field::layerCount), |
| "(%" PRIu32 ") does not match %s (%" PRIu32 ").", src_subresource.layerCount, |
| dst_subresource_loc.dot(Field::layerCount).Fields().c_str(), dst_subresource.layerCount); |
| } |
| |
| if (src_subresource.aspectMask != dst_subresource.aspectMask) { |
| vuid = is_2 ? "VUID-VkImageBlit2-aspectMask-00238" : "VUID-VkImageBlit-aspectMask-00238"; |
| skip |= LogError(vuid, all_objlist, src_subresource_loc.dot(Field::aspectMask), "(%s) does not match %s (%s).", |
| string_VkImageAspectFlags(src_subresource.aspectMask).c_str(), |
| dst_subresource_loc.dot(Field::aspectMask).Fields().c_str(), |
| string_VkImageAspectFlags(dst_subresource.aspectMask).c_str()); |
| } |
| |
| if (!VerifyAspectsPresent(src_subresource.aspectMask, src_format)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-aspectMask-00241" : "VUID-vkCmdBlitImage-aspectMask-00241"; |
| skip |= LogError(vuid, src_objlist, src_subresource_loc.dot(Field::aspectMask), |
| "(%s) cannot specify aspects not present in source image (%s).", |
| string_VkImageAspectFlags(src_subresource.aspectMask).c_str(), string_VkFormat(src_format)); |
| } |
| |
| if (!VerifyAspectsPresent(dst_subresource.aspectMask, dst_format)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-aspectMask-00242" : "VUID-vkCmdBlitImage-aspectMask-00242"; |
| skip |= LogError(vuid, dst_objlist, dst_subresource_loc.dot(Field::aspectMask), |
| "(%s) cannot specify aspects not present in destination image (%s).", |
| string_VkImageAspectFlags(src_subresource.aspectMask).c_str(), string_VkFormat(src_format)); |
| } |
| |
| // Validate source image offsets |
| VkExtent3D src_extent = src_image_state->GetEffectiveSubresourceExtent(src_subresource); |
| if (VK_IMAGE_TYPE_1D == src_type) { |
| if ((0 != region.srcOffsets[0].y) || (1 != region.srcOffsets[1].y)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00245" : "VUID-vkCmdBlitImage-srcImage-00245"; |
| skip |= |
| LogError(vuid, src_objlist, region_loc, |
| "srcOffsets[0].y is %" PRId32 " and srcOffsets[1].y is %" PRId32 " but srcImage is VK_IMAGE_TYPE_1D.", |
| region.srcOffsets[0].y, region.srcOffsets[1].y); |
| } |
| } |
| |
| if ((VK_IMAGE_TYPE_1D == src_type) || (VK_IMAGE_TYPE_2D == src_type)) { |
| if ((0 != region.srcOffsets[0].z) || (1 != region.srcOffsets[1].z)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00247" : "VUID-vkCmdBlitImage-srcImage-00247"; |
| skip |= LogError(vuid, src_objlist, region_loc, |
| "srcOffsets[0].z is %" PRId32 " and srcOffsets[1].z is %" PRId32 " but srcImage is %s.", |
| region.srcOffsets[0].z, region.srcOffsets[1].z, string_VkImageType(src_type)); |
| } |
| } |
| |
| bool oob = false; |
| if ((region.srcOffsets[0].x < 0) || (region.srcOffsets[0].x > static_cast<int32_t>(src_extent.width)) || |
| (region.srcOffsets[1].x < 0) || (region.srcOffsets[1].x > static_cast<int32_t>(src_extent.width))) { |
| oob = true; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcOffset-00243" : "VUID-vkCmdBlitImage-srcOffset-00243"; |
| skip |= LogError(vuid, src_objlist, region_loc, |
| "srcOffsets[0].x is %" PRId32 " and srcOffsets[1].x is %" PRId32 |
| " which exceed srcSubresource width extent (%" PRIu32 ").", |
| region.srcOffsets[0].x, region.srcOffsets[1].x, src_extent.width); |
| } |
| if ((region.srcOffsets[0].y < 0) || (region.srcOffsets[0].y > static_cast<int32_t>(src_extent.height)) || |
| (region.srcOffsets[1].y < 0) || (region.srcOffsets[1].y > static_cast<int32_t>(src_extent.height))) { |
| oob = true; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcOffset-00244" : "VUID-vkCmdBlitImage-srcOffset-00244"; |
| skip |= LogError(vuid, src_objlist, region_loc, |
| "srcOffsets[0].y is %" PRId32 " and srcOffsets[1].y is %" PRId32 |
| " which exceed srcSubresource height extent (%" PRIu32 ").", |
| region.srcOffsets[0].y, region.srcOffsets[1].y, src_extent.height); |
| } |
| if ((region.srcOffsets[0].z < 0) || (region.srcOffsets[0].z > static_cast<int32_t>(src_extent.depth)) || |
| (region.srcOffsets[1].z < 0) || (region.srcOffsets[1].z > static_cast<int32_t>(src_extent.depth))) { |
| oob = true; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcOffset-00246" : "VUID-vkCmdBlitImage-srcOffset-00246"; |
| skip |= LogError(vuid, src_objlist, region_loc, |
| "srcOffsets[0].z is %" PRId32 " and srcOffsets[1].z is %" PRId32 |
| " which exceed srcSubresource depth extent (%" PRIu32 ").", |
| region.srcOffsets[0].z, region.srcOffsets[1].z, src_extent.depth); |
| } |
| if (oob) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-pRegions-00215" : "VUID-vkCmdBlitImage-pRegions-00215"; |
| skip |= LogError(vuid, src_objlist, region_loc, "source image blit region exceeds image dimensions."); |
| } |
| |
| // Validate dest image offsets |
| VkExtent3D dst_extent = dst_image_state->GetEffectiveSubresourceExtent(dst_subresource); |
| if (VK_IMAGE_TYPE_1D == dst_type) { |
| if ((0 != region.dstOffsets[0].y) || (1 != region.dstOffsets[1].y)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-00250" : "VUID-vkCmdBlitImage-dstImage-00250"; |
| skip |= |
| LogError(vuid, dst_objlist, region_loc, |
| "dstOffsets[0].y is %" PRId32 " and dstOffsets[1].y is %" PRId32 " but dstImage is VK_IMAGE_TYPE_1D.", |
| region.dstOffsets[0].y, region.dstOffsets[1].y); |
| } |
| } |
| |
| if ((VK_IMAGE_TYPE_1D == dst_type) || (VK_IMAGE_TYPE_2D == dst_type)) { |
| if ((0 != region.dstOffsets[0].z) || (1 != region.dstOffsets[1].z)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstImage-00252" : "VUID-vkCmdBlitImage-dstImage-00252"; |
| skip |= LogError(vuid, dst_objlist, region_loc, |
| "dstOffsets[0].z is %" PRId32 " and dstOffsets[1].z is %" PRId32 " but dstImage is %s.", |
| region.dstOffsets[0].z, region.dstOffsets[1].z, string_VkImageType(dst_type)); |
| } |
| } |
| |
| oob = false; |
| if ((region.dstOffsets[0].x < 0) || (region.dstOffsets[0].x > static_cast<int32_t>(dst_extent.width)) || |
| (region.dstOffsets[1].x < 0) || (region.dstOffsets[1].x > static_cast<int32_t>(dst_extent.width))) { |
| oob = true; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstOffset-00248" : "VUID-vkCmdBlitImage-dstOffset-00248"; |
| skip |= LogError(vuid, dst_objlist, region_loc, |
| "dstOffsets[0].x is %" PRId32 " and dstOffsets[1].x is %" PRId32 |
| " which exceed dstSubresource width extent (%" PRIu32 ").", |
| region.dstOffsets[0].x, region.dstOffsets[1].x, dst_extent.width); |
| } |
| if ((region.dstOffsets[0].y < 0) || (region.dstOffsets[0].y > static_cast<int32_t>(dst_extent.height)) || |
| (region.dstOffsets[1].y < 0) || (region.dstOffsets[1].y > static_cast<int32_t>(dst_extent.height))) { |
| oob = true; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstOffset-00249" : "VUID-vkCmdBlitImage-dstOffset-00249"; |
| skip |= LogError(vuid, dst_objlist, region_loc, |
| "dstOffsets[0].y is %" PRId32 " and dstOffsets[1].y is %" PRId32 |
| " which exceed dstSubresource height extent (%" PRIu32 ").", |
| region.dstOffsets[0].x, region.dstOffsets[1].x, dst_extent.height); |
| } |
| if ((region.dstOffsets[0].z < 0) || (region.dstOffsets[0].z > static_cast<int32_t>(dst_extent.depth)) || |
| (region.dstOffsets[1].z < 0) || (region.dstOffsets[1].z > static_cast<int32_t>(dst_extent.depth))) { |
| oob = true; |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-dstOffset-00251" : "VUID-vkCmdBlitImage-dstOffset-00251"; |
| skip |= LogError(vuid, dst_objlist, region_loc, |
| "dstOffsets[0].z is %" PRId32 " and dstOffsets[1].z is %" PRId32 |
| " which exceed dstSubresource depth extent (%" PRIu32 ").", |
| region.dstOffsets[0].z, region.dstOffsets[1].z, dst_extent.depth); |
| } |
| if (oob) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-pRegions-00216" : "VUID-vkCmdBlitImage-pRegions-00216"; |
| skip |= LogError(vuid, dst_objlist, region_loc, "destination image blit region exceeds image dimensions."); |
| } |
| |
| if ((VK_IMAGE_TYPE_3D == src_type) || (VK_IMAGE_TYPE_3D == dst_type)) { |
| if ((0 != src_subresource.baseArrayLayer) || (1 != src_subresource.layerCount) || |
| (0 != dst_subresource.baseArrayLayer) || (1 != dst_subresource.layerCount)) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-srcImage-00240" : "VUID-vkCmdBlitImage-srcImage-00240"; |
| skip |= LogError(vuid, all_objlist, region_loc, |
| "srcImage %s\n" |
| "dstImage %s\n" |
| "srcSubresource (baseArrayLayer = %" PRIu32 ", layerCount = %" PRIu32 |
| ")\n" |
| "dstSubresource (baseArrayLayer = %" PRIu32 ", layerCount = %" PRIu32 ")\n", |
| string_VkImageType(src_type), string_VkImageType(dst_type), src_subresource.baseArrayLayer, |
| src_subresource.layerCount, dst_subresource.baseArrayLayer, dst_subresource.layerCount); |
| } |
| } |
| |
| // The union of all source regions, and the union of all destination regions, specified by the elements of regions, |
| // must not overlap in memory |
| if (srcImage == dstImage) { |
| for (uint32_t j = 0; j < regionCount; j++) { |
| if (RegionIntersectsBlit(®ion, &pRegions[j], src_image_state->createInfo.imageType, |
| vkuFormatIsMultiplane(src_format))) { |
| vuid = is_2 ? "VUID-VkBlitImageInfo2-pRegions-00217" : "VUID-vkCmdBlitImage-pRegions-00217"; |
| skip |= |
| LogError(vuid, all_objlist, loc, "pRegion[%" PRIu32 "] src overlaps with pRegions[%" PRIu32 "] dst.", i, j); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkImageBlit *pRegions, VkFilter filter, const ErrorObject &error_obj) const { |
| return ValidateCmdBlitImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter, |
| error_obj.location); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBlitImage2KHR(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR *pBlitImageInfo, |
| const ErrorObject &error_obj) const { |
| return PreCallValidateCmdBlitImage2(commandBuffer, pBlitImageInfo, error_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2 *pBlitImageInfo, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdBlitImage(commandBuffer, pBlitImageInfo->srcImage, pBlitImageInfo->srcImageLayout, pBlitImageInfo->dstImage, |
| pBlitImageInfo->dstImageLayout, pBlitImageInfo->regionCount, pBlitImageInfo->pRegions, |
| pBlitImageInfo->filter, error_obj.location.dot(Field::pBlitImageInfo)); |
| } |
| |
| template <typename RegionType> |
| void CoreChecks::RecordCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, |
| VkImageLayout dstImageLayout, uint32_t regionCount, const RegionType *pRegions, |
| VkFilter filter) { |
| auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (cb_state_ptr && src_image_state && dst_image_state) { |
| // Make sure that all image slices are updated to correct layout |
| for (uint32_t i = 0; i < regionCount; ++i) { |
| cb_state_ptr->SetImageInitialLayout(*src_image_state, pRegions[i].srcSubresource, srcImageLayout); |
| cb_state_ptr->SetImageInitialLayout(*dst_image_state, pRegions[i].dstSubresource, dstImageLayout); |
| } |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkImageBlit *pRegions, VkFilter filter) { |
| StateTracker::PreCallRecordCmdBlitImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, |
| pRegions, filter); |
| RecordCmdBlitImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter); |
| } |
| |
| void CoreChecks::PreCallRecordCmdBlitImage2KHR(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR *pBlitImageInfo) { |
| StateTracker::PreCallRecordCmdBlitImage2KHR(commandBuffer, pBlitImageInfo); |
| RecordCmdBlitImage(commandBuffer, pBlitImageInfo->srcImage, pBlitImageInfo->srcImageLayout, pBlitImageInfo->dstImage, |
| pBlitImageInfo->dstImageLayout, pBlitImageInfo->regionCount, pBlitImageInfo->pRegions, |
| pBlitImageInfo->filter); |
| } |
| |
| void CoreChecks::PreCallRecordCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR *pBlitImageInfo) { |
| StateTracker::PreCallRecordCmdBlitImage2(commandBuffer, pBlitImageInfo); |
| RecordCmdBlitImage(commandBuffer, pBlitImageInfo->srcImage, pBlitImageInfo->srcImageLayout, pBlitImageInfo->dstImage, |
| pBlitImageInfo->dstImageLayout, pBlitImageInfo->regionCount, pBlitImageInfo->pRegions, |
| pBlitImageInfo->filter); |
| } |
| |
| template <typename RegionType> |
| bool CoreChecks::ValidateCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const RegionType *pRegions, const Location &loc) const { |
| bool skip = false; |
| auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto src_image_state = Get<IMAGE_STATE>(srcImage); |
| auto dst_image_state = Get<IMAGE_STATE>(dstImage); |
| if (!cb_state_ptr || !src_image_state || !dst_image_state) { |
| return skip; |
| } |
| |
| const bool is_2 = loc.function == Func::vkCmdResolveImage2 || loc.function == Func::vkCmdResolveImage2KHR; |
| const char *vuid; |
| const Location src_image_loc = loc.dot(Field::srcImage); |
| const Location dst_image_loc = loc.dot(Field::dstImage); |
| |
| const CMD_BUFFER_STATE &cb_state = *cb_state_ptr; |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-00256" : "VUID-vkCmdResolveImage-srcImage-00256"; |
| skip |= ValidateMemoryIsBoundToImage(LogObjectList(commandBuffer, srcImage), *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-00258" : "VUID-vkCmdResolveImage-dstImage-00258"; |
| skip |= ValidateMemoryIsBoundToImage(LogObjectList(commandBuffer, dstImage), *dst_image_state, dst_image_loc, vuid); |
| skip |= ValidateCmd(cb_state, loc); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-02003" : "VUID-vkCmdResolveImage-dstImage-02003"; |
| skip |= ValidateImageFormatFeatureFlags(commandBuffer, *dst_image_state, VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT, |
| dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdResolveImage2-commandBuffer-01837" : "VUID-vkCmdResolveImage-commandBuffer-01837"; |
| skip |= ValidateProtectedImage(cb_state, *src_image_state, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdResolveImage2-commandBuffer-01838" : "VUID-vkCmdResolveImage-commandBuffer-01838"; |
| skip |= ValidateProtectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-vkCmdResolveImage2-commandBuffer-01839" : "VUID-vkCmdResolveImage-commandBuffer-01839"; |
| skip |= ValidateUnprotectedImage(cb_state, *dst_image_state, dst_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-06762" : "VUID-vkCmdResolveImage-srcImage-06762"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *src_image_state, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, true, vuid, src_image_loc); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-06763" : "VUID-vkCmdResolveImage-srcImage-06763"; |
| skip |= |
| ValidateImageFormatFeatureFlags(commandBuffer, *src_image_state, VK_FORMAT_FEATURE_TRANSFER_SRC_BIT, src_image_loc, vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-06764" : "VUID-vkCmdResolveImage-dstImage-06764"; |
| skip |= ValidateImageUsageFlags(commandBuffer, *dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, true, vuid, dst_image_loc); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-06765" : "VUID-vkCmdResolveImage-dstImage-06765"; |
| skip |= |
| ValidateImageFormatFeatureFlags(commandBuffer, *dst_image_state, VK_FORMAT_FEATURE_TRANSFER_DST_BIT, dst_image_loc, vuid); |
| |
| // Validation for VK_EXT_fragment_density_map |
| if (src_image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-02546" : "VUID-vkCmdResolveImage-dstImage-02546"; |
| skip |= LogError(vuid, objlist, src_image_loc, |
| "must not have been created with flags containing " |
| "VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT"); |
| } |
| if (dst_image_state->createInfo.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-02546" : "VUID-vkCmdResolveImage-dstImage-02546"; |
| skip |= LogError(vuid, objlist, dst_image_loc, |
| "must not have been created with flags containing " |
| "VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT"); |
| } |
| |
| const char *invalid_src_layout_vuid = |
| is_2 ? "VUID-VkResolveImageInfo2-srcImageLayout-01400" : "VUID-vkCmdResolveImage-srcImageLayout-01400"; |
| const char *invalid_dst_layout_vuid = |
| is_2 ? "VUID-VkResolveImageInfo2-dstImageLayout-01401" : "VUID-vkCmdResolveImage-dstImageLayout-01401"; |
| // For each region, the number of layers in the image subresource should not be zero |
| // For each region, src and dest image aspect must be color only |
| for (uint32_t i = 0; i < regionCount; i++) { |
| const Location region_loc = loc.dot(Field::pRegions, i); |
| const Location src_subresource_loc = region_loc.dot(Field::srcSubresource); |
| const Location dst_subresource_loc = region_loc.dot(Field::dstSubresource); |
| const RegionType region = pRegions[i]; |
| const VkImageSubresourceLayers &src_subresource = region.srcSubresource; |
| const VkImageSubresourceLayers &dst_subresource = region.dstSubresource; |
| |
| skip |= ValidateImageSubresourceLayers(cb_state.commandBuffer(), &src_subresource, src_subresource_loc); |
| skip |= ValidateImageSubresourceLayers(cb_state.commandBuffer(), &dst_subresource, dst_subresource_loc); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImageLayout-00260" : "VUID-vkCmdResolveImage-srcImageLayout-00260"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *src_image_state, src_subresource, srcImageLayout, |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_image_loc, invalid_src_layout_vuid, vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImageLayout-00262" : "VUID-vkCmdResolveImage-dstImageLayout-00262"; |
| skip |= VerifyImageLayoutSubresource(cb_state, *dst_image_state, dst_subresource, dstImageLayout, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_image_loc, invalid_dst_layout_vuid, vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcSubresource-01709" : "VUID-vkCmdResolveImage-srcSubresource-01709"; |
| skip |= ValidateImageMipLevel(commandBuffer, *src_image_state, src_subresource.mipLevel, |
| src_subresource_loc.dot(Field::mipLevel), vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstSubresource-01710" : "VUID-vkCmdResolveImage-dstSubresource-01710"; |
| skip |= ValidateImageMipLevel(commandBuffer, *dst_image_state, dst_subresource.mipLevel, |
| dst_subresource_loc.dot(Field::mipLevel), vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcSubresource-01711" : "VUID-vkCmdResolveImage-srcSubresource-01711"; |
| skip |= ValidateImageArrayLayerRange(commandBuffer, *src_image_state, src_subresource.baseArrayLayer, |
| src_subresource.layerCount, src_subresource_loc, vuid); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstSubresource-01712" : "VUID-vkCmdResolveImage-dstSubresource-01712"; |
| skip |= ValidateImageArrayLayerRange(commandBuffer, *dst_image_state, dst_subresource.baseArrayLayer, |
| dst_subresource.layerCount, dst_subresource_loc, vuid); |
| |
| // layer counts must match |
| if (src_subresource.layerCount != dst_subresource.layerCount && src_subresource.layerCount != VK_REMAINING_ARRAY_LAYERS && |
| dst_subresource.layerCount != VK_REMAINING_ARRAY_LAYERS) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkImageResolve2-layerCount-08803" : "VUID-VkImageResolve-layerCount-08803"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::layerCount), |
| "(%" PRIu32 ") does not match %s (%" PRIu32 ").", region.srcSubresource.layerCount, |
| dst_subresource_loc.dot(Field::layerCount).Fields().c_str(), region.dstSubresource.layerCount); |
| } |
| // For each region, src and dest image aspect must be color only |
| if ((src_subresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) || |
| (dst_subresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkImageResolve2-aspectMask-00266" : "VUID-VkImageResolve-aspectMask-00266"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::aspectMask), |
| "(%s) and dstSubresource.aspectMask (%s) must only be VK_IMAGE_ASPECT_COLOR_BIT.", |
| string_VkImageAspectFlags(src_subresource.aspectMask).c_str(), |
| string_VkImageAspectFlags(dst_subresource.aspectMask).c_str()); |
| } |
| |
| const VkImageType src_image_type = src_image_state->createInfo.imageType; |
| const VkImageType dst_image_type = dst_image_state->createInfo.imageType; |
| |
| if (VK_IMAGE_TYPE_3D == dst_image_type) { |
| if (src_subresource.layerCount != 1) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-04446" : "VUID-vkCmdResolveImage-srcImage-04446"; |
| skip |= LogError(vuid, objlist, src_subresource_loc.dot(Field::layerCount), "is %" PRIu32 " but dstImage is 3D.", |
| src_subresource.layerCount); |
| } |
| if ((dst_subresource.baseArrayLayer != 0) || (dst_subresource.layerCount != 1)) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-04447" : "VUID-vkCmdResolveImage-srcImage-04447"; |
| skip |= LogError(vuid, objlist, dst_subresource_loc.dot(Field::baseArrayLayer), |
| "is %" PRIu32 " and layerCount is %" PRIu32 " but dstImage 3D.", dst_subresource.baseArrayLayer, |
| dst_subresource.layerCount); |
| } |
| } |
| |
| if (VK_IMAGE_TYPE_1D == src_image_type) { |
| if ((region.srcOffset.y != 0) || (region.extent.height != 1)) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-00271" : "VUID-vkCmdResolveImage-srcImage-00271"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcOffset.y is %" PRId32 ", extent.height is %" PRIu32 ", but srcImage (%s) is 1D.", |
| region.srcOffset.y, region.extent.height, FormatHandle(src_image_state->image()).c_str()); |
| } |
| } |
| if ((VK_IMAGE_TYPE_1D == src_image_type) || (VK_IMAGE_TYPE_2D == src_image_type)) { |
| if ((region.srcOffset.z != 0) || (region.extent.depth != 1)) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-00273" : "VUID-vkCmdResolveImage-srcImage-00273"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcOffset.z is %" PRId32 ", extent.depth is %" PRIu32 ", but srcImage (%s) is 2D.", |
| region.srcOffset.z, region.extent.depth, FormatHandle(src_image_state->image()).c_str()); |
| } |
| } |
| |
| if (VK_IMAGE_TYPE_1D == dst_image_type) { |
| if ((region.dstOffset.y != 0) || (region.extent.height != 1)) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-00276" : "VUID-vkCmdResolveImage-dstImage-00276"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "dstOffset.y is %" PRId32 ", extent.height is %" PRIu32 ", but dstImage (%s) is 1D.", |
| region.dstOffset.y, region.extent.height, FormatHandle(dst_image_state->image()).c_str()); |
| } |
| } |
| if ((VK_IMAGE_TYPE_1D == dst_image_type) || (VK_IMAGE_TYPE_2D == dst_image_type)) { |
| if ((region.dstOffset.z != 0) || (region.extent.depth != 1)) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-00278" : "VUID-vkCmdResolveImage-dstImage-00278"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "dstOffset.z is %" PRId32 ", extent.depth is %" PRIu32 ", but dstImage (%s) is 2D.", |
| region.dstOffset.z, region.extent.depth, FormatHandle(dst_image_state->image()).c_str()); |
| } |
| } |
| |
| // Each srcImage dimension offset + extent limits must fall with image subresource extent |
| VkExtent3D subresource_extent = src_image_state->GetEffectiveSubresourceExtent(src_subresource); |
| // MipLevel bound is checked already and adding extra errors with a "subresource extent of zero" is confusing to |
| // developer |
| if (src_subresource.mipLevel < src_image_state->createInfo.mipLevels) { |
| uint32_t extent_check = ExceedsBounds(&(region.srcOffset), &(region.extent), &subresource_extent); |
| if ((extent_check & kXBit) != 0) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcOffset-00269" : "VUID-vkCmdResolveImage-srcOffset-00269"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcOffset.x (%" PRId32 ") + extent.width (%" PRIu32 |
| ") exceeds srcSubresource.extent.width (%" PRIu32 ").", |
| region.srcOffset.x, region.extent.width, subresource_extent.width); |
| } |
| |
| if ((extent_check & kYBit) != 0) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcOffset-00270" : "VUID-vkCmdResolveImage-srcOffset-00270"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcOffset.x (%" PRId32 ") + extent.height (%" PRIu32 |
| ") exceeds srcSubresource.extent.height (%" PRIu32 ").", |
| region.srcOffset.y, region.extent.height, subresource_extent.height); |
| } |
| |
| if ((extent_check & kZBit) != 0) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcOffset-00272" : "VUID-vkCmdResolveImage-srcOffset-00272"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "srcOffset.x (%" PRId32 ") + extent.depth (%" PRIu32 |
| ") exceeds srcSubresource.extent.depth (%" PRIu32 ").", |
| region.srcOffset.z, region.extent.depth, subresource_extent.depth); |
| } |
| } |
| |
| // Each dstImage dimension offset + extent limits must fall with image subresource extent |
| subresource_extent = dst_image_state->GetEffectiveSubresourceExtent(dst_subresource); |
| // MipLevel bound is checked already and adding extra errors with a "subresource extent of zero" is confusing to |
| // developer |
| if (dst_subresource.mipLevel < dst_image_state->createInfo.mipLevels) { |
| uint32_t extent_check = ExceedsBounds(&(region.dstOffset), &(region.extent), &subresource_extent); |
| if ((extent_check & kXBit) != 0) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstOffset-00274" : "VUID-vkCmdResolveImage-dstOffset-00274"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "dstOffset.x (%" PRId32 ") + extent.width (%" PRIu32 |
| ") exceeds dstSubresource.extent.width (%" PRIu32 ").", |
| region.dstOffset.x, region.extent.width, subresource_extent.width); |
| } |
| |
| if ((extent_check & kYBit) != 0) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstOffset-00275" : "VUID-vkCmdResolveImage-dstOffset-00275"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "dstOffset.x (%" PRId32 ") + extent.height (%" PRIu32 |
| ") exceeds dstSubresource.extent.height (%" PRIu32 ").", |
| region.dstOffset.x, region.extent.height, subresource_extent.height); |
| } |
| |
| if ((extent_check & kZBit) != 0) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstOffset-00277" : "VUID-vkCmdResolveImage-dstOffset-00277"; |
| skip |= LogError(vuid, objlist, region_loc, |
| "dstOffset.x (%" PRId32 ") + extent.depth (%" PRIu32 |
| ") exceeds dstSubresource.extent.depth (%" PRIu32 ").", |
| region.dstOffset.x, region.extent.depth, subresource_extent.depth); |
| } |
| } |
| } |
| |
| if (src_image_state->createInfo.format != dst_image_state->createInfo.format) { |
| const LogObjectList objlist(commandBuffer, srcImage, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-01386" : "VUID-vkCmdResolveImage-srcImage-01386"; |
| skip |= LogError(vuid, objlist, src_image_loc, "was created with format %s but dstImage format is %s.", |
| string_VkFormat(src_image_state->createInfo.format), string_VkFormat(dst_image_state->createInfo.format)); |
| } |
| if (src_image_state->createInfo.samples == VK_SAMPLE_COUNT_1_BIT) { |
| const LogObjectList objlist(commandBuffer, srcImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-srcImage-00257" : "VUID-vkCmdResolveImage-srcImage-00257"; |
| skip |= LogError(vuid, objlist, src_image_loc, "was created with sample count VK_SAMPLE_COUNT_1_BIT."); |
| } |
| if (dst_image_state->createInfo.samples != VK_SAMPLE_COUNT_1_BIT) { |
| const LogObjectList objlist(commandBuffer, dstImage); |
| vuid = is_2 ? "VUID-VkResolveImageInfo2-dstImage-00259" : "VUID-vkCmdResolveImage-dstImage-00259"; |
| skip |= LogError(vuid, objlist, dst_image_loc, "was created with sample count (%s) (not VK_SAMPLE_COUNT_1_BIT).", |
| string_VkSampleCountFlagBits(dst_image_state->createInfo.samples)); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
| VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, |
| const VkImageResolve *pRegions, const ErrorObject &error_obj) const { |
| return ValidateCmdResolveImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, |
| error_obj.location); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResolveImage2KHR(VkCommandBuffer commandBuffer, const VkResolveImageInfo2KHR *pResolveImageInfo, |
| const ErrorObject &error_obj) const { |
| return PreCallValidateCmdResolveImage2(commandBuffer, pResolveImageInfo, error_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2 *pResolveImageInfo, |
| const ErrorObject &error_obj) const { |
| return ValidateCmdResolveImage(commandBuffer, pResolveImageInfo->srcImage, pResolveImageInfo->srcImageLayout, |
| pResolveImageInfo->dstImage, pResolveImageInfo->dstImageLayout, pResolveImageInfo->regionCount, |
| pResolveImageInfo->pRegions, error_obj.location.dot(Field::pResolveImageInfo)); |
| } |