diff options
| author | Mark Lobodzinski <mark@lunarg.com> | 2016-03-29 17:10:14 -0600 |
|---|---|---|
| committer | Mark Lobodzinski <mark@lunarg.com> | 2016-03-31 17:12:55 -0600 |
| commit | 09cb0ce90a820ccb57a8a0fbf61f4343c7772d8c (patch) | |
| tree | be623e17ffd4897d2524e8e752e7ba319bdf15a0 /layers/image.cpp | |
| parent | ac4b24cb6fda0edd436f2dbbbafde7c5e40a1c3c (diff) | |
| download | usermoji-09cb0ce90a820ccb57a8a0fbf61f4343c7772d8c.tar.xz | |
layers: LX458, Extend image extent validation for CmdCopyImage
Also added many of the CmdCopyImage valid usage checks.
Change-Id: I398adf18b48eccacbd8e44ce53e50f3bf43f58ad
Diffstat (limited to 'layers/image.cpp')
| -rw-r--r-- | layers/image.cpp | 265 |
1 files changed, 213 insertions, 52 deletions
diff --git a/layers/image.cpp b/layers/image.cpp index a95584e1..164cfb18 100644 --- a/layers/image.cpp +++ b/layers/image.cpp @@ -28,12 +28,16 @@ * Author: Tobin Ehlis <tobin@lunarg.com> */ +// Allow use of STL min and max functions in Windows +#define NOMINMAX + #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <vector> +#include <algorithm> #include <unordered_map> #include <memory> using namespace std; @@ -649,71 +653,216 @@ vkCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImag } } -VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL -vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, - VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions) { +// Returns true if [x, xoffset] and [y, yoffset] overlap +static bool ranges_intersect(int32_t start, uint32_t start_offset, int32_t end, uint32_t end_offset) { + bool result = false; + uint32_t intersection_min = std::max(static_cast<uint32_t>(start), static_cast<uint32_t>(end)); + uint32_t intersection_max = std::min(static_cast<uint32_t>(start) + start_offset, static_cast<uint32_t>(end) + end_offset); + + if (intersection_max > intersection_min) { + result = true; + } + return result; +} + +// Returns true if two VkImageCopy structures overlap +static bool region_intersects(const VkImageCopy *src, const VkImageCopy *dst, VkImageType type) { + bool result = true; + if ((src->srcSubresource.mipLevel == dst->dstSubresource.mipLevel) && + (ranges_intersect(src->srcSubresource.baseArrayLayer, src->srcSubresource.layerCount, dst->dstSubresource.baseArrayLayer, + dst->dstSubresource.layerCount))) { + + switch (type) { + case VK_IMAGE_TYPE_3D: + result &= ranges_intersect(src->srcOffset.z, src->extent.depth, dst->dstOffset.z, dst->extent.depth); + // Intentionally fall through to 2D case + case VK_IMAGE_TYPE_2D: + result &= ranges_intersect(src->srcOffset.y, src->extent.height, dst->dstOffset.y, dst->extent.height); + // Intentionally fall through to 1D case + case VK_IMAGE_TYPE_1D: + result &= ranges_intersect(src->srcOffset.x, src->extent.width, dst->dstOffset.x, dst->extent.width); + break; + default: + // Unrecognized or new IMAGE_TYPE enums will be caught in parameter_validation + assert(false); + } + } + return result; +} + +// Returns true if offset and extent exceed image extents +static bool exceeds_bounds(const VkOffset3D *offset, const VkExtent3D *extent, IMAGE_STATE *image) { + bool result = false; + // Extents/depths cannot be negative but checks left in for clarity + + return result; +} + +VkBool32 cmd_copy_image_valid_usage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImage dstImage, uint32_t regionCount, + const VkImageCopy *pRegions) { + VkBool32 skipCall = VK_FALSE; layer_data *device_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map); auto srcImageEntry = device_data->imageMap.find(srcImage); auto dstImageEntry = device_data->imageMap.find(dstImage); - // For each region, src and dst number of layers should not be zero - // For each region, src and dst number of layers must match - // For each region, src aspect mask must match dest aspect mask - // For each region, color aspects cannot be mixed with depth/stencil aspects - for (uint32_t i = 0; i < regionCount; i++) { - if (pRegions[i].srcSubresource.layerCount == 0) { - char const str[] = "vkCmdCopyImage: number of layers in source subresource is zero"; - // TODO: Verify against Valid Use section of spec - skipCall |= - log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); - } + // TODO: This does not cover swapchain-created images. This should fall out when this layer is moved + // into the core_validation layer + if ((srcImageEntry != device_data->imageMap.end()) && (dstImageEntry != device_data->imageMap.end())) { - if (pRegions[i].dstSubresource.layerCount == 0) { - char const str[] = "vkCmdCopyImage: number of layers in destination subresource is zero"; - // TODO: Verify against Valid Use section of spec - skipCall |= - log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); - } + for (uint32_t i = 0; i < regionCount; i++) { - if (pRegions[i].srcSubresource.layerCount != pRegions[i].dstSubresource.layerCount) { - char const str[] = "vkCmdCopyImage: number of layers in source and destination subresources must match"; - skipCall |= - log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); - } + if (pRegions[i].srcSubresource.layerCount == 0) { + std::stringstream ss; + ss << "vkCmdCopyImage: number of layers in pRegions[" << i << "] srcSubresource is zero"; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", "%s", ss.str().c_str()); + } - if (pRegions[i].srcSubresource.aspectMask != pRegions[i].dstSubresource.aspectMask) { - char const str[] = "vkCmdCopyImage: Src and dest aspectMasks for each region must match"; - skipCall |= - log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); - } - if ((pRegions[i].srcSubresource.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) && - (pRegions[i].srcSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) { - char const str[] = "vkCmdCopyImage aspectMask cannot specify both COLOR and DEPTH/STENCIL aspects"; - skipCall |= - log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_INVALID_IMAGE_ASPECT, "IMAGE", str); - } - } + if (pRegions[i].dstSubresource.layerCount == 0) { + std::stringstream ss; + ss << "vkCmdCopyImage: number of layers in pRegions[" << i << "] dstSubresource is zero"; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", "%s", ss.str().c_str()); + } - if ((srcImageEntry != device_data->imageMap.end()) && (dstImageEntry != device_data->imageMap.end())) { - if (srcImageEntry->second.imageType != dstImageEntry->second.imageType) { - char const str[] = "vkCmdCopyImage called with unmatched source and dest image types."; - skipCall |= - log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_TYPE, "IMAGE", str); + // For each region the layerCount member of srcSubresource and dstSubresource must match + if (pRegions[i].srcSubresource.layerCount != pRegions[i].dstSubresource.layerCount) { + std::stringstream ss; + ss << "vkCmdCopyImage: number of layers in source and destination subresources for pRegions[" << i + << "] do not match"; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + + // For each region, the aspectMask member of srcSubresource and dstSubresource must match + if (pRegions[i].srcSubresource.aspectMask != pRegions[i].dstSubresource.aspectMask) { + char const str[] = "vkCmdCopyImage: Src and dest aspectMasks for each region must match"; + skipCall |= + log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + reinterpret_cast<uint64_t &>(commandBuffer), __LINE__, IMAGE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); + } + + // AspectMask must not contain VK_IMAGE_ASPECT_METADATA_BIT + if ((pRegions[i].srcSubresource.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT) || + (pRegions[i].dstSubresource.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT)) { + std::stringstream ss; + ss << "vkCmdCopyImage: pRegions[" << i << "] may not specify aspectMask containing VK_IMAGE_ASPECT_METADATA_BIT"; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_IMAGE_ASPECT, "IMAGE", "%s", ss.str().c_str()); + } + + // For each region, if aspectMask contains VK_IMAGE_ASPECT_COLOR_BIT, it must not contain either of + // VK_IMAGE_ASPECT_DEPTH_BIT or VK_IMAGE_ASPECT_STENCIL_BIT + if ((pRegions[i].srcSubresource.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) && + (pRegions[i].srcSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) { + char const str[] = "vkCmdCopyImage aspectMask cannot specify both COLOR and DEPTH/STENCIL aspects"; + skipCall |= + log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + reinterpret_cast<uint64_t &>(commandBuffer), __LINE__, IMAGE_INVALID_IMAGE_ASPECT, "IMAGE", str); + } + + // If either of the calling command's srcImage or dstImage parameters are of VkImageType VK_IMAGE_TYPE_3D, + // the baseArrayLayer and layerCount members of both srcSubresource and dstSubresource must be 0 and 1, respectively + if (((srcImageEntry->second.imageType == VK_IMAGE_TYPE_3D) || (dstImageEntry->second.imageType == VK_IMAGE_TYPE_3D)) && + ((pRegions[i].srcSubresource.baseArrayLayer != 0) || (pRegions[i].srcSubresource.layerCount != 1) || + (pRegions[i].dstSubresource.baseArrayLayer != 0) || (pRegions[i].dstSubresource.layerCount != 1))) { + std::stringstream ss; + ss << "vkCmdCopyImage: src or dstImage type was IMAGE_TYPE_3D, but in subRegion[" << i + << "] baseArrayLayer was not zero or layerCount was not 1."; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + + // MipLevel must be less than the mipLevels specified in VkImageCreateInfo when the image was created + if (pRegions[i].srcSubresource.mipLevel >= srcImageEntry->second.mipLevels) { + std::stringstream ss; + ss << "vkCmdCopyImage: pRegions[" << i + << "] specifies a src mipLevel greater than the number specified when the srcImage was created."; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + if (pRegions[i].dstSubresource.mipLevel >= dstImageEntry->second.mipLevels) { + std::stringstream ss; + ss << "vkCmdCopyImage: pRegions[" << i + << "] specifies a dst mipLevel greater than the number specified when the dstImage was created."; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + + // (baseArrayLayer + layerCount) must be less than or equal to the arrayLayers specified in VkImageCreateInfo when the + // image was created + if ((pRegions[i].srcSubresource.baseArrayLayer + pRegions[i].srcSubresource.layerCount) > + srcImageEntry->second.arraySize) { + std::stringstream ss; + ss << "vkCmdCopyImage: srcImage arrayLayers was " << srcImageEntry->second.arraySize << " but subRegion[" << i + << "] baseArrayLayer + layerCount is " + << (pRegions[i].srcSubresource.baseArrayLayer + pRegions[i].srcSubresource.layerCount); + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + if ((pRegions[i].dstSubresource.baseArrayLayer + pRegions[i].dstSubresource.layerCount) > + dstImageEntry->second.arraySize) { + std::stringstream ss; + ss << "vkCmdCopyImage: dstImage arrayLayers was " << dstImageEntry->second.arraySize << " but subRegion[" << i + << "] baseArrayLayer + layerCount is " + << (pRegions[i].dstSubresource.baseArrayLayer + pRegions[i].dstSubresource.layerCount); + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + + // The source region specified by a given element of pRegions must be a region that is contained within srcImage + if (exceeds_bounds(&pRegions[i].srcOffset, &pRegions[i].extent, &srcImageEntry->second)) { + std::stringstream ss; + ss << "vkCmdCopyImage: srcSubResource in pRegions[" << i << "] exceeds extents srcImage was created with"; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + + // The destination region specified by a given element of pRegions must be a region that is contained within dstImage + if (exceeds_bounds(&pRegions[i].dstOffset, &pRegions[i].extent, &dstImageEntry->second)) { + std::stringstream ss; + ss << "vkCmdCopyImage: dstSubResource in pRegions[" << i << "] exceeds extents dstImage was created with"; + skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + + // The union of all source regions, and the union of all destination regions, specified by the elements of pRegions, + // must not overlap in memory + if (srcImage == dstImage) { + for (uint32_t j = 0; j < regionCount; j++) { + if (region_intersects(&pRegions[i], &pRegions[j], srcImageEntry->second.imageType)) { + std::stringstream ss; + ss << "vkCmdCopyImage: pRegions[" << i << "] src overlaps with pRegions[" << j << "]."; + skipCall |= + log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, reinterpret_cast<uint64_t &>(commandBuffer), + __LINE__, IMAGE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); + } + } + } } - // Check that format is same size or exact stencil/depth - if (is_depth_format(srcImageEntry->second.format)) { + + // The formats of srcImage and dstImage 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. Depth/stencil formats must match exactly. + if (is_depth_format(srcImageEntry->second.format) || is_depth_format(dstImageEntry->second.format)) { if (srcImageEntry->second.format != dstImageEntry->second.format) { char const str[] = "vkCmdCopyImage called with unmatched source and dest image depth/stencil formats."; skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_FORMAT, "IMAGE", str); + reinterpret_cast<uint64_t &>(commandBuffer), __LINE__, IMAGE_MISMATCHED_IMAGE_FORMAT, "IMAGE", str); } } else { size_t srcSize = vk_format_get_size(srcImageEntry->second.format); @@ -722,10 +871,22 @@ vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout sr char const str[] = "vkCmdCopyImage called with unmatched source and dest image format sizes."; skipCall |= log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - (uint64_t)commandBuffer, __LINE__, IMAGE_MISMATCHED_IMAGE_FORMAT, "IMAGE", str); + reinterpret_cast<uint64_t &>(commandBuffer), __LINE__, IMAGE_MISMATCHED_IMAGE_FORMAT, "IMAGE", str); } } } + return skipCall; +} + +VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, + VkImageLayout srcImageLayout, VkImage dstImage, + VkImageLayout dstImageLayout, uint32_t regionCount, + const VkImageCopy *pRegions) { + + VkBool32 skipCall = VK_FALSE; + layer_data *device_data = get_my_data_ptr(get_dispatch_key(commandBuffer), layer_data_map); + + skipCall = cmd_copy_image_valid_usage(commandBuffer, srcImage, dstImage, regionCount, pRegions); if (VK_FALSE == skipCall) { device_data->device_dispatch_table->CmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, |
