From ccf97111f4ba16a9866641a8b517a8d07298b2dc Mon Sep 17 00:00:00 2001 From: Dave Houlton Date: Thu, 2 Feb 2017 17:26:23 -0700 Subject: layers: Add checks&tests to Img-Buf copy - GH284 Added 16 VU checks for CmdCopyImageToBuffer and CmdCopyBufferToImage Fixed 1 test in MiscImageLayerTests Added 12 test in ImageBufferCopyTests Individual commits: GH284 add VUs 1240, 1241, 1258, 1259 GH284 add VUs 1238, 1244 GH284 add 1271 1272 1273 1274 1279 1281 1746 1747 remove 1238 1244 1262 1269 GH284 add 1227 1228 1245 1246 GH284 fix validation test MiscImageLayerTests for VU 1269/1747 swap GH284 update database with new VU checks GH284 redo VUs 1227 1246, move them into core validation GH284 final checks and tests GH284 fix a rebase merge error GH284 fix uint64 format specifier that broke the Android build Change-Id: I5b69eddab945fb09d4cf3645349fa45a61576355 --- layers/core_validation.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 3 deletions(-) (limited to 'layers/core_validation.cpp') diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index 883c201b..08cb6a81 100644 --- a/layers/core_validation.cpp +++ b/layers/core_validation.cpp @@ -3414,7 +3414,7 @@ static bool checkGraphicsOrComputeBit(const layer_data *dev_data, VkQueueFlags f if (!((flags & VK_QUEUE_GRAPHICS_BIT) || (flags & VK_QUEUE_COMPUTE_BIT))) return log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__, DRAWSTATE_INVALID_COMMAND_BUFFER, "DS", - "Cannot call %s on a command buffer allocated from a pool without graphics capabilities.", name); + "Cannot call %s on a command buffer allocated from a pool without graphics or compute capabilities.", name); return false; } @@ -7719,6 +7719,100 @@ VKAPI_ATTR void VKAPI_CALL CmdBlitImage(VkCommandBuffer commandBuffer, VkImage s } } +static bool ValidateImageBounds(const layer_data *dev_data, const VkImageCreateInfo *image_info, const uint32_t regionCount, + const VkBufferImageCopy *pRegions, const char *func_name, UNIQUE_VALIDATION_ERROR_CODE msg_code) { + bool skip = false; + + for (uint32_t i = 0; i < regionCount; i++) { + bool overrun = false; + VkExtent3D extent = pRegions[i].imageExtent; + VkOffset3D offset = pRegions[i].imageOffset; + VkExtent3D image_extent = image_info->extent; + + // for compressed images, the image createInfo.extent is in texel blocks + // convert to texels here + if (vk_format_is_compressed(image_info->format)) { + VkExtent2D texel_block_extent = vk_format_compressed_block_size(image_info->format); + image_extent.width *= texel_block_extent.width; + image_extent.height *= texel_block_extent.height; + } + + // Extents/depths cannot be negative but checks left in for clarity + switch (image_info->imageType) { + case VK_IMAGE_TYPE_3D: // Validate z and depth + if ((offset.z + extent.depth > image_extent.depth) || (offset.z < 0) || + ((offset.z + static_cast(extent.depth)) < 0)) { + overrun = true; + } + // Intentionally fall through to 2D case to check height + case VK_IMAGE_TYPE_2D: // Validate y and height + if ((offset.y + extent.height > image_extent.height) || (offset.y < 0) || + ((offset.y + static_cast(extent.height)) < 0)) { + overrun = true; + } + // Intentionally fall through to 1D case to check width + case VK_IMAGE_TYPE_1D: // Validate x and width + if ((offset.x + extent.width > image_extent.width) || (offset.x < 0) || + ((offset.x + static_cast(extent.width)) < 0)) { + overrun = true; + } + break; + default: + assert(false); + } + + if (overrun) { + skip |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + (uint64_t)0, __LINE__, msg_code, "DS", "%s: pRegion[%d] exceeds image bounds. %s.", func_name, i, + validation_error_map[msg_code]); + } + } + + return skip; +} + +static inline bool ValidtateBufferBounds(layer_data *dev_data, IMAGE_STATE *image_state, BUFFER_STATE *buff_state, + uint32_t regionCount, const VkBufferImageCopy *pRegions, const char *func_name, + UNIQUE_VALIDATION_ERROR_CODE msg_code) { + bool skip = false; + + VkDeviceSize buffer_size = buff_state->createInfo.size; + + for (uint32_t i = 0; i < regionCount; i++) { + VkExtent3D copy_extent = pRegions[i].imageExtent; + + VkDeviceSize buffer_width = (0 == pRegions[i].bufferRowLength ? copy_extent.width : pRegions[i].bufferRowLength); + VkDeviceSize buffer_height = (0 == pRegions[i].bufferImageHeight ? copy_extent.height : pRegions[i].bufferImageHeight); + VkDeviceSize unit_size = vk_format_get_size(image_state->createInfo.format); // size (bytes) of texel or block + + if (vk_format_is_compressed(image_state->createInfo.format)) { + VkExtent2D texel_block_extent = vk_format_compressed_block_size(image_state->createInfo.format); + buffer_width /= texel_block_extent.width; // switch to texel block units + buffer_height /= texel_block_extent.height; + copy_extent.width /= texel_block_extent.width; + copy_extent.height /= texel_block_extent.height; + } + + // Either depth or layerCount must be 1 (or both). This is the number of 'slices' to copy + uint32_t zCopy = max(copy_extent.depth, pRegions[i].imageSubresource.layerCount); + assert(zCopy > 0); + + // Calculate buffer offset of final copied byte, + 1. + VkDeviceSize max_buffer_offset = (zCopy - 1) * buffer_height * buffer_width; // offset to slice + max_buffer_offset += ((copy_extent.height - 1) * buffer_width) + copy_extent.width; // add row,col + max_buffer_offset *= unit_size; // convert to bytes + max_buffer_offset += pRegions[i].bufferOffset; // add initial offset (bytes) + + if (buffer_size < max_buffer_offset) { + skip |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + (uint64_t)0, __LINE__, msg_code, "DS", "%s: pRegion[%d] exceeds buffer bounds. %s.", func_name, i, + validation_error_map[msg_code]); + } + } + + return skip; +} + VKAPI_ATTR void VKAPI_CALL CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions) { @@ -7730,6 +7824,34 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyBufferToImage(VkCommandBuffer commandBuffer, V auto src_buff_state = GetBufferState(dev_data, srcBuffer); auto dst_image_state = GetImageState(dev_data, dstImage); if (cb_node && src_buff_state && dst_image_state) { + // Validate command buffer state + if (CB_RECORDING != cb_node->state) { + skip_call |= + log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + (uint64_t)cb_node->commandBuffer, __LINE__, VALIDATION_ERROR_01240, "DS", + "Cannot call vkCmdCopyBufferToImage() on command buffer which is not in recording state. %s.", + validation_error_map[VALIDATION_ERROR_01240]); + } else { + skip_call |= ValidateCmdSubpassState(dev_data, cb_node, CMD_COPYBUFFERTOIMAGE); + } + + // Command pool must support graphics, compute, or transfer operations + auto pPool = GetCommandPoolNode(dev_data, cb_node->createInfo.commandPool); + VkQueueFlags queue_flags = dev_data->phys_dev_properties.queue_family_properties[pPool->queueFamilyIndex].queueFlags; + if (0 == (queue_flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT))) { + skip_call |= + log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + (uint64_t)cb_node->createInfo.commandPool, __LINE__, VALIDATION_ERROR_01241, "DS", + "Cannot call vkCmdCopyBufferToImage() on a command buffer allocated from a pool without graphics, compute, " + "or transfer capabilities. %s.", + validation_error_map[VALIDATION_ERROR_01241]); + } + + skip_call |= ValidateImageBounds(dev_data, &(dst_image_state->createInfo), regionCount, pRegions, + "vkCmdCopyBufferToImage()", VALIDATION_ERROR_01228); + skip_call |= ValidtateBufferBounds(dev_data, dst_image_state, src_buff_state, regionCount, pRegions, + "vkCmdCopyBufferToImage()", VALIDATION_ERROR_01227); + skip_call |= ValidateImageSampleCount(dev_data, dst_image_state, VK_SAMPLE_COUNT_1_BIT, "vkCmdCopyBufferToImage(): dstImage", VALIDATION_ERROR_01232); skip_call |= ValidateMemoryIsBoundToBuffer(dev_data, src_buff_state, "vkCmdCopyBufferToImage()", VALIDATION_ERROR_02535); @@ -7749,7 +7871,6 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyBufferToImage(VkCommandBuffer commandBuffer, V function = [=]() { return ValidateBufferMemoryIsValid(dev_data, src_buff_state, "vkCmdCopyBufferToImage()"); }; cb_node->validate_functions.push_back(function); - skip_call |= ValidateCmd(dev_data, cb_node, CMD_COPYBUFFERTOIMAGE, "vkCmdCopyBufferToImage()"); UpdateCmdBufferLastCmd(cb_node, CMD_COPYBUFFERTOIMAGE); skip_call |= insideRenderPass(dev_data, cb_node, "vkCmdCopyBufferToImage()", VALIDATION_ERROR_01242); for (uint32_t i = 0; i < regionCount; ++i) { @@ -7760,6 +7881,8 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyBufferToImage(VkCommandBuffer commandBuffer, V } } else { assert(0); + + // TODO: report VU01244 here, or put in object tracker? } lock.unlock(); if (!skip_call) @@ -7776,6 +7899,34 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, V auto src_image_state = GetImageState(dev_data, srcImage); auto dst_buff_state = GetBufferState(dev_data, dstBuffer); if (cb_node && src_image_state && dst_buff_state) { + // Validate command buffer state + if (CB_RECORDING != cb_node->state) { + skip_call |= + log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + (uint64_t)cb_node->commandBuffer, __LINE__, VALIDATION_ERROR_01258, "DS", + "Cannot call vkCmdCopyImageToBuffer() on command buffer which is not in recording state. %s.", + validation_error_map[VALIDATION_ERROR_01258]); + } else { + skip_call |= ValidateCmdSubpassState(dev_data, cb_node, CMD_COPYIMAGETOBUFFER); + } + + // Command pool must support graphics, compute, or transfer operations + auto pPool = GetCommandPoolNode(dev_data, cb_node->createInfo.commandPool); + VkQueueFlags queue_flags = dev_data->phys_dev_properties.queue_family_properties[pPool->queueFamilyIndex].queueFlags; + if (0 == (queue_flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT))) { + skip_call |= + log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + (uint64_t)cb_node->createInfo.commandPool, __LINE__, VALIDATION_ERROR_01259, "DS", + "Cannot call vkCmdCopyImageToBuffer() on a command buffer allocated from a pool without graphics, compute, " + "or transfer capabilities. %s.", + validation_error_map[VALIDATION_ERROR_01259]); + } + + skip_call |= ValidateImageBounds(dev_data, &(src_image_state->createInfo), regionCount, pRegions, + "vkCmdCopyBufferToImage()", VALIDATION_ERROR_01245); + skip_call |= ValidtateBufferBounds(dev_data, src_image_state, dst_buff_state, regionCount, pRegions, + "vkCmdCopyImageToBuffer()", VALIDATION_ERROR_01246); + skip_call |= ValidateImageSampleCount(dev_data, src_image_state, VK_SAMPLE_COUNT_1_BIT, "vkCmdCopyImageToBuffer(): srcImage", VALIDATION_ERROR_01249); skip_call |= ValidateMemoryIsBoundToImage(dev_data, src_image_state, "vkCmdCopyImageToBuffer()", VALIDATION_ERROR_02537); @@ -7799,7 +7950,6 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, V }; cb_node->validate_functions.push_back(function); - skip_call |= ValidateCmd(dev_data, cb_node, CMD_COPYIMAGETOBUFFER, "vkCmdCopyImageToBuffer()"); UpdateCmdBufferLastCmd(cb_node, CMD_COPYIMAGETOBUFFER); skip_call |= insideRenderPass(dev_data, cb_node, "vkCmdCopyImageToBuffer()", VALIDATION_ERROR_01260); for (uint32_t i = 0; i < regionCount; ++i) { @@ -7810,6 +7960,8 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, V } } else { assert(0); + + // TODO: report VU01262 here, or put in object tracker? } lock.unlock(); if (!skip_call) -- cgit v1.2.3