diff options
Diffstat (limited to 'layers/core_validation.cpp')
| -rw-r--r-- | layers/core_validation.cpp | 158 |
1 files changed, 155 insertions, 3 deletions
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<int32_t>(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<int32_t>(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<int32_t>(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) |
