blob: d728ea7f70a6ce377b6f6df9bcbc3839d5bb1e60 [file] [log] [blame]
/* 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.
* Modifications Copyright (C) 2022 RasterGrid Kft.
*
* 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 <vulkan/vk_enum_string_helper.h>
#include "core_validation.h"
#include "generated/chassis.h"
#include "generated/pnext_chain_extraction.h"
bool CoreChecks::ValidateImageFormatFeatures(const VkImageCreateInfo *pCreateInfo, const Location &loc) const {
bool skip = false;
// validates based on imageCreateFormatFeatures from vkspec.html#resources-image-creation-limits
VkFormatFeatureFlags2KHR tiling_features = 0;
const VkImageTiling image_tiling = pCreateInfo->tiling;
const VkFormat image_format = pCreateInfo->format;
if (image_format == VK_FORMAT_UNDEFINED) {
// VU 01975 states format can't be undefined unless an android externalFormat
#ifdef VK_USE_PLATFORM_ANDROID_KHR
const VkExternalFormatANDROID *ext_fmt_android = vku::FindStructInPNextChain<VkExternalFormatANDROID>(pCreateInfo->pNext);
if ((image_tiling == VK_IMAGE_TILING_OPTIMAL) && (ext_fmt_android != nullptr) && (0 != ext_fmt_android->externalFormat)) {
auto it = ahb_ext_formats_map.find(ext_fmt_android->externalFormat);
if (it != ahb_ext_formats_map.end()) {
tiling_features = it->second;
}
}
#endif
} else if (image_tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
vvl::unordered_set<uint64_t> drm_format_modifiers;
const VkImageDrmFormatModifierExplicitCreateInfoEXT *drm_explicit =
vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(pCreateInfo->pNext);
const VkImageDrmFormatModifierListCreateInfoEXT *drm_implicit =
vku::FindStructInPNextChain<VkImageDrmFormatModifierListCreateInfoEXT>(pCreateInfo->pNext);
if (drm_explicit != nullptr) {
drm_format_modifiers.insert(drm_explicit->drmFormatModifier);
} else {
// VUID 02261 makes sure its only explict or implict in parameter checking
assert(drm_implicit != nullptr);
for (uint32_t i = 0; i < drm_implicit->drmFormatModifierCount; i++) {
drm_format_modifiers.insert(drm_implicit->pDrmFormatModifiers[i]);
}
}
VkDrmFormatModifierPropertiesListEXT fmt_drm_props = vku::InitStructHelper();
VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
DispatchGetPhysicalDeviceFormatProperties2(physical_device, image_format, &fmt_props_2);
std::vector<VkDrmFormatModifierPropertiesEXT> drm_properties;
drm_properties.resize(fmt_drm_props.drmFormatModifierCount);
fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();
DispatchGetPhysicalDeviceFormatProperties2(physical_device, image_format, &fmt_props_2);
for (uint32_t i = 0; i < fmt_drm_props.drmFormatModifierCount; i++) {
if (drm_format_modifiers.find(fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifier) !=
drm_format_modifiers.end()) {
tiling_features |= fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifierTilingFeatures;
}
}
} else {
VkFormatProperties3KHR format_properties = GetPDFormatProperties(image_format);
tiling_features = (image_tiling == VK_IMAGE_TILING_LINEAR) ? format_properties.linearTilingFeatures
: format_properties.optimalTilingFeatures;
}
// Lack of disjoint format feature support while using the flag
if (vkuFormatIsMultiplane(image_format) && ((pCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0) &&
((tiling_features & VK_FORMAT_FEATURE_2_DISJOINT_BIT_KHR) == 0)) {
skip |= LogError("VUID-VkImageCreateInfo-imageCreateFormatFeatures-02260", device, loc.dot(Field::usage),
"includes VK_IMAGE_CREATE_DISJOINT_BIT, but %s doesn't support "
"VK_FORMAT_FEATURE_DISJOINT_BIT (supported features: %s).",
string_VkFormat(pCreateInfo->format), string_VkFormatFeatureFlags2(tiling_features).c_str());
}
if (((tiling_features & VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT) == 0) &&
(pCreateInfo->usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT)) {
skip |= LogError("VUID-VkImageCreateInfo-imageCreateFormatFeatures-09048", device, loc.dot(Field::usage),
"includes VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT, but %s doesn't support "
"VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT (supported features: %s).",
string_VkFormat(pCreateInfo->format), string_VkFormatFeatureFlags2(tiling_features).c_str());
}
return skip;
}
bool CoreChecks::PreCallValidateCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImage *pImage,
const ErrorObject &error_obj) const {
bool skip = false;
const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo);
if (IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) {
skip |= ValidateCreateImageANDROID(pCreateInfo, create_info_loc);
} else { // These checks are omitted or replaced when Android HW Buffer extension is active
if (pCreateInfo->format == VK_FORMAT_UNDEFINED) {
return LogError("VUID-VkImageCreateInfo-pNext-01975", device, create_info_loc.dot(Field::format),
"must not be VK_FORMAT_UNDEFINED.");
}
}
const VkPhysicalDeviceLimits *device_limits = &phys_dev_props.limits;
const VkImageUsageFlags attach_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
if (pCreateInfo->usage & attach_flags) {
if (pCreateInfo->extent.width > device_limits->maxFramebufferWidth) {
skip |= LogError("VUID-VkImageCreateInfo-usage-00964", device, create_info_loc.dot(Field::usage),
"(%s) include a frame buffer attachment bit and image width (%" PRIu32
") exceeds "
"device maxFramebufferWidth (%" PRIu32 ").",
string_VkImageUsageFlags(pCreateInfo->usage).c_str(), pCreateInfo->extent.width,
device_limits->maxFramebufferWidth);
}
if (pCreateInfo->extent.height > device_limits->maxFramebufferHeight) {
skip |= LogError("VUID-VkImageCreateInfo-usage-00965", device, create_info_loc.dot(Field::usage),
"(%s) include a frame buffer attachment bit and image height (%" PRIu32
") exceeds "
"device maxFramebufferHeight (%" PRIu32 ").",
string_VkImageUsageFlags(pCreateInfo->usage).c_str(), pCreateInfo->extent.height,
device_limits->maxFramebufferHeight);
}
}
VkImageCreateFlags sparseFlags =
VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;
if ((pCreateInfo->flags & sparseFlags) && (pCreateInfo->usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) {
skip |= LogError("VUID-VkImageCreateInfo-None-01925", device, create_info_loc,
"images using sparse memory cannot have VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT set.");
}
if (!enabled_features.fragment_density_map_offset_features.fragmentDensityMapOffset &&
(pCreateInfo->usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT)) {
uint32_t ceiling_width = static_cast<uint32_t>(ceilf(
static_cast<float>(device_limits->maxFramebufferWidth) /
std::max(static_cast<float>(phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.width), 1.0f)));
if (pCreateInfo->extent.width > ceiling_width) {
skip |= LogError("VUID-VkImageCreateInfo-fragmentDensityMapOffset-06514", device, create_info_loc.dot(Field::usage),
"includes VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT and image width (%" PRIu32
") exceeds the "
"ceiling of device "
"maxFramebufferWidth (%" PRIu32 ") / minFragmentDensityTexelSize.width (%" PRIu32
"). The ceiling value: %" PRIu32 ".",
pCreateInfo->extent.width, device_limits->maxFramebufferWidth,
phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.width, ceiling_width);
}
uint32_t ceiling_height = static_cast<uint32_t>(ceilf(
static_cast<float>(device_limits->maxFramebufferHeight) /
std::max(static_cast<float>(phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.height), 1.0f)));
if (pCreateInfo->extent.height > ceiling_height) {
skip |= LogError("VUID-VkImageCreateInfo-fragmentDensityMapOffset-06515", device, create_info_loc.dot(Field::usage),
"includes VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT and image height (%" PRIu32
") exceeds the "
"ceiling of device "
"maxFramebufferHeight (%" PRIu32 ") / minFragmentDensityTexelSize.height (%" PRIu32
"). The ceiling value: %" PRIu32 ".",
pCreateInfo->extent.height, device_limits->maxFramebufferHeight,
phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.height, ceiling_height);
}
}
VkImageFormatProperties2 image_format_properties = vku::InitStructHelper();
VkPhysicalDeviceImageFormatInfo2 image_format_info = vku::InitStructHelper();
image_format_info.type = pCreateInfo->imageType;
image_format_info.format = pCreateInfo->format;
image_format_info.tiling = pCreateInfo->tiling;
image_format_info.usage = pCreateInfo->usage;
image_format_info.flags = pCreateInfo->flags;
vvl::PnextChainVkPhysicalDeviceImageFormatInfo2 image_format_info_pnext_chain{};
image_format_info.pNext = vvl::PnextChainExtract(pCreateInfo->pNext, image_format_info_pnext_chain);
// Exit early if any thing is not succesful
VkResult result = VK_SUCCESS;
if (pCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
if (IsExtEnabled(device_extensions.vk_khr_get_physical_device_properties2)) {
result = DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &image_format_info, &image_format_properties);
} else {
result = DispatchGetPhysicalDeviceImageFormatProperties(physical_device, pCreateInfo->format, pCreateInfo->imageType,
pCreateInfo->tiling, pCreateInfo->usage, pCreateInfo->flags,
&image_format_properties.imageFormatProperties);
}
// 1. vkGetPhysicalDeviceImageFormatProperties[2] only success code is VK_SUCCESS
// 2. If call returns an error, then "imageCreateImageFormatPropertiesList" is defined to be the empty list
// 3. All values in 02251 are undefined if "imageCreateImageFormatPropertiesList" is empty.
if (result != VK_SUCCESS) {
// External memory will always have a "imageCreateImageFormatPropertiesList" so skip
#ifdef VK_USE_PLATFORM_ANDROID_KHR
if (!vku::FindStructInPNextChain<VkExternalFormatANDROID>(pCreateInfo->pNext)) {
#endif // VK_USE_PLATFORM_ANDROID_KHR
Func command = IsExtEnabled(device_extensions.vk_khr_get_physical_device_properties2)
? Func::vkGetPhysicalDeviceImageFormatProperties2
: Func::vkGetPhysicalDeviceImageFormatProperties;
skip |= LogError("VUID-VkImageCreateInfo-imageCreateMaxMipLevels-02251", device, create_info_loc,
"The following parameters -\n"
"format (%s)\n"
"type (%s)\n"
"tiling (%s)\n"
"usage (%s)\n"
"flags (%s)\n"
"returned (%s) when calling %s.",
string_VkFormat(pCreateInfo->format), string_VkImageType(pCreateInfo->imageType),
string_VkImageTiling(pCreateInfo->tiling), string_VkImageUsageFlags(pCreateInfo->usage).c_str(),
string_VkImageCreateFlags(pCreateInfo->flags).c_str(), string_VkResult(result), String(command));
#ifdef VK_USE_PLATFORM_ANDROID_KHR
}
#endif // VK_USE_PLATFORM_ANDROID_KHR
}
} else {
auto *modifier_list = vku::FindStructInPNextChain<VkImageDrmFormatModifierListCreateInfoEXT>(pCreateInfo->pNext);
auto *explicit_modifier = vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(pCreateInfo->pNext);
VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_format_modifier = vku::InitStructHelper();
drm_format_modifier.sharingMode = pCreateInfo->sharingMode;
drm_format_modifier.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount;
drm_format_modifier.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices;
vvl::PnextChainScopedAdd scoped_add_drm_fmt_mod(&image_format_info, &drm_format_modifier);
if (modifier_list) {
for (uint32_t i = 0; i < modifier_list->drmFormatModifierCount; i++) {
drm_format_modifier.drmFormatModifier = modifier_list->pDrmFormatModifiers[i];
result =
DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &image_format_info, &image_format_properties);
// The application gives a list of modifier and the driver selects one. If one is valid, stop there.
if (result == VK_SUCCESS) {
break;
}
}
} else if (explicit_modifier) {
drm_format_modifier.drmFormatModifier = explicit_modifier->drmFormatModifier;
result = DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &image_format_info, &image_format_properties);
}
if (result != VK_SUCCESS) {
// Will not have to worry about VkExternalFormatANDROID if using DRM format modifier
std::string drm_source = modifier_list ? "pDrmFormatModifiers[]" : "VkImageDrmFormatModifierExplicitCreateInfoEXT";
skip |= LogError("VUID-VkImageCreateInfo-imageCreateMaxMipLevels-02251", device, create_info_loc,
"The following parameters -\n"
"format (%s)\n"
"type (%s)\n"
"tiling (%s)\n"
"usage (%s)\n"
"flags (%s)\n"
"drmFormatModifier (%" PRIu64
") from %s\n"
"returned (%s) when calling VkGetPhysicalDeviceImageFormatProperties2.",
string_VkFormat(pCreateInfo->format), string_VkImageType(pCreateInfo->imageType),
string_VkImageTiling(pCreateInfo->tiling), string_VkImageUsageFlags(pCreateInfo->usage).c_str(),
string_VkImageCreateFlags(pCreateInfo->flags).c_str(), drm_format_modifier.drmFormatModifier,
drm_source.c_str(), string_VkResult(result));
}
}
// only check if we got valid image format info back
if (result == VK_SUCCESS) {
const auto format_limits = image_format_properties.imageFormatProperties;
if (pCreateInfo->mipLevels > format_limits.maxMipLevels) {
skip |= LogError("VUID-VkImageCreateInfo-mipLevels-02255", device, create_info_loc.dot(Field::mipLevels),
"(%d) exceed image format maxMipLevels (%d) for format %s.", pCreateInfo->mipLevels,
format_limits.maxMipLevels, string_VkFormat(pCreateInfo->format));
}
uint64_t texel_count = static_cast<uint64_t>(pCreateInfo->extent.width) *
static_cast<uint64_t>(pCreateInfo->extent.height) *
static_cast<uint64_t>(pCreateInfo->extent.depth) * static_cast<uint64_t>(pCreateInfo->arrayLayers) *
static_cast<uint64_t>(pCreateInfo->samples);
// Depth/Stencil formats size can't be accurately calculated
if (!vkuFormatIsDepthAndStencil(pCreateInfo->format)) {
uint64_t total_size =
static_cast<uint64_t>(std::ceil(vkuFormatTexelSize(pCreateInfo->format) * static_cast<double>(texel_count)));
// Round up to imageGranularity boundary
VkDeviceSize image_granularity = phys_dev_props.limits.bufferImageGranularity;
uint64_t ig_mask = image_granularity - 1;
total_size = (total_size + ig_mask) & ~ig_mask;
if (total_size > format_limits.maxResourceSize) {
skip |= LogWarning(kVUID_Core_Image_InvalidFormatLimitsViolation, device, error_obj.location,
"resource size exceeds allowable maximum Image resource size = %" PRIu64
", maximum resource size = %" PRIu64 " for format %s.",
total_size, format_limits.maxResourceSize, string_VkFormat(pCreateInfo->format));
}
}
if (pCreateInfo->arrayLayers > format_limits.maxArrayLayers) {
skip |= LogError("VUID-VkImageCreateInfo-arrayLayers-02256", device, create_info_loc.dot(Field::arrayLayers),
"(%d) exceeds allowable maximum supported by format %s (format maxArrayLayers: %" PRIu32 ").",
pCreateInfo->arrayLayers, string_VkFormat(pCreateInfo->format), format_limits.maxArrayLayers);
}
if ((pCreateInfo->samples & format_limits.sampleCounts) == 0) {
skip |= LogError("VUID-VkImageCreateInfo-samples-02258", device, create_info_loc.dot(Field::samples),
"(%s) is not supported by format %s (format sampleCounts: 0x%.8X).",
string_VkSampleCountFlagBits(pCreateInfo->samples), string_VkFormat(pCreateInfo->format),
format_limits.sampleCounts);
}
if (pCreateInfo->extent.width > format_limits.maxExtent.width) {
skip |= LogError("VUID-VkImageCreateInfo-extent-02252", device, create_info_loc.dot(Field::extent).dot(Field::width),
"(%" PRIu32 ") exceeds allowable maximum image extent width %" PRIu32 " for format %s.",
pCreateInfo->extent.width, format_limits.maxExtent.width, string_VkFormat(pCreateInfo->format));
}
if (pCreateInfo->extent.height > format_limits.maxExtent.height) {
skip |= LogError("VUID-VkImageCreateInfo-extent-02253", device, create_info_loc.dot(Field::extent).dot(Field::height),
"(%" PRIu32 ") exceeds allowable maximum image extent height %" PRIu32 " for format %s.",
pCreateInfo->extent.height, format_limits.maxExtent.height, string_VkFormat(pCreateInfo->format));
}
if (pCreateInfo->extent.depth > format_limits.maxExtent.depth) {
skip |= LogError("VUID-VkImageCreateInfo-extent-02254", device, create_info_loc.dot(Field::extent).dot(Field::depth),
"(%" PRIu32 ") exceeds allowable maximum image extent depth %" PRIu32 " for format %s.",
pCreateInfo->extent.depth, format_limits.maxExtent.depth, string_VkFormat(pCreateInfo->format));
}
}
// Tests for "Formats requiring sampler YCBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views"
if (FormatRequiresYcbcrConversionExplicitly(pCreateInfo->format)) {
if (pCreateInfo->mipLevels != 1) {
skip |= LogError("VUID-VkImageCreateInfo-format-06410", device, create_info_loc.dot(Field::mipLevels),
"(%d), but when using a YCbCr Conversion format (%s), mipLevels must be 1.", pCreateInfo->mipLevels,
string_VkFormat(pCreateInfo->format));
}
if (pCreateInfo->samples != VK_SAMPLE_COUNT_1_BIT) {
skip |= LogError("VUID-VkImageCreateInfo-format-06411", device, create_info_loc.dot(Field::samples),
"(%s), but when using a YCbCr Conversion format (%s), samples must be "
"VK_SAMPLE_COUNT_1_BIT.",
string_VkSampleCountFlagBits(pCreateInfo->samples), string_VkFormat(pCreateInfo->format));
}
if (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) {
skip |= LogError("VUID-VkImageCreateInfo-format-06412", device, create_info_loc.dot(Field::imageType),
"(%s), but when using a YCbCr Conversion format (%s), imageType must be "
"VK_IMAGE_TYPE_2D.",
string_VkImageType(pCreateInfo->imageType), string_VkFormat(pCreateInfo->format));
}
}
if (IsExtEnabled(device_extensions.vk_khr_maintenance2)) {
if (pCreateInfo->flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT) {
if (!vkuFormatIsCompressed(pCreateInfo->format)) {
skip |= LogError(
"VUID-VkImageCreateInfo-flags-01572", device, create_info_loc.dot(Field::flags),
"contains VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, but format (%s) must be a compressed image format.",
string_VkFormat(pCreateInfo->format));
}
if (!(pCreateInfo->flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT)) {
skip |= LogError("VUID-VkImageCreateInfo-flags-01573", device, create_info_loc.dot(Field::flags),
"contains VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, "
"flags must also contain VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT.");
}
}
}
if (pCreateInfo->sharingMode == VK_SHARING_MODE_CONCURRENT && pCreateInfo->pQueueFamilyIndices) {
skip |= ValidatePhysicalDeviceQueueFamilies(pCreateInfo->queueFamilyIndexCount, pCreateInfo->pQueueFamilyIndices,
create_info_loc, "VUID-VkImageCreateInfo-sharingMode-01420");
}
if (!vkuFormatIsMultiplane(pCreateInfo->format) && !(pCreateInfo->flags & VK_IMAGE_CREATE_ALIAS_BIT) &&
(pCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT)) {
skip |= LogError("VUID-VkImageCreateInfo-format-01577", device, create_info_loc,
"format is %s and flags are %s. The flags should not include VK_IMAGE_CREATE_DISJOINT_BIT.",
string_VkFormat(pCreateInfo->format), string_VkImageCreateFlags(pCreateInfo->flags).c_str());
}
const auto swapchain_create_info = vku::FindStructInPNextChain<VkImageSwapchainCreateInfoKHR>(pCreateInfo->pNext);
if (swapchain_create_info != nullptr) {
if (swapchain_create_info->swapchain != VK_NULL_HANDLE) {
auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain_create_info->swapchain);
const VkSwapchainCreateFlagsKHR swapchain_flags = swapchain_state->createInfo.flags;
// Validate rest of Swapchain Image create check that require swapchain state
const char *vuid = "VUID-VkImageSwapchainCreateInfoKHR-swapchain-00995";
if (((swapchain_flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) != 0) &&
((pCreateInfo->flags & VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT) == 0)) {
skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
"was created with VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR flag so "
"all swapchain images must have the VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT flag set.");
}
if (((swapchain_flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) != 0) &&
((pCreateInfo->flags & VK_IMAGE_CREATE_PROTECTED_BIT) == 0)) {
skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
"was created with VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR flag so all "
"swapchain images must have the VK_IMAGE_CREATE_PROTECTED_BIT flag set.");
}
const VkImageCreateFlags mutable_flags = (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT);
if (((swapchain_flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) != 0) &&
((pCreateInfo->flags & mutable_flags) != mutable_flags)) {
skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
"was created with VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR flag so "
"all swapchain images must have the VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT and "
"VK_IMAGE_CREATE_EXTENDED_USAGE_BIT flags both set.");
}
}
}
if ((pCreateInfo->flags & VK_IMAGE_CREATE_PROTECTED_BIT) != 0) {
if (enabled_features.core11.protectedMemory == VK_FALSE) {
skip |= LogError("VUID-VkImageCreateInfo-flags-01890", device, create_info_loc.dot(Field::flags),
"has VK_IMAGE_CREATE_PROTECTED_BIT set, but the protectedMemory device feature is not enabled.");
}
const VkImageCreateFlags invalid_flags =
VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;
if ((pCreateInfo->flags & invalid_flags) != 0) {
skip |= LogError("VUID-VkImageCreateInfo-None-01891", device, create_info_loc.dot(Field::flags),
"can't have both protected and sparse flags set.");
}
}
if ((pCreateInfo->flags & VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT) != 0) {
if (!(enabled_features.multisampled_render_to_single_sampled_features.multisampledRenderToSingleSampled)) {
skip |= LogError("VUID-VkImageCreateInfo-multisampledRenderToSingleSampled-06882", device,
create_info_loc.dot(Field::flags),
"contains VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT but the "
"multisampledRenderToSingleSampled feature is not enabled.");
}
if (pCreateInfo->samples != VK_SAMPLE_COUNT_1_BIT) {
skip |= LogError("VUID-VkImageCreateInfo-flags-06883", device, create_info_loc.dot(Field::flags),
"contains VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT but samples (%s) is not equal "
"to VK_SAMPLE_COUNT_1_BIT.",
string_VkSampleCountFlagBits(pCreateInfo->samples));
}
}
skip |= ValidateImageFormatFeatures(pCreateInfo, create_info_loc);
// Check compatibility with VK_KHR_portability_subset
if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) {
if (VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT & pCreateInfo->flags &&
VK_FALSE == enabled_features.portability_subset_features.imageView2DOn3DImage) {
skip |= LogError("VUID-VkImageCreateInfo-imageView2DOn3DImage-04459", device, create_info_loc,
"(portability error) VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT is not supported.");
}
if ((VK_SAMPLE_COUNT_1_BIT != pCreateInfo->samples) && (1 != pCreateInfo->arrayLayers) &&
(VK_FALSE == enabled_features.portability_subset_features.multisampleArrayImage)) {
skip |= LogError("VUID-VkImageCreateInfo-multisampleArrayImage-04460", device, create_info_loc,
"(portability error) Cannot create an image with samples/texel > 1 && arrayLayers != 1");
}
}
const auto external_memory_create_info_nv = vku::FindStructInPNextChain<VkExternalMemoryImageCreateInfoNV>(pCreateInfo->pNext);
const auto external_memory_create_info = vku::FindStructInPNextChain<VkExternalMemoryImageCreateInfo>(pCreateInfo->pNext);
if (external_memory_create_info_nv != nullptr && external_memory_create_info != nullptr) {
skip |= LogError("VUID-VkImageCreateInfo-pNext-00988", device, create_info_loc,
"has both VkExternalMemoryImageCreateInfoNV and "
"VkExternalMemoryImageCreateInfo chained structs.");
}
if (external_memory_create_info && external_memory_create_info->handleTypes != 0) {
if (pCreateInfo->initialLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
skip |= LogError("VUID-VkImageCreateInfo-pNext-01443", device,
create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfo, Field::handleTypes),
"is %" PRIu32 " but pCreateInfo->initialLayout is %s.", external_memory_create_info->handleTypes,
string_VkImageLayout(pCreateInfo->initialLayout));
}
// Check external memory handle types compatibility
const uint32_t any_type = 1u << MostSignificantBit(external_memory_create_info->handleTypes);
VkPhysicalDeviceExternalImageFormatInfo external_image_info = vku::InitStructHelper();
external_image_info.handleType = static_cast<VkExternalMemoryHandleTypeFlagBits>(any_type);
vvl::PnextChainScopedAdd scoped_add_ext_img_info(&image_format_info, &external_image_info);
VkExternalImageFormatProperties external_image_properties = vku::InitStructHelper();
VkImageFormatProperties2 image_properties = vku::InitStructHelper(&external_image_properties);
VkExternalMemoryHandleTypeFlags compatible_types = 0;
if (pCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
result = DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &image_format_info, &image_properties);
compatible_types = external_image_properties.externalMemoryProperties.compatibleHandleTypes;
} else {
auto modifier_list = vku::FindStructInPNextChain<VkImageDrmFormatModifierListCreateInfoEXT>(pCreateInfo->pNext);
auto explicit_modifier = vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(pCreateInfo->pNext);
VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_format_modifier = vku::InitStructHelper();
drm_format_modifier.sharingMode = pCreateInfo->sharingMode;
drm_format_modifier.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount;
drm_format_modifier.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices;
vvl::PnextChainScopedAdd scoped_add_drm_fmt_mod(&image_format_info, &drm_format_modifier);
if (modifier_list) {
for (uint32_t i = 0; i < modifier_list->drmFormatModifierCount; i++) {
drm_format_modifier.drmFormatModifier = modifier_list->pDrmFormatModifiers[i];
result =
DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &image_format_info, &image_properties);
if (result == VK_SUCCESS) {
compatible_types = external_image_properties.externalMemoryProperties.compatibleHandleTypes;
if ((external_memory_create_info->handleTypes & compatible_types) ==
external_memory_create_info->handleTypes)
break;
}
}
if (compatible_types != 0) result = VK_SUCCESS;
} else if (explicit_modifier) {
drm_format_modifier.drmFormatModifier = explicit_modifier->drmFormatModifier;
result = DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &image_format_info, &image_properties);
compatible_types = external_image_properties.externalMemoryProperties.compatibleHandleTypes;
}
}
if (result != VK_SUCCESS) {
skip |= LogError(
"VUID-VkImageCreateInfo-pNext-00990", device, create_info_loc,
"The handle type (%s), format (%s), type (%s), tiling (%s), usage (%s), flags (%s) "
"is not supported combination of parameters and vkGetPhysicalDeviceImageFormatProperties2 returned back %s.",
string_VkExternalMemoryHandleTypeFlagBits(external_image_info.handleType),
string_VkFormat(image_format_info.format), string_VkImageType(image_format_info.type),
string_VkImageTiling(image_format_info.tiling), string_VkImageUsageFlags(image_format_info.usage).c_str(),
string_VkImageCreateFlags(image_format_info.flags).c_str(), string_VkResult(result));
} else if ((external_memory_create_info->handleTypes & compatible_types) != external_memory_create_info->handleTypes) {
skip |= LogError(
"VUID-VkImageCreateInfo-pNext-00990", device,
create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfo, Field::handleTypes),
"(%s) is not reported as compatible by vkGetPhysicalDeviceImageFormatProperties2. Compatible types are %s.",
string_VkExternalMemoryHandleTypeFlags(external_memory_create_info->handleTypes).c_str(),
string_VkExternalMemoryHandleTypeFlags(compatible_types).c_str());
}
} else if (external_memory_create_info_nv && external_memory_create_info_nv->handleTypes != 0) {
if (pCreateInfo->initialLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
skip |= LogError("VUID-VkImageCreateInfo-pNext-01443", device,
create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfoNV, Field::handleTypes),
"is %" PRIu32 " but pCreateInfo->initialLayout is %s.", external_memory_create_info_nv->handleTypes,
string_VkImageLayout(pCreateInfo->initialLayout));
}
// Check external memory handle types compatibility
const uint32_t any_type = 1u << MostSignificantBit(external_memory_create_info_nv->handleTypes);
auto handle_type = static_cast<VkExternalMemoryHandleTypeFlagBitsNV>(any_type);
VkExternalImageFormatPropertiesNV external_image_properties = {};
result = DispatchGetPhysicalDeviceExternalImageFormatPropertiesNV(
physical_device, pCreateInfo->format, pCreateInfo->imageType, pCreateInfo->tiling, pCreateInfo->usage,
pCreateInfo->flags, handle_type, &external_image_properties);
const auto compatible_types = external_image_properties.compatibleHandleTypes;
if (result != VK_SUCCESS) {
skip |= LogError("VUID-VkImageCreateInfo-pNext-00991", device, create_info_loc,
"The handle type (%s), format (%s), type (%s), tiling (%s), usage (%s), flags (%s) "
"is not supported combination of parameters and vkGetPhysicalDeviceExternalImageFormatPropertiesNV "
"returned back %s.",
string_VkExternalMemoryHandleTypeFlagBitsNV(handle_type), string_VkFormat(pCreateInfo->format),
string_VkImageType(pCreateInfo->imageType), string_VkImageTiling(pCreateInfo->tiling),
string_VkImageUsageFlags(pCreateInfo->usage).c_str(),
string_VkImageCreateFlags(pCreateInfo->flags).c_str(), string_VkResult(result));
} else if ((external_memory_create_info_nv->handleTypes & compatible_types) !=
external_memory_create_info_nv->handleTypes) {
skip |= LogError("VUID-VkImageCreateInfo-pNext-00991", device,
create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfoNV, Field::handleTypes),
"(%s) is not reported as compatible by vkGetPhysicalDeviceExternalImageFormatPropertiesNV.",
string_VkExternalMemoryHandleTypeFlagsNV(external_memory_create_info_nv->handleTypes).c_str());
}
}
if (device_group_create_info.physicalDeviceCount == 1) {
if (pCreateInfo->flags & VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT) {
skip |= LogError("VUID-VkImageCreateInfo-physicalDeviceCount-01421", device, create_info_loc.dot(Field::flags),
"contains VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT, but the device was created with "
"VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1. Device creation with "
"VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1 may have been implicit.");
}
}
if ((pCreateInfo->flags & VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT) &&
!enabled_features.descriptor_buffer_features.descriptorBufferCaptureReplay) {
skip |= LogError("VUID-VkImageCreateInfo-flags-08104", device, create_info_loc.dot(Field::flags),
"contains VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT but the descriptorBufferCaptureReplay "
"feature is not enabled.");
}
auto opaque_capture_descriptor_buffer = vku::FindStructInPNextChain<VkOpaqueCaptureDescriptorDataCreateInfoEXT>(pCreateInfo->pNext);
if (opaque_capture_descriptor_buffer && !(pCreateInfo->flags & VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)) {
skip |= LogError("VUID-VkImageCreateInfo-pNext-08105", device, create_info_loc.dot(Field::flags),
"(%s) does not have VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, but "
"VkOpaqueCaptureDescriptorDataCreateInfoEXT is in pNext chain.",
string_VkImageCreateFlags(pCreateInfo->flags).c_str());
}
bool has_decode_usage =
pCreateInfo->usage & (VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR);
bool has_encode_usage =
pCreateInfo->usage & (VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
if (has_decode_usage || has_encode_usage) {
const auto *video_profiles = vku::FindStructInPNextChain<VkVideoProfileListInfoKHR>(pCreateInfo->pNext);
skip |= ValidateVideoProfileListInfo(video_profiles, device, "vkCreateImage", has_decode_usage,
"VUID-VkImageCreateInfo-usage-04815", has_encode_usage,
"VUID-VkImageCreateInfo-usage-04816");
if (video_profiles && video_profiles->profileCount > 0) {
auto format_props_list = GetVideoFormatProperties(pCreateInfo->usage, video_profiles);
bool supported_video_format = false;
for (auto &format_props : format_props_list) {
if (pCreateInfo->format == format_props.format &&
(pCreateInfo->flags & format_props.imageCreateFlags) == pCreateInfo->flags &&
pCreateInfo->imageType == format_props.imageType && pCreateInfo->tiling == format_props.imageTiling &&
(pCreateInfo->usage & format_props.imageUsageFlags) == pCreateInfo->usage) {
supported_video_format = true;
}
}
if (!supported_video_format) {
skip |=
LogError("VUID-VkImageCreateInfo-pNext-06811", device, create_info_loc,
"image creation parameters (flags: 0x%08x, format: %s, imageType: %s, "
"tiling: %s) are not supported by any of the supported video format properties for "
"the video profiles specified in the VkVideoProfileListInfoKHR structure included in "
"the pCreateInfo->pNext chain, as reported by "
"vkGetPhysicalDeviceVideoFormatPropertiesKHR for the same video profiles "
"and the image usage flags specified in pCreateInfo->usage (0x%08x)",
pCreateInfo->flags, string_VkFormat(pCreateInfo->format), string_VkImageType(pCreateInfo->imageType),
string_VkImageTiling(pCreateInfo->tiling), pCreateInfo->usage);
}
}
}
return skip;
}
void CoreChecks::PostCallRecordCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImage *pImage,
const RecordObject &record_obj) {
if (VK_SUCCESS != record_obj.result) return;
StateTracker::PostCallRecordCreateImage(device, pCreateInfo, pAllocator, pImage, record_obj);
if ((pCreateInfo->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0) {
// non-sparse images set up their layout maps when memory is bound
auto image_state = Get<IMAGE_STATE>(*pImage);
image_state->SetInitialLayoutMap();
}
}
bool CoreChecks::PreCallValidateDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator,
const ErrorObject &error_obj) const {
auto image_state = Get<IMAGE_STATE>(image);
bool skip = false;
if (image_state) {
if (image_state->IsSwapchainImage() && image_state->owned_by_swapchain) {
skip |= LogError("VUID-vkDestroyImage-image-04882", image, error_obj.location.dot(Field::image),
"%s is a presentable image controlled by the implementation and must be destroyed "
"with vkDestroySwapchainKHR.",
FormatHandle(image_state->image()).c_str());
}
skip |= ValidateObjectNotInUse(image_state.get(), error_obj.location, "VUID-vkDestroyImage-image-01000");
}
return skip;
}
void CoreChecks::PreCallRecordDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator) {
// Clean up validation specific data
auto image_state = Get<IMAGE_STATE>(image);
qfo_release_image_barrier_map.erase(image);
// Clean up generic image state
StateTracker::PreCallRecordDestroyImage(device, image, pAllocator);
}
bool CoreChecks::ValidateClearImageSubresourceRange(const CMD_BUFFER_STATE &cb_state, const IMAGE_STATE &image_state,
const VkImageSubresourceRange &range, const Location &loc) const {
bool skip = false;
if (range.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) {
LogObjectList objlist(cb_state.commandBuffer(), image_state.image());
skip |= LogError("VUID-vkCmdClearColorImage-aspectMask-02498", objlist, loc.dot(Field::aspectMask),
"is %s (must only include COLOR_BIT).", string_VkImageAspectFlags(range.aspectMask).c_str());
}
return skip;
}
bool CoreChecks::PreCallValidateCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearColorValue *pColor, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges, const ErrorObject &error_obj) const {
bool skip = false;
// TODO : Verify memory is in VK_IMAGE_STATE_CLEAR state
auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer);
auto image_state_ptr = Get<IMAGE_STATE>(image);
if (!cb_state_ptr || !image_state_ptr) {
return skip;
}
const auto &cb_state = *cb_state_ptr;
const auto &image_state = *image_state_ptr;
const Location image_loc = error_obj.location.dot(Field::image);
skip |= ValidateMemoryIsBoundToImage(LogObjectList(commandBuffer, image), image_state, image_loc,
"VUID-vkCmdClearColorImage-image-00003");
skip |= ValidateCmd(cb_state, error_obj.location);
if (IsExtEnabled(device_extensions.vk_khr_maintenance1)) {
skip |= ValidateImageFormatFeatureFlags(commandBuffer, image_state, VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT_KHR, image_loc,
"VUID-vkCmdClearColorImage-image-01993");
}
skip |= ValidateProtectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearColorImage-commandBuffer-01805");
skip |= ValidateUnprotectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearColorImage-commandBuffer-01806");
for (uint32_t i = 0; i < rangeCount; ++i) {
const Location range_loc = error_obj.location.dot(Field::pRanges, i);
skip |= ValidateCmdClearColorSubresourceRange(image_state, pRanges[i], range_loc);
skip |= ValidateClearImageSubresourceRange(cb_state, image_state, pRanges[i], range_loc);
skip |= VerifyClearImageLayout(cb_state, image_state, pRanges[i], imageLayout, range_loc);
}
const VkFormat format = image_state.createInfo.format;
if (vkuFormatIsDepthOrStencil(format)) {
LogObjectList objlist(commandBuffer, image);
skip |=
LogError("VUID-vkCmdClearColorImage-image-00007", objlist, image_loc,
"(%s) was created with a depth/stencil format (%s).", FormatHandle(image).c_str(), string_VkFormat(format));
} else if (vkuFormatIsCompressed(format)) {
LogObjectList objlist(commandBuffer, image);
skip |= LogError("VUID-vkCmdClearColorImage-image-00007", objlist, image_loc,
"(%s) was created with a compressed format (%s).", FormatHandle(image).c_str(), string_VkFormat(format));
}
if (!(image_state.createInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
LogObjectList objlist(commandBuffer, image);
skip |= LogError("VUID-vkCmdClearColorImage-image-00002", objlist, image_loc,
"(%s) was created with usage %s (missing VK_IMAGE_USAGE_TRANSFER_DST_BIT).", FormatHandle(image).c_str(),
string_VkImageUsageFlags(image_state.createInfo.usage).c_str());
}
// Tests for "Formats requiring sampler Y’CBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views"
if (FormatRequiresYcbcrConversionExplicitly(format)) {
LogObjectList objlist(commandBuffer, image);
skip |= LogError("VUID-vkCmdClearColorImage-image-01545", objlist, image_loc, "(%s) was created with format %s.",
FormatHandle(image).c_str(), string_VkFormat(format));
}
return skip;
}
void CoreChecks::PreCallRecordCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearColorValue *pColor, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges) {
StateTracker::PreCallRecordCmdClearColorImage(commandBuffer, image, imageLayout, pColor, rangeCount, pRanges);
auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer);
auto image_state = Get<IMAGE_STATE>(image);
if (cb_state_ptr && image_state) {
for (uint32_t i = 0; i < rangeCount; ++i) {
cb_state_ptr->SetImageInitialLayout(image, pRanges[i], imageLayout);
}
}
}
bool CoreChecks::ValidateClearDepthStencilValue(VkCommandBuffer commandBuffer, VkClearDepthStencilValue clearValue,
const Location &loc) const {
bool skip = false;
if (!IsExtEnabled(device_extensions.vk_ext_depth_range_unrestricted)) {
if (!(clearValue.depth >= 0.0) || !(clearValue.depth <= 1.0)) {
skip |=
LogError("VUID-VkClearDepthStencilValue-depth-00022", commandBuffer, loc.dot(Field::depth),
"is %f (not within the [0.0, 1.0] range) but VK_EXT_depth_range_unrestricted extension is not enabled.",
clearValue.depth);
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges,
const ErrorObject &error_obj) const {
bool skip = false;
// TODO : Verify memory is in VK_IMAGE_STATE_CLEAR state
auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer);
auto image_state_ptr = Get<IMAGE_STATE>(image);
if (!cb_state_ptr || !image_state_ptr) {
return skip;
}
const auto &cb_state = *cb_state_ptr;
const auto &image_state = *image_state_ptr;
const Location image_loc = error_obj.location.dot(Field::image);
const VkFormat image_format = image_state.createInfo.format;
skip |= ValidateMemoryIsBoundToImage(LogObjectList(commandBuffer, image), image_state, image_loc,
"VUID-vkCmdClearDepthStencilImage-image-00010");
skip |= ValidateCmd(cb_state, error_obj.location);
if (IsExtEnabled(device_extensions.vk_khr_maintenance1)) {
skip |= ValidateImageFormatFeatureFlags(commandBuffer, image_state, VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT_KHR, image_loc,
"VUID-vkCmdClearDepthStencilImage-image-01994");
}
skip |= ValidateClearDepthStencilValue(commandBuffer, *pDepthStencil, error_obj.location.dot(Field::pDepthStencil));
skip |= ValidateProtectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearDepthStencilImage-commandBuffer-01807");
skip |= ValidateUnprotectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearDepthStencilImage-commandBuffer-01808");
const auto image_stencil_struct = vku::FindStructInPNextChain<VkImageStencilUsageCreateInfo>(image_state.createInfo.pNext);
for (uint32_t i = 0; i < rangeCount; ++i) {
const Location range_loc = error_obj.location.dot(Field::pRanges, i);
skip |= ValidateCmdClearDepthSubresourceRange(image_state, pRanges[i], range_loc);
skip |= VerifyClearImageLayout(cb_state, image_state, pRanges[i], imageLayout, range_loc);
// Image aspect must be depth or stencil or both
VkImageAspectFlags valid_aspects = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
if (((pRanges[i].aspectMask & valid_aspects) == 0) || ((pRanges[i].aspectMask & ~valid_aspects) != 0)) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |=
LogError("VUID-vkCmdClearDepthStencilImage-aspectMask-02824", objlist, range_loc.dot(Field::aspectMask),
"is %s (can only be DEPTH_BIT or STENCIL_BIT).", string_VkImageAspectFlags(pRanges[i].aspectMask).c_str());
}
if ((pRanges[i].aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) {
if (vkuFormatHasDepth(image_format) == false) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |= LogError("VUID-vkCmdClearDepthStencilImage-image-02826", objlist, range_loc.dot(Field::aspectMask),
"has a VK_IMAGE_ASPECT_DEPTH_BIT but %s "
"doesn't have a depth component.",
string_VkFormat(image_format));
}
if ((image_state.createInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |= LogError(
"VUID-vkCmdClearDepthStencilImage-pRanges-02660", objlist, range_loc.dot(Field::aspectMask),
"includes VK_IMAGE_ASPECT_DEPTH_BIT, but the image was not created with VK_IMAGE_USAGE_TRANSFER_DST_BIT.");
}
}
if ((pRanges[i].aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) {
if (vkuFormatHasStencil(image_format) == false) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |= LogError("VUID-vkCmdClearDepthStencilImage-image-02825", objlist, range_loc.dot(Field::aspectMask),
"has a VK_IMAGE_ASPECT_STENCIL_BIT but "
"%s doesn't have a stencil component.",
string_VkFormat(image_format));
}
if (image_stencil_struct != nullptr) {
if ((image_stencil_struct->stencilUsage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |= LogError("VUID-vkCmdClearDepthStencilImage-pRanges-02658", objlist, range_loc.dot(Field::aspectMask),
"includes VK_IMAGE_ASPECT_STENCIL_BIT and "
"image was created with VkImageStencilUsageCreateInfo::stencilUsage = %s.",
string_VkImageUsageFlags(image_stencil_struct->stencilUsage).c_str());
}
} else if ((image_state.createInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |= LogError("VUID-vkCmdClearDepthStencilImage-pRanges-02659", objlist, range_loc.dot(Field::aspectMask),
"includes VK_IMAGE_ASPECT_STENCIL_BIT and "
"image was not created with VkImageStencilUsageCreateInfo, but was created with "
"VK_IMAGE_USAGE_TRANSFER_DST_BIT.");
}
}
}
if (!vkuFormatIsDepthOrStencil(image_format)) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |=
LogError("VUID-vkCmdClearDepthStencilImage-image-00014", objlist, image_loc,
"(%s) doesn't have a depth/stencil format (%s).", FormatHandle(image).c_str(), string_VkFormat(image_format));
}
if (VK_IMAGE_USAGE_TRANSFER_DST_BIT != (VK_IMAGE_USAGE_TRANSFER_DST_BIT & image_state.createInfo.usage)) {
LogObjectList objlist(cb_state.commandBuffer(), image);
skip |= LogError("VUID-vkCmdClearDepthStencilImage-pRanges-02659", objlist, image_loc,
"(%s) was not created with the "
"VK_IMAGE_USAGE_TRANSFER_DST_BIT set.",
FormatHandle(image).c_str());
}
return skip;
}
void CoreChecks::PreCallRecordCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount,
const VkImageSubresourceRange *pRanges) {
StateTracker::PreCallRecordCmdClearDepthStencilImage(commandBuffer, image, imageLayout, pDepthStencil, rangeCount, pRanges);
auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer);
auto image_state = Get<IMAGE_STATE>(image);
if (cb_state_ptr && image_state) {
for (uint32_t i = 0; i < rangeCount; ++i) {
cb_state_ptr->SetImageInitialLayout(image, pRanges[i], imageLayout);
}
}
}
// Returns true if sub_rect is entirely contained within rect
static inline bool ContainsRect(VkRect2D rect, VkRect2D sub_rect) {
if ((sub_rect.offset.x < rect.offset.x) || (sub_rect.offset.x + sub_rect.extent.width > rect.offset.x + rect.extent.width) ||
(sub_rect.offset.y < rect.offset.y) || (sub_rect.offset.y + sub_rect.extent.height > rect.offset.y + rect.extent.height)) {
return false;
}
return true;
}
static std::string string_VkRect2D(VkRect2D rect) {
std::stringstream ss;
ss << "offset.x: " << rect.offset.x << ", offset.y: " << rect.offset.y << ", extent.width: " << rect.extent.width
<< ", extent.height: " << rect.extent.height;
return ss.str();
}
bool CoreChecks::ValidateClearAttachmentExtent(const CMD_BUFFER_STATE &cb_state, const VkRect2D &render_area,
uint32_t render_pass_layer_count, uint32_t rect_count,
const VkClearRect *clear_rects, const Location &loc) const {
bool skip = false;
for (uint32_t i = 0; i < rect_count; i++) {
if (!ContainsRect(render_area, clear_rects[i].rect)) {
skip |=
LogError("VUID-vkCmdClearAttachments-pRects-00016", cb_state.Handle(), loc.dot(Field::pRects, i).dot(Field::rect),
"(%s) is not contained in the area of "
"the current render pass instance (%s).",
string_VkRect2D(clear_rects[i].rect).c_str(), string_VkRect2D(render_area).c_str());
}
const uint32_t rect_base_layer = clear_rects[i].baseArrayLayer;
const uint32_t rect_layer_count = clear_rects[i].layerCount;
// The layer indices specified by elements of pRects must be inferior to render pass layer count
if (rect_base_layer + rect_layer_count > render_pass_layer_count) {
skip |= LogError(
"VUID-vkCmdClearAttachments-pRects-06937", cb_state.Handle(), loc.dot(Field::pRects, i).dot(Field::baseArrayLayer),
"(%" PRIu32 ") + layerCount (%" PRIu32 ") (sum: %" PRIu32
"), is larger then the number of layers rendered to in the current render pass instance (%" PRIu32 ").",
rect_base_layer, rect_layer_count, rect_base_layer + rect_layer_count, render_pass_layer_count);
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
const VkClearAttachment *pAttachments, uint32_t rectCount,
const VkClearRect *pRects, const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state_ptr) {
return skip;
}
const CMD_BUFFER_STATE &cb_state = *cb_state_ptr;
skip |= ValidateCmd(cb_state, error_obj.location);
if (skip) return skip; // basic validation failed, might have null pointers
// Validate that attachments are in reference list of active subpass
const auto &render_area = (cb_state.activeRenderPass->use_dynamic_rendering)
? cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info.renderArea
: cb_state.active_render_pass_begin_info.renderArea;
if (cb_state.createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) {
uint32_t layer_count = 0;
if (cb_state.activeRenderPass->UsesDynamicRendering()) {
layer_count = cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info.layerCount;
} else {
layer_count = cb_state.activeFramebuffer.get()->createInfo.layers;
}
skip |= ValidateClearAttachmentExtent(cb_state, render_area, layer_count, rectCount, pRects, error_obj.location);
}
for (uint32_t attachment_index = 0; attachment_index < attachmentCount; attachment_index++) {
const Location &attachment_loc = error_obj.location.dot(Field::pAttachments, attachment_index);
auto clear_desc = &pAttachments[attachment_index];
const VkImageAspectFlags aspect_mask = clear_desc->aspectMask;
bool is_valid_color_attachment_index = false;
const IMAGE_VIEW_STATE *color_view_state = nullptr;
uint32_t color_attachment_count = 0;
const IMAGE_VIEW_STATE *depth_view_state = nullptr;
const IMAGE_VIEW_STATE *stencil_view_state = nullptr;
uint32_t view_mask = 0;
if (cb_state.activeRenderPass->UsesDynamicRendering()) {
is_valid_color_attachment_index = cb_state.IsValidDynamicColorAttachmentImageIndex(clear_desc->colorAttachment);
color_view_state = cb_state.GetActiveAttachmentImageViewState(
cb_state.GetDynamicColorAttachmentImageIndex(clear_desc->colorAttachment));
color_attachment_count = cb_state.GetDynamicColorAttachmentCount();
depth_view_state = cb_state.GetActiveAttachmentImageViewState(cb_state.GetDynamicDepthAttachmentImageIndex());
stencil_view_state = cb_state.GetActiveAttachmentImageViewState(cb_state.GetDynamicStencilAttachmentImageIndex());
view_mask = cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info.viewMask;
} else {
const auto *renderpass_create_info = cb_state.activeRenderPass->createInfo.ptr();
const auto *subpass_desc = &renderpass_create_info->pSubpasses[cb_state.GetActiveSubpass()];
const auto *framebuffer = cb_state.activeFramebuffer.get();
is_valid_color_attachment_index = (clear_desc->colorAttachment == VK_ATTACHMENT_UNUSED);
if (subpass_desc) {
is_valid_color_attachment_index |= clear_desc->colorAttachment < subpass_desc->colorAttachmentCount;
if (framebuffer && (clear_desc->colorAttachment != VK_ATTACHMENT_UNUSED) &&
(clear_desc->colorAttachment < subpass_desc->colorAttachmentCount)) {
if (subpass_desc->pColorAttachments[clear_desc->colorAttachment].attachment <
framebuffer->createInfo.attachmentCount) {
color_view_state = cb_state.GetActiveAttachmentImageViewState(
subpass_desc->pColorAttachments[clear_desc->colorAttachment].attachment);
}
}
color_attachment_count = subpass_desc->colorAttachmentCount;
if (framebuffer && subpass_desc->pDepthStencilAttachment &&
(subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)) {
depth_view_state =
cb_state.GetActiveAttachmentImageViewState(subpass_desc->pDepthStencilAttachment->attachment);
stencil_view_state = depth_view_state;
const VkFormat image_view_format = depth_view_state->safe_create_info.format;
if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) && !vkuFormatHasDepth(image_view_format)) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass(),
depth_view_state->image_view());
skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07884", objlist, attachment_loc,
"in pSubpasses[%" PRIu32
"] has VK_IMAGE_ASPECT_DEPTH_BIT and is backed by an image view with format (%s).",
cb_state.GetActiveSubpass(), string_VkFormat(image_view_format));
}
if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) && !vkuFormatHasStencil(image_view_format)) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass(),
stencil_view_state->image_view());
skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07885", objlist, attachment_loc,
"in pSubpasses[%" PRIu32
"] has VK_IMAGE_ASPECT_STENCIL_BIT and is backed by an image view with format (%s).",
cb_state.GetActiveSubpass(), string_VkFormat(image_view_format));
}
}
view_mask = subpass_desc->viewMask;
}
}
if (aspect_mask & VK_IMAGE_ASPECT_METADATA_BIT) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-VkClearAttachment-aspectMask-00020", objlist, attachment_loc.dot(Field::aspectMask), "is %s.",
string_VkImageAspectFlags(aspect_mask).c_str());
} else 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)) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-VkClearAttachment-aspectMask-02246", objlist, attachment_loc.dot(Field::aspectMask), "is %s.",
string_VkImageAspectFlags(aspect_mask).c_str());
} else if (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) {
if (!is_valid_color_attachment_index) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07271", objlist, attachment_loc.dot(Field::colorAttachment),
"(%" PRIu32 " is larger than colorAttachmentCount (%" PRIu32 ").", clear_desc->colorAttachment,
color_attachment_count);
}
if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) || (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT)) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-VkClearAttachment-aspectMask-00019", objlist, attachment_loc.dot(Field::aspectMask),
"is %s.", string_VkImageAspectFlags(aspect_mask).c_str());
}
} else if (aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
skip |= ValidateClearDepthStencilValue(commandBuffer, clear_desc->clearValue.depthStencil,
attachment_loc.dot(Field::clearValue).dot(Field::depthStencil));
}
std::array<const IMAGE_VIEW_STATE *, 3> image_views = {nullptr, nullptr, nullptr};
if (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) {
image_views[0] = color_view_state;
}
if (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) {
image_views[1] = depth_view_state;
}
if (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) {
image_views[2] = stencil_view_state;
}
if (image_views[1] == image_views[2]) {
image_views[2] = nullptr;
}
for (auto image_view : image_views) {
if (image_view) {
skip |= ValidateProtectedImage(cb_state, *image_view->image_state, error_obj.location,
"VUID-vkCmdClearAttachments-commandBuffer-02504");
skip |= ValidateUnprotectedImage(cb_state, *image_view->image_state, error_obj.location,
"VUID-vkCmdClearAttachments-commandBuffer-02505");
}
}
// With a non-zero view mask, multiview functionality is considered to be enabled
if (view_mask > 0) {
for (uint32_t i = 0; i < rectCount; ++i) {
if (pRects[i].baseArrayLayer != 0 || pRects[i].layerCount != 1) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdClearAttachments-baseArrayLayer-00018", objlist,
error_obj.location.dot(Field::pRects, i).dot(Field::baseArrayLayer),
"is %" PRIu32 " and layerCount is %" PRIu32 ", but the render pass instance uses multiview.",
pRects[i].baseArrayLayer, pRects[i].layerCount);
}
}
}
}
return skip;
}
void CoreChecks::PreCallRecordCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
const VkClearAttachment *pAttachments, uint32_t rectCount,
const VkClearRect *pRects) {
auto cb_state_ptr = GetWrite<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state_ptr) {
return;
}
const CMD_BUFFER_STATE &cb_state = *cb_state_ptr;
if (!cb_state.activeRenderPass || (cb_state.createInfo.level != VK_COMMAND_BUFFER_LEVEL_SECONDARY)) {
return;
}
std::shared_ptr<std::vector<VkClearRect>> clear_rect_copy;
if (cb_state.activeRenderPass->use_dynamic_rendering_inherited) {
for (uint32_t attachment_index = 0; attachment_index < attachmentCount; attachment_index++) {
const auto clear_desc = &pAttachments[attachment_index];
auto colorAttachmentCount = cb_state.activeRenderPass->inheritance_rendering_info.colorAttachmentCount;
int image_index = -1;
if ((clear_desc->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) && (clear_desc->colorAttachment < colorAttachmentCount)) {
image_index = cb_state.GetDynamicColorAttachmentImageIndex(clear_desc->colorAttachment);
} else if (clear_desc->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT)) {
image_index = cb_state.GetDynamicDepthAttachmentImageIndex();
} else if (clear_desc->aspectMask & (VK_IMAGE_ASPECT_STENCIL_BIT)) {
image_index = cb_state.GetDynamicStencilAttachmentImageIndex();
}
if (image_index != -1) {
if (!clear_rect_copy) {
// We need a copy of the clear rectangles that will persist until the last lambda executes
// but we want to create it as lazily as possible
clear_rect_copy.reset(new std::vector<VkClearRect>(pRects, pRects + rectCount));
}
// if a secondary level command buffer inherits the framebuffer from the primary command buffer
// (see VkCommandBufferInheritanceInfo), this validation must be deferred until queue submit time
auto val_fn = [this, rectCount, clear_rect_copy](const CMD_BUFFER_STATE &secondary, const CMD_BUFFER_STATE *prim_cb,
const FRAMEBUFFER_STATE *) {
assert(rectCount == clear_rect_copy->size());
bool skip = false;
const Location loc(Func::vkCmdClearAttachments);
skip = ValidateClearAttachmentExtent(
secondary, prim_cb->activeRenderPass->dynamic_rendering_begin_rendering_info.renderArea,
prim_cb->activeRenderPass->dynamic_rendering_begin_rendering_info.layerCount, rectCount,
clear_rect_copy->data(), loc);
return skip;
};
cb_state_ptr->cmd_execute_commands_functions.emplace_back(val_fn);
}
}
} else if (cb_state.activeRenderPass->use_dynamic_rendering == false) {
const VkRenderPassCreateInfo2 *renderpass_create_info = cb_state.activeRenderPass->createInfo.ptr();
const VkSubpassDescription2 *subpass_desc = &renderpass_create_info->pSubpasses[cb_state.GetActiveSubpass()];
for (uint32_t attachment_index = 0; attachment_index < attachmentCount; attachment_index++) {
const auto clear_desc = &pAttachments[attachment_index];
uint32_t fb_attachment = VK_ATTACHMENT_UNUSED;
if ((clear_desc->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) &&
(clear_desc->colorAttachment < subpass_desc->colorAttachmentCount)) {
fb_attachment = subpass_desc->pColorAttachments[clear_desc->colorAttachment].attachment;
} else if ((clear_desc->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) &&
subpass_desc->pDepthStencilAttachment) {
fb_attachment = subpass_desc->pDepthStencilAttachment->attachment;
}
if (fb_attachment != VK_ATTACHMENT_UNUSED) {
if (!clear_rect_copy) {
// We need a copy of the clear rectangles that will persist until the last lambda executes
// but we want to create it as lazily as possible
clear_rect_copy.reset(new std::vector<VkClearRect>(pRects, pRects + rectCount));
}
// if a secondary level command buffer inherits the framebuffer from the primary command buffer
// (see VkCommandBufferInheritanceInfo), this validation must be deferred until queue submit time
auto val_fn = [this, rectCount, clear_rect_copy](const CMD_BUFFER_STATE &secondary, const CMD_BUFFER_STATE *prim_cb,
const FRAMEBUFFER_STATE *fb) {
assert(rectCount == clear_rect_copy->size());
const auto &render_area = prim_cb->active_render_pass_begin_info.renderArea;
bool skip = false;
if (fb && prim_cb->IsPrimary()) {
const Location loc(Func::vkCmdClearAttachments);
skip = ValidateClearAttachmentExtent(secondary, render_area, fb->createInfo.layers, rectCount,
clear_rect_copy->data(), loc);
}
return skip;
};
cb_state_ptr->cmd_execute_commands_functions.emplace_back(val_fn);
}
}
}
}
// Helper function to validate usage flags for images
bool CoreChecks::ValidateImageUsageFlags(VkCommandBuffer cb, IMAGE_STATE const &image_state, VkImageUsageFlags desired, bool strict,
const char *vuid, const Location &image_loc) const {
bool skip = false;
LogObjectList objlist(cb, image_state.Handle());
bool correct_usage = false;
if (strict) {
correct_usage = ((image_state.createInfo.usage & desired) == desired);
} else {
correct_usage = ((image_state.createInfo.usage & desired) != 0);
}
if (!correct_usage) {
skip = LogError(vuid, objlist, image_loc, "(%s) was created with %s but requires %s.",
FormatHandle(image_state.Handle()).c_str(), string_VkImageUsageFlags(image_state.createInfo.usage).c_str(),
string_VkImageUsageFlags(desired).c_str());
}
return skip;
}
bool CoreChecks::ValidateImageFormatFeatureFlags(VkCommandBuffer cb, IMAGE_STATE const &image_state,
VkFormatFeatureFlags2KHR desired, const Location &image_loc,
const char *vuid) const {
bool skip = false;
const VkFormatFeatureFlags2KHR image_format_features = image_state.format_features;
if ((image_format_features & desired) != desired) {
const LogObjectList objlist(cb, image_state.Handle());
// Same error, but more details if it was an AHB external format
if (image_state.HasAHBFormat()) {
skip |= LogError(
vuid, objlist, image_loc,
"(%s) was created with an external format having VkFormatFeatureFlags2 (%s) which is missing the required feature "
"%s (Features found in VkAndroidHardwareBufferFormatPropertiesANDROID::formatFeatures).",
FormatHandle(image_state).c_str(), string_VkFormatFeatureFlags2(image_format_features).c_str(),
string_VkFormatFeatureFlags2(desired).c_str());
} else {
skip |= LogError(vuid, objlist, image_loc,
"(%s) was created with format %s and tiling %s which have VkFormatFeatureFlags2 (%s) which in turn is "
"missing the required feature %s.",
FormatHandle(image_state).c_str(), string_VkFormat(image_state.createInfo.format),
string_VkImageTiling(image_state.createInfo.tiling),
string_VkFormatFeatureFlags2(image_format_features).c_str(),
string_VkFormatFeatureFlags2(desired).c_str());
}
}
return skip;
}
// For the given format verify that the aspect masks make sense
bool CoreChecks::ValidateImageAspectMask(VkImage image, VkFormat format, VkImageAspectFlags aspect_mask, bool is_image_disjoint,
const Location &loc, const char *vuid) const {
bool skip = false;
// checks color format and (single-plane or non-disjoint)
// if ycbcr extension is not supported then single-plane and non-disjoint are always both true
if ((vkuFormatIsColor(format)) && ((vkuFormatIsMultiplane(format) == false) || (is_image_disjoint == false))) {
if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != VK_IMAGE_ASPECT_COLOR_BIT) {
skip |= LogError(
vuid, image, loc,
"Using format (%s) with aspect flags (%s) but color image formats must have the VK_IMAGE_ASPECT_COLOR_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
} else if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != aspect_mask) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but color image formats must have ONLY the "
"VK_IMAGE_ASPECT_COLOR_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
}
} else if (vkuFormatIsDepthAndStencil(format)) {
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) == 0) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but depth/stencil image formats must have at least one "
"of VK_IMAGE_ASPECT_DEPTH_BIT and VK_IMAGE_ASPECT_STENCIL_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
} else if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != aspect_mask) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but combination depth/stencil image formats can have "
"only the VK_IMAGE_ASPECT_DEPTH_BIT and VK_IMAGE_ASPECT_STENCIL_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
}
} else if (vkuFormatIsDepthOnly(format)) {
if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != VK_IMAGE_ASPECT_DEPTH_BIT) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but depth-only image formats must have the "
"VK_IMAGE_ASPECT_DEPTH_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
} else if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != aspect_mask) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but depth-only image formats can have only the "
"VK_IMAGE_ASPECT_DEPTH_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
}
} else if (vkuFormatIsStencilOnly(format)) {
if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != VK_IMAGE_ASPECT_STENCIL_BIT) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but stencil-only image formats must have the "
"VK_IMAGE_ASPECT_STENCIL_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
} else if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != aspect_mask) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but stencil-only image formats can have only the "
"VK_IMAGE_ASPECT_STENCIL_BIT set.",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
}
} else if (vkuFormatIsMultiplane(format)) {
VkImageAspectFlags valid_flags = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT;
if (3 == vkuFormatPlaneCount(format)) {
valid_flags = valid_flags | VK_IMAGE_ASPECT_PLANE_2_BIT;
}
if ((aspect_mask & valid_flags) != aspect_mask) {
skip |= LogError(vuid, image, loc,
"Using format (%s) with aspect flags (%s) but multi-plane image formats may have only "
"VK_IMAGE_ASPECT_COLOR_BIT or VK_IMAGE_ASPECT_PLANE_n_BITs set, where n = [0, 1, 2].",
string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
}
}
return skip;
}
bool CoreChecks::ValidateImageSubresourceRange(const uint32_t image_mip_count, const uint32_t image_layer_count,
const VkImageSubresourceRange &subresourceRange,
const char *image_layer_count_var_name, const VkImage image,
const SubresourceRangeErrorCodes &errorCodes,
const Location &subresource_loc) const {
bool skip = false;
// Validate mip levels
if (subresourceRange.baseMipLevel >= image_mip_count) {
skip |= LogError(errorCodes.base_mip_err, image, subresource_loc.dot(Field::baseMipLevel),
"(%" PRIu32 ") is greater or equal to the mip level count of the image (%" PRIu32 ").",
subresourceRange.baseMipLevel, image_mip_count);
}
if (subresourceRange.levelCount != VK_REMAINING_MIP_LEVELS) {
if (subresourceRange.levelCount == 0) {
skip |= LogError("VUID-VkImageSubresourceRange-levelCount-01720", image, subresource_loc.dot(Field::levelCount),
"is zero.");
} else {
const uint64_t necessary_mip_count = uint64_t{subresourceRange.baseMipLevel} + uint64_t{subresourceRange.levelCount};
if (necessary_mip_count > image_mip_count) {
skip |= LogError(errorCodes.mip_count_err, image, subresource_loc.dot(Field::baseMipLevel),
"(%" PRIu32 ") + levelCount (%" PRIu32 ") is (%" PRIu64
") which is greater than the mip level count of the image (i.e. greater than %" PRIu32 ").",
subresourceRange.baseMipLevel, subresourceRange.levelCount, necessary_mip_count, image_mip_count);
}
}
}
// Validate array layers
if (subresourceRange.baseArrayLayer >= image_layer_count) {
skip |= LogError(errorCodes.base_layer_err, image, subresource_loc.dot(Field::baseArrayLayer),
"(%" PRIu32 ") is greater or equal to the %s of the image when it was created (%" PRIu32 ").",
subresourceRange.baseArrayLayer, image_layer_count_var_name, image_layer_count);
}
if (subresourceRange.layerCount != VK_REMAINING_ARRAY_LAYERS) {
if (subresourceRange.layerCount == 0) {
skip |= LogError("VUID-VkImageSubresourceRange-layerCount-01721", image, subresource_loc.dot(Field::layerCount),
"is zero.");
} else {
const uint64_t necessary_layer_count =
uint64_t{subresourceRange.baseArrayLayer} + uint64_t{subresourceRange.layerCount};
if (necessary_layer_count > image_layer_count) {
skip |= LogError(errorCodes.layer_count_err, image, subresource_loc.dot(Field::baseArrayLayer),
"(%" PRIu32 ") + layerCount (%" PRIu32 ") is (%" PRIu64
") which is greater than the %s of the image when it was created (%" PRIu32 ").",
subresourceRange.baseArrayLayer, subresourceRange.layerCount, necessary_layer_count,
image_layer_count_var_name, image_layer_count);
}
}
}
if (subresourceRange.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
if (subresourceRange.aspectMask &
(VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT)) {
skip |= LogError("VUID-VkImageSubresourceRange-aspectMask-01670", image, subresource_loc.dot(Field::aspectMask),
"is %s.", string_VkImageAspectFlags(subresourceRange.aspectMask).c_str());
}
}
// aspectMask must not contain VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT
if (subresourceRange.aspectMask & (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-VkImageSubresourceRange-aspectMask-02278", image, subresource_loc.dot(Field::aspectMask), "is %s.",
string_VkImageAspectFlags(subresourceRange.aspectMask).c_str());
}
return skip;
}
bool CoreChecks::ValidateCreateImageViewSubresourceRange(const IMAGE_STATE &image_state, bool is_imageview_2d_type,
const VkImageSubresourceRange &subresourceRange,
const Location &loc) const {
const bool is_khr_maintenance1 = IsExtEnabled(device_extensions.vk_khr_maintenance1);
const bool is_2d_compatible =
image_state.createInfo.flags & (VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT | VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT);
const bool is_image_slicable = (image_state.createInfo.imageType == VK_IMAGE_TYPE_3D) && is_2d_compatible;
const bool is_3_d_to_2_d_map = is_khr_maintenance1 && is_image_slicable && is_imageview_2d_type;
uint32_t image_layer_count;
if (is_3_d_to_2_d_map) {
const auto layers = LayersFromRange(subresourceRange);
const auto extent = image_state.GetEffectiveSubresourceExtent(layers);
image_layer_count = extent.depth;
} else {
image_layer_count = image_state.createInfo.arrayLayers;
}
const auto image_layer_count_var_name = is_3_d_to_2_d_map ? "extent.depth" : "arrayLayers";
SubresourceRangeErrorCodes subresource_range_error_codes = {};
subresource_range_error_codes.base_mip_err = "VUID-VkImageViewCreateInfo-subresourceRange-01478";
subresource_range_error_codes.mip_count_err = "VUID-VkImageViewCreateInfo-subresourceRange-01718";
subresource_range_error_codes.base_layer_err =
is_3_d_to_2_d_map ? "VUID-VkImageViewCreateInfo-image-02724" : "VUID-VkImageViewCreateInfo-image-06724";
subresource_range_error_codes.layer_count_err = is_3_d_to_2_d_map ? "VUID-VkImageViewCreateInfo-subresourceRange-02725"
: "VUID-VkImageViewCreateInfo-subresourceRange-06725";
return ValidateImageSubresourceRange(image_state.createInfo.mipLevels, image_layer_count, subresourceRange,
image_layer_count_var_name, image_state.image(), subresource_range_error_codes,
loc.dot(Field::subresourceRange));
}
bool CoreChecks::ValidateCmdClearColorSubresourceRange(const IMAGE_STATE &image_state,
const VkImageSubresourceRange &subresourceRange, const Location &loc) const {
SubresourceRangeErrorCodes subresource_range_error_codes = {};
subresource_range_error_codes.base_mip_err = "VUID-vkCmdClearColorImage-baseMipLevel-01470";
subresource_range_error_codes.mip_count_err = "VUID-vkCmdClearColorImage-pRanges-01692";
subresource_range_error_codes.base_layer_err = "VUID-vkCmdClearColorImage-baseArrayLayer-01472";
subresource_range_error_codes.layer_count_err = "VUID-vkCmdClearColorImage-pRanges-01693";
return ValidateImageSubresourceRange(image_state.createInfo.mipLevels, image_state.createInfo.arrayLayers, subresourceRange,
"arrayLayers", image_state.image(), subresource_range_error_codes,
loc.dot(Field::subresourceRange));
}
bool CoreChecks::ValidateCmdClearDepthSubresourceRange(const IMAGE_STATE &image_state,
const VkImageSubresourceRange &subresourceRange, const Location &loc) const {
SubresourceRangeErrorCodes subresource_range_error_codes = {};
subresource_range_error_codes.base_mip_err = "VUID-vkCmdClearDepthStencilImage-baseMipLevel-01474";
subresource_range_error_codes.mip_count_err = "VUID-vkCmdClearDepthStencilImage-pRanges-01694";
subresource_range_error_codes.base_layer_err = "VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476";
subresource_range_error_codes.layer_count_err = "VUID-vkCmdClearDepthStencilImage-pRanges-01695";
return ValidateImageSubresourceRange(image_state.createInfo.mipLevels, image_state.createInfo.arrayLayers, subresourceRange,
"arrayLayers", image_state.image(), subresource_range_error_codes,
loc.dot(Field::subresourceRange));
}
bool CoreChecks::ValidateImageBarrierSubresourceRange(const Location &loc, const IMAGE_STATE &image_state,
const VkImageSubresourceRange &subresourceRange) const {
return ValidateImageSubresourceRange(image_state.createInfo.mipLevels, image_state.createInfo.arrayLayers, subresourceRange,
"arrayLayers", image_state.image(), sync_vuid_maps::GetSubResourceVUIDs(loc),
loc.dot(Field::subresourceRange));
}
bool CoreChecks::ValidateImageViewFormatFeatures(const IMAGE_STATE &image_state, const VkFormat view_format,
const VkImageUsageFlags image_usage, const Location &create_info_loc) const {
// Pass in image_usage here instead of extracting it from image_state in case there's a chained VkImageViewUsageCreateInfo
bool skip = false;
VkFormatFeatureFlags2KHR tiling_features = 0;
const VkImageTiling image_tiling = image_state.createInfo.tiling;
if (image_state.HasAHBFormat()) {
// AHB image view and image share same feature sets
tiling_features = image_state.format_features;
} else if (image_tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
// Parameter validation should catch if this is used without VK_EXT_image_drm_format_modifier
assert(IsExtEnabled(device_extensions.vk_ext_image_drm_format_modifier));
VkImageDrmFormatModifierPropertiesEXT drm_format_properties = vku::InitStructHelper();
DispatchGetImageDrmFormatModifierPropertiesEXT(device, image_state.image(), &drm_format_properties);
VkDrmFormatModifierPropertiesListEXT fmt_drm_props = vku::InitStructHelper();
VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
DispatchGetPhysicalDeviceFormatProperties2(physical_device, view_format, &fmt_props_2);
std::vector<VkDrmFormatModifierPropertiesEXT> drm_properties;
drm_properties.resize(fmt_drm_props.drmFormatModifierCount);
fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();
DispatchGetPhysicalDeviceFormatProperties2(physical_device, view_format, &fmt_props_2);
for (uint32_t i = 0; i < fmt_drm_props.drmFormatModifierCount; i++) {
if (fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifier == drm_format_properties.drmFormatModifier) {
tiling_features = fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifierTilingFeatures;
break;
}
}
} else {
VkFormatProperties3KHR format_properties = GetPDFormatProperties(view_format);
tiling_features = (image_tiling == VK_IMAGE_TILING_LINEAR) ? format_properties.linearTilingFeatures
: format_properties.optimalTilingFeatures;
}
if (tiling_features == 0) {
skip |= LogError("VUID-VkImageViewCreateInfo-None-02273", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s has no supported format features on this "
"physical device.",
string_VkFormat(view_format), string_VkImageTiling(image_tiling));
} else if ((image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(tiling_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-02274", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s only supports %s.", string_VkFormat(view_format), string_VkImageTiling(image_tiling),
string_VkFormatFeatureFlags2(tiling_features).c_str());
} else if ((image_usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(tiling_features & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-02275", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s only supports %s.", string_VkFormat(view_format), string_VkImageTiling(image_tiling),
string_VkFormatFeatureFlags2(tiling_features).c_str());
} else if ((image_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) &&
!(tiling_features & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV))) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-08931", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s only supports %s.", string_VkFormat(view_format), string_VkImageTiling(image_tiling),
string_VkFormatFeatureFlags2(tiling_features).c_str());
} else if ((image_usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) &&
!(tiling_features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-02277", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s only supports %s.", string_VkFormat(view_format), string_VkImageTiling(image_tiling),
string_VkFormatFeatureFlags2(tiling_features).c_str());
} else if ((image_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) &&
!(tiling_features & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV))) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-08932", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s only supports %s.", string_VkFormat(view_format), string_VkImageTiling(image_tiling),
string_VkFormatFeatureFlags2(tiling_features).c_str());
} else if ((image_usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR) &&
!(tiling_features & VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) {
if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-04550", image_state.image(), create_info_loc.dot(Field::format),
"%s with tiling %s only supports %s.", string_VkFormat(view_format),
string_VkImageTiling(image_tiling), string_VkFormatFeatureFlags2(tiling_features).c_str());
}
}
return skip;
}
// Returns whether two formats have identical components (compares the size and type of each component)
// EX. (R8G8B8A8, B8G8R8A8) -> true
bool FormatsEqualComponentBits(VkFormat format_a, VkFormat format_b) {
const VKU_FORMAT_INFO format_info_a = vkuGetFormatInfo(format_a);
const VKU_FORMAT_INFO format_info_b = vkuGetFormatInfo(format_b);
if (format_info_a.compatibility == VKU_FORMAT_COMPATIBILITY_CLASS_NONE || format_info_b.compatibility == VKU_FORMAT_COMPATIBILITY_CLASS_NONE) {
return false;
} else if (format_info_a.component_count != format_info_b.component_count) {
return false;
}
// Need to loop match each component type is found in both formats
// formats are maxed at 4 components, so the double loop is not going to scale
for (uint32_t i = 0; i < format_info_a.component_count; i++) {
const VKU_FORMAT_COMPONENT_INFO component_a = format_info_a.components[i];
bool component_match = false;
for (uint32_t j = 0; j < format_info_b.component_count; j++) {
const VKU_FORMAT_COMPONENT_INFO component_b = format_info_b.components[j];
if ((component_a.type == component_b.type) && (component_a.size == component_b.size)) {
component_match = true;
break;
}
}
if (!component_match) {
return false;
}
}
return true;
}
bool CoreChecks::PreCallValidateCreateImageView(VkDevice device, const VkImageViewCreateInfo *pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks *pAllocator,
[[maybe_unused]] VkImageView *pView, const ErrorObject &error_obj) const {
bool skip = false;
auto image_state_ptr = Get<IMAGE_STATE>(pCreateInfo->image);
if (!image_state_ptr) {
return skip;
}
const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo);
const auto &image_state = *image_state_ptr;
const VkImageUsageFlags valid_usage_flags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR |
VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT |
VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR |
VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM | VK_IMAGE_USAGE_SAMPLE_BLOCK_MATCH_BIT_QCOM;
skip |= ValidateImageUsageFlags(VK_NULL_HANDLE, image_state, valid_usage_flags, false, "VUID-VkImageViewCreateInfo-image-04441",
create_info_loc.dot(Field::image));
// If this isn't a sparse image, it needs to have memory backing it at CreateImageView time
skip |= ValidateMemoryIsBoundToImage(LogObjectList(device, pCreateInfo->image), image_state, create_info_loc.dot(Field::image),
"VUID-VkImageViewCreateInfo-image-01020");
// Checks imported from image layer
skip |= ValidateCreateImageViewSubresourceRange(
image_state, pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_2D || pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY,
pCreateInfo->subresourceRange, create_info_loc);
const auto normalized_subresource_range = image_state.NormalizeSubresourceRange(pCreateInfo->subresourceRange);
const VkImageCreateFlags image_flags = image_state.createInfo.flags;
const VkFormat image_format = image_state.createInfo.format;
const VkFormat view_format = pCreateInfo->format;
const VkImageAspectFlags aspect_mask = pCreateInfo->subresourceRange.aspectMask;
const VkImageType image_type = image_state.createInfo.imageType;
const VkImageViewType view_type = pCreateInfo->viewType;
const uint32_t layer_count = pCreateInfo->subresourceRange.layerCount;
// If there's a chained VkImageViewUsageCreateInfo struct, modify image_usage to match
VkImageUsageFlags image_usage = image_state.createInfo.usage;
if (const auto chained_ivuci_struct = vku::FindStructInPNextChain<VkImageViewUsageCreateInfo>(pCreateInfo->pNext); chained_ivuci_struct) {
if (IsExtEnabled(device_extensions.vk_khr_maintenance2)) {
const auto image_stencil_struct = vku::FindStructInPNextChain<VkImageStencilUsageCreateInfo>(image_state.createInfo.pNext);
if (image_stencil_struct == nullptr) {
if ((image_usage | chained_ivuci_struct->usage) != image_usage) {
skip |=
LogError("VUID-VkImageViewCreateInfo-pNext-02662", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewUsageCreateInfo, Field::usage),
"(%s) must not include any bits that were not set in VkImageCreateInfo::usage (%s) of the image.",
string_VkImageUsageFlags(chained_ivuci_struct->usage).c_str(),
string_VkImageUsageFlags(image_usage).c_str());
}
} else {
if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) == VK_IMAGE_ASPECT_STENCIL_BIT &&
(image_stencil_struct->stencilUsage | chained_ivuci_struct->usage) != image_stencil_struct->stencilUsage) {
skip |=
LogError("VUID-VkImageViewCreateInfo-pNext-02663", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewUsageCreateInfo, Field::usage),
"(%s) must not include any bits that were not set in VkImageStencilUsageCreateInfo::stencilUsage "
"(%s) if subResourceRange.aspectMask includes VK_IMAGE_ASPECT_STENCIL_BIT.",
string_VkImageUsageFlags(chained_ivuci_struct->usage).c_str(),
string_VkImageUsageFlags(image_stencil_struct->stencilUsage).c_str());
}
if ((aspect_mask & ~VK_IMAGE_ASPECT_STENCIL_BIT) != 0 &&
(image_usage | chained_ivuci_struct->usage) != image_usage) {
skip |=
LogError("VUID-VkImageViewCreateInfo-pNext-02664", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewUsageCreateInfo, Field::usage),
"(%s) must not include any bits that were not set in VkImageCreateInfo::usage (%s) of the image "
"if subResourceRange.aspectMask (%s) includes bits other than VK_IMAGE_ASPECT_STENCIL_BIT.",
string_VkImageUsageFlags(chained_ivuci_struct->usage).c_str(),
string_VkImageUsageFlags(image_usage).c_str(), string_VkImageAspectFlags(aspect_mask).c_str());
}
}
}
image_usage = chained_ivuci_struct->usage;
}
if (const auto sliced_create_info_ext = vku::FindStructInPNextChain<VkImageViewSlicedCreateInfoEXT>(pCreateInfo->pNext);
sliced_create_info_ext) {
const bool feature_disabled = (enabled_features.sliced_3d_features.imageSlicedViewOf3D == VK_FALSE);
if (feature_disabled) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-None-07871", pCreateInfo->image, create_info_loc,
"imageSlicedViewOf3D is not enabled.");
}
if (image_type != VK_IMAGE_TYPE_3D) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-image-07869", pCreateInfo->image,
create_info_loc.dot(Field::image), "was created with imageType (%s).", string_VkImageType(image_type));
}
if (view_type != VK_IMAGE_VIEW_TYPE_3D) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-viewType-07909", pCreateInfo->image,
create_info_loc.dot(Field::viewType), "is (%s).", string_VkImageViewType(view_type));
}
const uint32_t effective_mip_levels = ResolveRemainingLevels(image_state.createInfo, pCreateInfo->subresourceRange);
if (effective_mip_levels != 1) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-None-07870", pCreateInfo->image, create_info_loc,
"Image view references %" PRIu32 " mip levels.", effective_mip_levels);
}
const uint32_t effective_view_depth = image_state.GetEffectiveSubresourceExtent(pCreateInfo->subresourceRange).depth;
const uint32_t slice_offset = sliced_create_info_ext->sliceOffset;
const uint32_t slice_count = sliced_create_info_ext->sliceCount;
if (slice_offset >= effective_view_depth) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-sliceOffset-07867", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewSlicedCreateInfoEXT, Field::sliceOffset),
"(%" PRIu32 ") must be less than the effective view depth (%" PRIu32 ").", slice_offset,
effective_view_depth);
}
if (slice_count != VK_REMAINING_3D_SLICES_EXT) {
if (slice_count == 0) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-sliceCount-07868", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewSlicedCreateInfoEXT, Field::sliceCount), "is 0.");
}
if ((slice_offset + slice_count) > effective_view_depth) {
skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-sliceCount-07868", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewSlicedCreateInfoEXT, Field::sliceOffset),
"(%" PRIu32 ") + sliceCount (%" PRIu32 ") greater than effective view depth (%" PRIu32 ").",
slice_offset, slice_count, effective_view_depth);
}
}
}
// If image used VkImageFormatListCreateInfo need to make sure a format from list is used
if (const auto format_list_info = vku::FindStructInPNextChain<VkImageFormatListCreateInfo>(image_state.createInfo.pNext);
format_list_info && (format_list_info->viewFormatCount > 0)) {
bool found_format = false;
for (uint32_t i = 0; i < format_list_info->viewFormatCount; i++) {
if (format_list_info->pViewFormats[i] == view_format) {
found_format = true;
break;
}
}
if (found_format == false) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-01585", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageFormatListCreateInfo, Field::pViewFormats),
"has no formats that match the VkImageViewCreateInfo::format (%s).", string_VkFormat(view_format));
}
}
const bool multiplane_image = vkuFormatIsMultiplane(image_format);
if (multiplane_image && IsMultiplePlaneAspect(aspect_mask)) {
skip |= LogError("VUID-VkImageViewCreateInfo-subresourceRange-07818", pCreateInfo->image,
create_info_loc.dot(Field::subresourceRange).dot(Field::aspectMask), "(%s) is invalid for %s.",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
}
// Validate VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT state, if view/image formats differ
if ((image_flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) && (image_format != view_format)) {
const auto view_class = vkuFormatCompatibilityClass(view_format);
if (multiplane_image) {
const VkFormat compat_format = vkuFindMultiplaneCompatibleFormat(image_format, static_cast<VkImageAspectFlagBits>(aspect_mask));
const auto image_class = vkuFormatCompatibilityClass(compat_format);
// Need valid aspect mask otherwise will throw extra error when getting compatible format
// Also this can be VK_IMAGE_ASPECT_COLOR_BIT
const bool has_valid_aspect = IsOnlyOneValidPlaneAspect(image_format, aspect_mask);
if (has_valid_aspect && ((image_class != view_class) || (image_class == VKU_FORMAT_COMPATIBILITY_CLASS_NONE))) {
// Need to only check if one is NONE to handle edge case both are NONE
// View format must match the multiplane compatible format
skip |= LogError("VUID-VkImageViewCreateInfo-image-01586", pCreateInfo->image, create_info_loc.dot(Field::format),
"(%s) is not compatible with plane %" PRIu32 " of the %s format %s, must be compatible with %s.",
string_VkFormat(view_format), vkuGetPlaneIndex(static_cast<VkImageAspectFlagBits>(aspect_mask)), FormatHandle(pCreateInfo->image).c_str(),
string_VkFormat(image_format), string_VkFormat(compat_format));
}
} else if (!(image_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT)) {
// Format MUST be compatible (in the same format compatibility class) as the format the image was created with
const auto image_class = vkuFormatCompatibilityClass(image_format);
// Need to only check if one is NONE to handle edge case both are NONE
if ((image_class != view_class) || (image_class == VKU_FORMAT_COMPATIBILITY_CLASS_NONE)) {
skip |=
LogError("VUID-VkImageViewCreateInfo-image-01761", pCreateInfo->image, create_info_loc.dot(Field::format),
"%s is not in the same format compatibility class as %s format %s. Images created with the "
"VK_IMAGE_CREATE_MUTABLE_FORMAT BIT can support ImageViews with differing formats but they must be in "
"the same compatibility class.",
string_VkFormat(view_format), FormatHandle(pCreateInfo->image).c_str(), string_VkFormat(image_format));
}
}
} else {
// Format MUST be IDENTICAL to the format the image was created with
// Unless it is a multi-planar color bit aspect
if ((image_format != view_format) && (!multiplane_image || (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT))) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-01762", pCreateInfo->image, create_info_loc.dot(Field::format),
"%s is different from %s format (%s). Formats MUST be IDENTICAL unless VK_IMAGE_CREATE_MUTABLE_FORMAT "
"BIT was set on image creation.",
string_VkFormat(view_format), FormatHandle(pCreateInfo->image).c_str(), string_VkFormat(image_format));
}
}
if (image_state.createInfo.samples != VK_SAMPLE_COUNT_1_BIT && view_type != VK_IMAGE_VIEW_TYPE_2D &&
view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-04972", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with sample count %s, but pCreateInfo->viewType is %s.",
string_VkSampleCountFlagBits(image_state.createInfo.samples), string_VkImageViewType(view_type));
}
if (image_usage & (VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR)) {
if (view_type != VK_IMAGE_VIEW_TYPE_2D && view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-04818", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with video encode usage flags, but pCreateInfo->viewType (%s) "
"is not VK_IMAGE_VIEW_TYPE_2D or VK_IMAGE_VIEW_TYPE_2D_ARRAY.",
string_VkImageViewType(view_type));
}
if (!IsIdentitySwizzle(pCreateInfo->components)) {
skip |= LogError(
"VUID-VkImageViewCreateInfo-image-04818", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with video encode usage flags, but not all members of "
"pCreateInfo->components have identity swizzle. Here are the actual swizzle values:\n"
"r swizzle = %s\n"
"g swizzle = %s\n"
"b swizzle = %s\n"
"a swizzle = %s\n",
string_VkComponentSwizzle(pCreateInfo->components.r), string_VkComponentSwizzle(pCreateInfo->components.g),
string_VkComponentSwizzle(pCreateInfo->components.b), string_VkComponentSwizzle(pCreateInfo->components.a));
}
}
// Validate correct image aspect bits for desired formats and format consistency
skip |= ValidateImageAspectMask(image_state.image(), image_format, aspect_mask, image_state.disjoint, error_obj.location);
// Valdiate Image/ImageView type compatibility #resources-image-views-compatibility
switch (image_type) {
case VK_IMAGE_TYPE_1D:
if (view_type != VK_IMAGE_VIEW_TYPE_1D && view_type != VK_IMAGE_VIEW_TYPE_1D_ARRAY) {
skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", pCreateInfo->image,
create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
}
break;
case VK_IMAGE_TYPE_2D:
if (view_type != VK_IMAGE_VIEW_TYPE_2D && view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
if ((view_type == VK_IMAGE_VIEW_TYPE_CUBE || view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) &&
!(image_flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-01003", pCreateInfo->image,
create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
} else if (view_type != VK_IMAGE_VIEW_TYPE_CUBE && view_type != VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {
skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", pCreateInfo->image,
create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
}
}
break;
case VK_IMAGE_TYPE_3D:
if (IsExtEnabled(device_extensions.vk_khr_maintenance1)) {
if (view_type != VK_IMAGE_VIEW_TYPE_3D) {
if ((view_type == VK_IMAGE_VIEW_TYPE_2D || view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY)) {
if (IsExtEnabled(device_extensions.vk_ext_image_2d_view_of_3d)) {
if (!(image_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT)) {
if (view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
skip |= LogError(
"VUID-VkImageViewCreateInfo-image-06723", pCreateInfo->image,
create_info_loc.dot(Field::viewType),
"(%s) is not compatible with image type "
"%s since the image doesn't have VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT flag set.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
} else if (view_type == VK_IMAGE_VIEW_TYPE_2D &&
!(image_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-06728", pCreateInfo->image,
create_info_loc.dot(Field::viewType),
"(%s) is not compatible with image type "
"%s since the image doesn't have VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT or "
"VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT flag set.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
}
}
} else if (!(image_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) &&
(view_type == VK_IMAGE_VIEW_TYPE_2D)) {
// TODO - combine this logic
skip |= LogError("VUID-VkImageViewCreateInfo-image-06728", pCreateInfo->image,
create_info_loc.dot(Field::viewType),
"VK_IMAGE_VIEW_TYPE_2D is not compatible "
"with image type "
"%s since the image doesn't have VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT flag set.",
string_VkImageType(image_type));
}
if ((image_flags & (VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT |
VK_IMAGE_CREATE_SPARSE_ALIASED_BIT))) {
skip |= LogError(
"VUID-VkImageViewCreateInfo-image-04971", pCreateInfo->image, create_info_loc.dot(Field::viewType),
"(%s) is not compatible with image type %s "
"when the VK_IMAGE_CREATE_SPARSE_BINDING_BIT, VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, or "
"VK_IMAGE_CREATE_SPARSE_ALIASED_BIT flags are enabled.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
} else if (pCreateInfo->subresourceRange.levelCount != 1) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-04970", pCreateInfo->image,
create_info_loc.dot(Field::viewType),
"(%s) is with image type %s must have a "
"levelCount of 1 but it is %" PRIu32 ".",
string_VkImageViewType(view_type), string_VkImageType(image_type),
pCreateInfo->subresourceRange.levelCount);
}
} else {
skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", pCreateInfo->image,
create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
}
}
} else {
if (view_type != VK_IMAGE_VIEW_TYPE_3D) {
// Help point to VK_KHR_maintenance1
if ((view_type == VK_IMAGE_VIEW_TYPE_2D || view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY)) {
skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", pCreateInfo->image,
create_info_loc.dot(Field::viewType),
"(%s) is not compatible with image type %s "
"without VK_KHR_maintenance1 enabled which was promoted in Vulkan 1.0.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
} else {
skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", pCreateInfo->image,
create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
string_VkImageViewType(view_type), string_VkImageType(image_type));
}
}
}
break;
default:
break;
}
skip |= ValidateCreateImageViewANDROID(pCreateInfo, create_info_loc);
skip |= ValidateImageViewFormatFeatures(image_state, view_format, image_usage, create_info_loc);
if (enabled_features.shading_rate_image_features.shadingRateImage) {
if (image_usage & VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV) {
if (view_format != VK_FORMAT_R8_UINT) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-02087", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with usage containing "
"VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV, format must be VK_FORMAT_R8_UINT.");
}
}
}
if (enabled_features.shading_rate_image_features.shadingRateImage ||
enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate) {
if (image_usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR) {
if (view_type != VK_IMAGE_VIEW_TYPE_2D && view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-02086", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with usage containing "
"VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, viewType must be "
"VK_IMAGE_VIEW_TYPE_2D or VK_IMAGE_VIEW_TYPE_2D_ARRAY.");
}
}
}
if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate &&
!phys_dev_ext_props.fragment_shading_rate_props.layeredShadingRateAttachments &&
image_usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR && normalized_subresource_range.layerCount != 1) {
skip |= LogError("VUID-VkImageViewCreateInfo-usage-04551", pCreateInfo->image,
create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
"is %" PRIu32 " for a shading rate attachment image view.", normalized_subresource_range.layerCount);
}
if (layer_count == VK_REMAINING_ARRAY_LAYERS) {
const uint32_t remaining_layers = image_state.createInfo.arrayLayers - pCreateInfo->subresourceRange.baseArrayLayer;
if (view_type == VK_IMAGE_VIEW_TYPE_CUBE && remaining_layers != 6) {
skip |= LogError("VUID-VkImageViewCreateInfo-viewType-02962", pCreateInfo->image,
create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
"VK_REMAINING_ARRAY_LAYERS=(%d) must be 6.", remaining_layers);
}
if (view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY && ((remaining_layers) % 6) != 0) {
skip |= LogError("VUID-VkImageViewCreateInfo-viewType-02963", pCreateInfo->image,
create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
"VK_REMAINING_ARRAY_LAYERS=(%d) must be a multiple of 6.", remaining_layers);
}
if ((remaining_layers != 1) && ((view_type == VK_IMAGE_VIEW_TYPE_1D) || (view_type == VK_IMAGE_VIEW_TYPE_2D) ||
(view_type == VK_IMAGE_VIEW_TYPE_3D))) {
skip |=
LogError("VUID-VkImageViewCreateInfo-imageViewType-04974", pCreateInfo->image, create_info_loc.dot(Field::viewType),
"%s and the subresourceRange.layerCount "
"VK_REMAINING_ARRAY_LAYERS=(%d) and must 1 (try looking into VK_IMAGE_VIEW_TYPE_*_ARRAY).",
string_VkImageViewType(view_type), remaining_layers);
}
} else {
if ((layer_count != 1) && ((view_type == VK_IMAGE_VIEW_TYPE_1D) || (view_type == VK_IMAGE_VIEW_TYPE_2D) ||
(view_type == VK_IMAGE_VIEW_TYPE_3D))) {
skip |= LogError("VUID-VkImageViewCreateInfo-imageViewType-04973", pCreateInfo->image,
create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
"(%" PRIu32 ") must be 1 when using viewType %s (try looking into VK_IMAGE_VIEW_TYPE_*_ARRAY).",
layer_count, string_VkImageViewType(view_type));
}
}
if (image_usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT) {
if (normalized_subresource_range.levelCount != 1) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-02571", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with usage containing "
"VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT, subresourceRange.levelCount (%d) must be 1",
pCreateInfo->subresourceRange.levelCount);
}
}
if (pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT) {
if (!enabled_features.fragment_density_map_features.fragmentDensityMapDynamic) {
skip |= LogError("VUID-VkImageViewCreateInfo-flags-02572", pCreateInfo->image, create_info_loc.dot(Field::flags),
"contains VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT but the fragmentDensityMapDynamic "
"feature is not enabled.");
}
} else {
if (image_usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT) {
if (image_flags & (VK_IMAGE_CREATE_PROTECTED_BIT | VK_IMAGE_CREATE_SPARSE_BINDING_BIT |
VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-flags-04116", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with usage containing "
"VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT flags must not contain any of "
"VK_IMAGE_CREATE_PROTECTED_BIT, VK_IMAGE_CREATE_SPARSE_BINDING_BIT, "
"VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, or VK_IMAGE_CREATE_SPARSE_ALIASED_BIT");
}
}
}
if (image_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT) {
if (!vkuFormatIsCompressed(view_format)) {
if (pCreateInfo->subresourceRange.levelCount != 1) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-07072", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT bit, "
"and format (%s) is not compressed, but subresourcesRange.levelCount (%" PRIu32 ") is not 1.",
string_VkFormat(view_format), pCreateInfo->subresourceRange.levelCount);
}
if (pCreateInfo->subresourceRange.layerCount != 1) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-07072", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT bit, "
"and format (%s) is not compressed, but subresourcesRange.layerCount (%" PRIu32 ") is not 1.",
string_VkFormat(view_format), pCreateInfo->subresourceRange.layerCount);
}
}
const bool class_compatible = vkuFormatCompatibilityClass(view_format) == vkuFormatCompatibilityClass(image_format);
// "uncompressed format that is size-compatible" so if compressed, same as not being compatible
const bool size_compatible =
vkuFormatIsCompressed(view_format) ? false : vkuFormatElementSize(view_format) == vkuFormatElementSize(image_format);
if (!class_compatible && !size_compatible) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-01583", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT bit and "
"format (%s), but pCreateInfo->format (%s) are not compatible.",
string_VkFormat(image_format), string_VkFormat(view_format));
}
}
if (pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DEFERRED_BIT_EXT) {
if (!enabled_features.fragment_density_map2_features.fragmentDensityMapDeferred) {
skip |= LogError("VUID-VkImageViewCreateInfo-flags-03567", pCreateInfo->image, create_info_loc.dot(Field::flags),
"includes VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DEFERRED_BIT_EXT but the "
"fragmentDensityMapDeferred feature is not enabled.");
}
if (pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT) {
skip |= LogError("VUID-VkImageViewCreateInfo-flags-03568", pCreateInfo->image, create_info_loc.dot(Field::flags),
"(%s) includes both DEFERRED_BIT and DYNAMIC_BIT.",
string_VkImageViewCreateFlags(pCreateInfo->flags).c_str());
}
}
if (IsExtEnabled(device_extensions.vk_ext_fragment_density_map2)) {
if ((image_flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) && (image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) &&
(layer_count > phys_dev_ext_props.fragment_density_map2_props.maxSubsampledArrayLayers)) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-03569", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with flags containing "
"VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT and usage containing VK_IMAGE_USAGE_SAMPLED_BIT "
"subresourceRange.layerCount (%d) must: be less than or equal to maxSubsampledArrayLayers (%d)",
layer_count, phys_dev_ext_props.fragment_density_map2_props.maxSubsampledArrayLayers);
}
}
if (const auto astc_decode_mode = vku::FindStructInPNextChain<VkImageViewASTCDecodeModeEXT>(pCreateInfo->pNext);
(astc_decode_mode != nullptr)) {
if ((enabled_features.astc_decode_features.decodeModeSharedExponent == VK_FALSE) &&
(astc_decode_mode->decodeMode == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32)) {
skip |= LogError("VUID-VkImageViewASTCDecodeModeEXT-decodeMode-02231", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewASTCDecodeModeEXT, Field::decodeMode),
"is VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 but decodeModeSharedExponent feature is not enabled.");
}
}
if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) {
// If swizzling is disabled, make sure it isn't used
// NOTE: as of spec version 1.2.183, VUID 04465 states: "all elements of components _must_ be
// VK_COMPONENT_SWIZZLE_IDENTITY."
// However, issue https://github.com/KhronosGroup/Vulkan-Portability/issues/27 points out that the identity can
// also be defined via R, G, B, A enums in the correct order.
// Spec change is at https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/4600
if ((VK_FALSE == enabled_features.portability_subset_features.imageViewFormatSwizzle) &&
!IsIdentitySwizzle(pCreateInfo->components)) {
skip |= LogError("VUID-VkImageViewCreateInfo-imageViewFormatSwizzle-04465", pCreateInfo->image, create_info_loc,
"(portability error): swizzle is disabled for this device.");
}
// Ensure ImageView's format has the same number of bits and components as Image's format if format reinterpretation is
// disabled
if (!enabled_features.portability_subset_features.imageViewFormatReinterpretation &&
!FormatsEqualComponentBits(pCreateInfo->format, image_state.createInfo.format)) {
skip |=
LogError("VUID-VkImageViewCreateInfo-imageViewFormatReinterpretation-04466", pCreateInfo->image, create_info_loc,
"(portability error): ImageView format must have"
" the same number of components and bits per component as the Image's format");
}
}
if (const auto image_view_min_lod = vku::FindStructInPNextChain<VkImageViewMinLodCreateInfoEXT>(pCreateInfo->pNext); image_view_min_lod) {
if ((!enabled_features.image_view_min_lod_features.minLod) && (image_view_min_lod->minLod != 0)) {
skip |= LogError("VUID-VkImageViewMinLodCreateInfoEXT-minLod-06455", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewMinLodCreateInfoEXT, Field::minLod),
"%f, but the minLod feature is not enabled.", image_view_min_lod->minLod);
}
const auto max_level =
static_cast<float>(pCreateInfo->subresourceRange.baseMipLevel + (pCreateInfo->subresourceRange.levelCount - 1));
if (image_view_min_lod->minLod > max_level) {
skip |= LogError("VUID-VkImageViewMinLodCreateInfoEXT-minLod-06456", pCreateInfo->image,
create_info_loc.pNext(Struct::VkImageViewMinLodCreateInfoEXT, Field::minLod),
"(%f) must be less or equal to the index of the last mipmap level "
"accessible to the view (%f).",
image_view_min_lod->minLod, max_level);
}
}
const auto ycbcr_conversion = vku::FindStructInPNextChain<VkSamplerYcbcrConversionInfo>(pCreateInfo->pNext);
if (ycbcr_conversion && ycbcr_conversion->conversion != VK_NULL_HANDLE) {
auto ycbcr_state = Get<SAMPLER_YCBCR_CONVERSION_STATE>(ycbcr_conversion->conversion);
if (pCreateInfo->format != ycbcr_state->format) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06658", pCreateInfo->image,
create_info_loc.pNext(Struct::VkSamplerYcbcrConversionInfo, Field::conversion),
"was created with format %s which is different than pCreateInfo->format %s.",
string_VkFormat(ycbcr_state->format), string_VkFormat(view_format));
}
} else if ((image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) && FormatRequiresYcbcrConversionExplicitly(view_format)) {
skip |= LogError("VUID-VkImageViewCreateInfo-format-06415", pCreateInfo->image, create_info_loc.dot(Field::image),
"was created with VK_IMAGE_USAGE_SAMPLED_BIT, but pCreateInfo->format %s requires a "
"VkSamplerYcbcrConversion but one was not passed in the pNext chain.",
string_VkFormat(view_format));
}
if (pCreateInfo->viewType != VK_IMAGE_VIEW_TYPE_2D && pCreateInfo->viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
VkImageUsageFlags decode_usage = VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
if (image_usage & decode_usage) {
skip |= LogError("VUID-VkImageViewCreateInfo-image-04817", pCreateInfo->image, create_info_loc.dot(Field::viewType),
"%s is incompatible with decode usage.", string_VkImageViewType(pCreateInfo->viewType));
}
}
if ((pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT) &&
!enabled_features.descriptor_buffer_features.descriptorBufferCaptureReplay) {
skip |= LogError("VUID-VkImageViewCreateInfo-flags-08106", pCreateInfo->image, create_info_loc.dot(Field::flags),
"includes VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT but the "
"descriptorBufferCaptureReplay feature is not enabled.");
}
if (const auto opaque_capture_descriptor_buffer =
vku::FindStructInPNextChain<VkOpaqueCaptureDescriptorDataCreateInfoEXT>(pCreateInfo->pNext);
opaque_capture_descriptor_buffer && !(pCreateInfo->flags & VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-08107", pCreateInfo->image, create_info_loc.dot(Field::flags),
"(%s) is missing VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT but "
"VkOpaqueCaptureDescriptorDataCreateInfoEXT is in pNext chain.",
string_VkImageViewCreateFlags(pCreateInfo->flags).c_str());
}
skip |= ValidateImageViewSampleWeightQCOM(pCreateInfo, image_state, create_info_loc);
return skip;
}
bool CoreChecks::PreCallValidateDestroyImageView(VkDevice device, VkImageView imageView, const VkAllocationCallbacks *pAllocator,
const ErrorObject &error_obj) const {
auto image_view_state = Get<IMAGE_VIEW_STATE>(imageView);
bool skip = false;
if (image_view_state) {
skip |= ValidateObjectNotInUse(image_view_state.get(), error_obj.location, "VUID-vkDestroyImageView-imageView-01026");
}
return skip;
}
bool CoreChecks::ValidateGetImageSubresourceLayout(const IMAGE_STATE &image_state, const VkImageSubresource &subresource,
const Location &subresource_loc) const {
bool skip = false;
const bool is_2 = subresource_loc.function != Func::vkGetImageSubresourceLayout;
const VkImageAspectFlags aspect_mask = subresource.aspectMask;
// The aspectMask member of pSubresource must only have a single bit set
if (GetBitSetCount(aspect_mask) != 1) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-aspectMask-00997" : "VUID-vkGetImageSubresourceLayout-aspectMask-00997";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask), "(%s) must have exactly 1 bit set.",
string_VkImageAspectFlags(aspect_mask).c_str());
}
// mipLevel must be less than the mipLevels specified in VkImageCreateInfo when the image was created
if (subresource.mipLevel >= image_state.createInfo.mipLevels) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-mipLevel-01716" : "VUID-vkGetImageSubresourceLayout-mipLevel-01716";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::mipLevel),
"(%" PRIu32 ") must be less than %" PRIu32 ".", subresource.mipLevel, image_state.createInfo.mipLevels);
}
// arrayLayer must be less than the arrayLayers specified in VkImageCreateInfo when the image was created
if (subresource.arrayLayer >= image_state.createInfo.arrayLayers) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-arrayLayer-01717" : "VUID-vkGetImageSubresourceLayout-arrayLayer-01717";
skip |=
LogError(vuid, image_state.image(), subresource_loc.dot(Field::arrayLayer),
"(%" PRIu32 ") must be less than %" PRIu32 ".", subresource.arrayLayer, image_state.createInfo.arrayLayers);
}
const VkFormat image_format = image_state.createInfo.format;
const bool tiling_linear_optimal =
image_state.createInfo.tiling == VK_IMAGE_TILING_LINEAR || image_state.createInfo.tiling == VK_IMAGE_TILING_OPTIMAL;
if (vkuFormatIsColor(image_format) && !vkuFormatIsMultiplane(image_format) && (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT) &&
tiling_linear_optimal) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-format-08886" : "VUID-vkGetImageSubresourceLayout-format-08886";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask),
"is %s but image was created with color format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
if (vkuFormatHasDepth(image_format) && ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0)) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-format-04462" : "VUID-vkGetImageSubresourceLayout-format-04462";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask),
"is %s but image was created with depth format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
if (vkuFormatHasStencil(image_format) && ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) == 0)) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-format-04463" : "VUID-vkGetImageSubresourceLayout-format-04463";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask),
"is %s but image was created with stencil format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
if (!vkuFormatHasDepth(image_format) && !vkuFormatHasStencil(image_format)) {
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-format-04464" : "VUID-vkGetImageSubresourceLayout-format-04464";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask),
"is %s but image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
}
// subresource's aspect must be compatible with image's format.
if (image_state.createInfo.tiling == VK_IMAGE_TILING_LINEAR) {
if (vkuFormatIsMultiplane(image_format) && !IsOnlyOneValidPlaneAspect(image_format, aspect_mask)) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-tiling-08717" : "VUID-vkGetImageSubresourceLayout-tiling-08717";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask), "(%s) is invalid for format %s.",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
}
} else if (image_state.createInfo.tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
if ((aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT) && (aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT) &&
(aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT) && (aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT)) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-tiling-02271" : "VUID-vkGetImageSubresourceLayout-tiling-02271";
skip |=
LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask),
"(%s) must be VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT.", string_VkImageAspectFlags(aspect_mask).c_str());
} else {
// Parameter validation should catch if this is used without VK_EXT_image_drm_format_modifier
assert(IsExtEnabled(device_extensions.vk_ext_image_drm_format_modifier));
VkImageDrmFormatModifierPropertiesEXT drm_format_properties = vku::InitStructHelper();
DispatchGetImageDrmFormatModifierPropertiesEXT(device, image_state.image(), &drm_format_properties);
VkDrmFormatModifierPropertiesListEXT fmt_drm_props = vku::InitStructHelper();
VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
DispatchGetPhysicalDeviceFormatProperties2(physical_device, image_state.createInfo.format, &fmt_props_2);
std::vector<VkDrmFormatModifierPropertiesEXT> drm_properties{fmt_drm_props.drmFormatModifierCount};
fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();
DispatchGetPhysicalDeviceFormatProperties2(physical_device, image_state.createInfo.format, &fmt_props_2);
uint32_t max_plane_count = 0u;
for (auto const &drm_property : drm_properties) {
if (drm_format_properties.drmFormatModifier == drm_property.drmFormatModifier) {
max_plane_count = drm_property.drmFormatModifierPlaneCount;
break;
}
}
VkImageAspectFlagBits allowed_plane_indices[] = {
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};
bool is_valid = false;
for (uint32_t i = 0u; i < max_plane_count; ++i) {
if (aspect_mask == allowed_plane_indices[i]) {
is_valid = true;
break;
}
}
if (!is_valid) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-tiling-02271" : "VUID-vkGetImageSubresourceLayout-tiling-02271";
skip |= LogError(vuid, image_state.image(), subresource_loc.dot(Field::aspectMask),
"is %s for image format %s, but drmFormatModifierPlaneCount is %" PRIu32 " (drmFormatModifier = %" PRIu64 ").",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_state.createInfo.format),
max_plane_count, drm_format_properties.drmFormatModifier);
}
}
}
if (image_state.IsExternalBuffer() && image_state.GetBoundMemoryStates().empty()) {
const char *vuid =
is_2 ? "VUID-vkGetImageSubresourceLayout2KHR-image-01895" : "VUID-vkGetImageSubresourceLayout-image-01895";
skip |= LogError(vuid, image_state.image(), subresource_loc,
"Attempt to query layout from an image created with "
"VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID handleType which has not yet been "
"bound to memory.");
}
return skip;
}
bool CoreChecks::PreCallValidateGetImageSubresourceLayout(VkDevice device, VkImage image, const VkImageSubresource *pSubresource,
VkSubresourceLayout *pLayout, const ErrorObject &error_obj) const {
bool skip = false;
auto image_state = Get<IMAGE_STATE>(image);
if (pSubresource && pLayout && image_state) {
skip = ValidateGetImageSubresourceLayout(*image_state, *pSubresource, error_obj.location.dot(Field::pSubresource));
if ((image_state->createInfo.tiling != VK_IMAGE_TILING_LINEAR) &&
(image_state->createInfo.tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT)) {
skip |= LogError("VUID-vkGetImageSubresourceLayout-image-07790", image, error_obj.location,
"image was created with tiling %s.", string_VkImageTiling(image_state->createInfo.tiling));
}
}
return skip;
}
bool CoreChecks::PreCallValidateGetImageSubresourceLayout2KHR(VkDevice device, VkImage image,
const VkImageSubresource2KHR *pSubresource,
VkSubresourceLayout2KHR *pLayout,
const ErrorObject &error_obj) const {
bool skip = false;
auto image_state = Get<IMAGE_STATE>(image);
if (pSubresource && pLayout && image_state) {
skip = ValidateGetImageSubresourceLayout(*image_state, pSubresource->imageSubresource,
error_obj.location.dot(Field::pSubresource).dot(Field::imageSubresource));
}
return skip;
}
bool CoreChecks::PreCallValidateGetImageSubresourceLayout2EXT(VkDevice device, VkImage image,
const VkImageSubresource2EXT *pSubresource,
VkSubresourceLayout2EXT *pLayout,
const ErrorObject &error_obj) const {
return PreCallValidateGetImageSubresourceLayout2KHR(device, image, pSubresource, pLayout, error_obj);
}
bool CoreChecks::PreCallValidateGetImageDrmFormatModifierPropertiesEXT(VkDevice device, VkImage image,
VkImageDrmFormatModifierPropertiesEXT *pProperties,
const ErrorObject &error_obj) const {
bool skip = false;
auto image_state = Get<IMAGE_STATE>(image);
if (image_state) {
if (image_state->createInfo.tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
skip |=
LogError("VUID-vkGetImageDrmFormatModifierPropertiesEXT-image-02272", image, error_obj.location.dot(Field::image),
"was created with tiling %s.", string_VkImageTiling(image_state->createInfo.tiling));
}
}
return skip;
}
static const SubresourceRangeErrorCodes TransitionImageLayoutVUIDs{
"VUID-VkHostImageLayoutTransitionInfoEXT-subresourceRange-01486",
"VUID-VkHostImageLayoutTransitionInfoEXT-subresourceRange-01724",
"VUID-VkHostImageLayoutTransitionInfoEXT-subresourceRange-01488",
"VUID-VkHostImageLayoutTransitionInfoEXT-subresourceRange-01725",
};
bool CoreChecks::PreCallValidateTransitionImageLayoutEXT(VkDevice device, uint32_t transitionCount,
const VkHostImageLayoutTransitionInfoEXT *pTransitions,
const ErrorObject &error_obj) const {
bool skip = false;
for (uint32_t i = 0; i < transitionCount; ++i) {
const Location transition_loc = error_obj.location.dot(Field::pTransitions, i);
const auto &transition = pTransitions[i];
const auto image_state = Get<IMAGE_STATE>(transition.image);
const auto image_format = image_state->createInfo.format;
const auto aspect_mask = transition.subresourceRange.aspectMask;
const bool has_depth_mask = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
const bool has_stencil_mask = (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
if ((image_state->createInfo.usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) == 0) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-image-09055", objlist, transition_loc.dot(Field::image),
"was created with usage (%s) which does not contain "
"VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT.",
string_VkImageUsageFlags(image_state->createInfo.usage).c_str());
}
skip |= ValidateImageSubresourceRange(image_state->createInfo.mipLevels, image_state->createInfo.arrayLayers,
transition.subresourceRange, "arrayLayers", image_state->image(),
TransitionImageLayoutVUIDs, transition_loc.dot(Field::subresourceRange));
skip |=
ValidateMemoryIsBoundToImage(LogObjectList(device, transition.image), *image_state, transition_loc.dot(Field::image),
"VUID-VkHostImageLayoutTransitionInfoEXT-image-01932");
if (vkuFormatIsColor(image_format) && (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT)) {
if (!vkuFormatIsMultiplane(image_format)) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-image-09241", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s (not VK_IMAGE_ASPECT_COLOR_BIT) and image was created with format %s.",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
} else if (!image_state->disjoint) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-image-09242", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s (not VK_IMAGE_ASPECT_COLOR_BIT) and image was created with format %s.",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
}
}
if ((vkuFormatIsMultiplane(image_format)) && (image_state->disjoint)) {
if (!IsValidPlaneAspect(image_format, aspect_mask) && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) == 0)) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-image-01672", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
}
if (vkuFormatIsDepthAndStencil(image_format)) {
if (enabled_features.core12.separateDepthStencilLayouts) {
if (!has_depth_mask && !has_stencil_mask) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-image-03319", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
} else {
if (!has_depth_mask || !has_stencil_mask) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-image-03320", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
string_VkFormat(image_format));
}
}
}
if (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) {
if ((transition.oldLayout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) ||
(transition.oldLayout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL) ||
(transition.newLayout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) ||
(transition.newLayout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL)) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-aspectMask-08702", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s meaning that neither oldLayout nor newLayout can be "
"VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL"
" (oldLayout = %s and newLayout = %s).",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkImageLayout(transition.oldLayout),
string_VkImageLayout(transition.oldLayout));
}
}
if (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) {
if ((transition.oldLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ||
(transition.oldLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL) ||
(transition.newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ||
(transition.newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL)) {
const LogObjectList objlist(device, image_state->Handle());
skip |= LogError("VUID-VkHostImageLayoutTransitionInfoEXT-aspectMask-08703", objlist,
transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
"is %s meaning that neither oldLayout nor newLayout can be "
"VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL"
" (oldLayout = %s and newLayout = %s).",
string_VkImageAspectFlags(aspect_mask).c_str(), string_VkImageLayout(transition.oldLayout),
string_VkImageLayout(transition.oldLayout));
}
}
if ((transition.oldLayout != VK_IMAGE_LAYOUT_UNDEFINED) && (transition.oldLayout != VK_IMAGE_LAYOUT_PREINITIALIZED)) {
auto *props = &phys_dev_ext_props.host_image_copy_properties;
skip |= ValidateHostCopyImageLayout(device, transition.image, props->copySrcLayoutCount, props->pCopySrcLayouts,
transition.oldLayout, transition_loc.dot(Field::oldLayout), "pCopySrcLayouts",
"VUID-VkHostImageLayoutTransitionInfoEXT-oldLayout-09230");
}
const auto *props = &phys_dev_ext_props.host_image_copy_properties;
skip |= ValidateHostCopyImageLayout(device, transition.image, props->copyDstLayoutCount, props->pCopyDstLayouts,
transition.newLayout, transition_loc.dot(Field::newLayout), "pCopyDstLayouts",
"VUID-VkHostImageLayoutTransitionInfoEXT-newLayout-09057");
if (transition.oldLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
skip |= ValidateHostCopyCurrentLayout(device, transition.oldLayout, transition.subresourceRange, i, *image_state,
transition_loc.dot(Field::oldLayout), "transition",
"VUID-VkHostImageLayoutTransitionInfoEXT-oldLayout-09229");
}
}
return skip;
}
void CoreChecks::PostCallRecordTransitionImageLayoutEXT(VkDevice device, uint32_t transitionCount,
const VkHostImageLayoutTransitionInfoEXT *pTransitions,
const RecordObject &record_obj) {
ValidationStateTracker::PostCallRecordTransitionImageLayoutEXT(device, transitionCount, pTransitions, record_obj);
if (VK_SUCCESS != record_obj.result) return;
for (uint32_t i = 0; i < transitionCount; ++i) {
auto &transition = pTransitions[i];
auto image_state = Get<IMAGE_STATE>(transition.image);
if (!image_state) continue;
image_state->SetImageLayout(transition.subresourceRange, transition.newLayout);
}
}
// Validates the image is allowed to be protected
bool CoreChecks::ValidateProtectedImage(const CMD_BUFFER_STATE &cb_state, const IMAGE_STATE &image_state, const Location &loc,
const char *vuid, const char *more_message) const {
bool skip = false;
// if driver supports protectedNoFault the operation is valid, just has undefined values
if ((!phys_dev_props_core11.protectedNoFault) && (cb_state.unprotected == true) && (image_state.unprotected == false)) {
const LogObjectList objlist(cb_state.Handle(), image_state.Handle());
skip |= LogError(vuid, objlist, loc, "(%s) is a protected image, but command buffer (%s) is unprotected.%s",
FormatHandle(image_state).c_str(), FormatHandle(cb_state).c_str(), more_message);
}
return skip;
}
// Validates the image is allowed to be unprotected
bool CoreChecks::ValidateUnprotectedImage(const CMD_BUFFER_STATE &cb_state, const IMAGE_STATE &image_state, const Location &loc,
const char *vuid, const char *more_message) const {
bool skip = false;
// if driver supports protectedNoFault the operation is valid, just has undefined values
if ((!phys_dev_props_core11.protectedNoFault) && (cb_state.unprotected == false) && (image_state.unprotected == true)) {
const LogObjectList objlist(cb_state.Handle(), image_state.Handle());
skip |= LogError(vuid, objlist, loc, "(%s) is an unprotected image, but command buffer (%s) is protected.%s",
FormatHandle(image_state).c_str(), FormatHandle(cb_state).c_str(), more_message);
}
return skip;
}
bool CoreChecks::ValidateImageViewSampleWeightQCOM(const VkImageViewCreateInfo *pCreateInfo, const IMAGE_STATE &image_state,
const Location &loc) const {
bool skip = false;
auto sample_weight_info = vku::FindStructInPNextChain<VkImageViewSampleWeightCreateInfoQCOM>(pCreateInfo->pNext);
if (!sample_weight_info) {
return skip;
}
const VkImageAspectFlags aspect_mask = pCreateInfo->subresourceRange.aspectMask;
const VkImageType image_type = image_state.createInfo.imageType;
const VkImageUsageFlags image_usage = image_state.createInfo.usage;
const VkExtent3D image_extent = image_state.createInfo.extent;
const uint32_t layer_count = pCreateInfo->subresourceRange.layerCount;
if ((enabled_features.image_processing_features.textureSampleWeighted == VK_FALSE)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06944", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"but textureSampleWeighted feature is not enabled.");
}
// Validate VkImage and VkImageView compatibility with VkImageViewSampleWeightCreateInfoQCOM
if ((image_usage & VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM) == 0) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06945", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"but image not created with VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM.");
}
if (!IsIdentitySwizzle(pCreateInfo->components)) {
skip |=
LogError("VUID-VkImageViewCreateInfo-pNext-06946", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"but not all members of pCreateInfo->components have identity swizzle. Here are the actual swizzle values:\n"
"r swizzle = %s\n"
"g swizzle = %s\n"
"b swizzle = %s\n"
"a swizzle = %s\n",
string_VkComponentSwizzle(pCreateInfo->components.r), string_VkComponentSwizzle(pCreateInfo->components.g),
string_VkComponentSwizzle(pCreateInfo->components.b), string_VkComponentSwizzle(pCreateInfo->components.a));
}
if (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06947", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"but the subresource range aspect mask (%s) is not VK_IMAGE_ASPECT_COLOR_BIT.",
string_VkImageAspectFlags(aspect_mask).c_str());
}
if (pCreateInfo->subresourceRange.levelCount != 1) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06948", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"but the subresource level count (%" PRIu32 ") is not equal to 1.",
pCreateInfo->subresourceRange.levelCount);
}
if ((pCreateInfo->viewType != VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (pCreateInfo->viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06949", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"but the view type (%s) is not VK_IMAGE_VIEW_TYPE_1D_ARRAY or VK_IMAGE_VIEW_TYPE_2D_ARRAY.",
string_VkImageViewType(pCreateInfo->viewType));
}
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (image_type != VK_IMAGE_TYPE_1D)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06950", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"and the viewType is VK_IMAGE_VIEW_TYPE_1D_ARRAY but the image type (%s) is not VK_IMAGE_TYPE_1D.",
string_VkImageType(image_type));
}
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (layer_count != 2)) {
skip |=
LogError("VUID-VkImageViewCreateInfo-pNext-06951", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"and the view type is VK_IMAGE_VIEW_TYPE_1D_ARRAY but the subresourceRange layerCount (%" PRIu32 ") is not 2.",
layer_count);
}
const uint32_t filter_width_aligned_to_four = (sample_weight_info->filterSize.width + 3) & ~(3);
const uint32_t min_image_width =
sample_weight_info->numPhases * (std::max(filter_width_aligned_to_four, sample_weight_info->filterSize.height));
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (image_extent.width < min_image_width)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06952", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"for a VK_IMAGE_VIEW_TYPE_1D_ARRAY with an image created with extent.width (%" PRIu32
"), but numPhases (%" PRIu32 "), filterSize.width (%" PRIu32 "), and filterSize.height (%" PRIu32
") requires an image extent.width greater than or equal to (%" PRIu32 ").",
image_extent.width, sample_weight_info->numPhases, sample_weight_info->filterSize.width,
sample_weight_info->filterSize.height, min_image_width);
}
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (image_type != VK_IMAGE_TYPE_2D)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06953", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"and the view type is VK_IMAGE_VIEW_TYPE_2D_ARRAY but the image created type (%s) is "
"not VK_IMAGE_TYPE_2D.",
string_VkImageType(image_type));
}
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (layer_count < (sample_weight_info->numPhases))) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06954", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"and the view type is VK_IMAGE_VIEW_TYPE_1D_ARRAY but the subresourceRange "
"layerCount (%" PRIu32
") is not greater than or equal to "
"VkImageViewSampleWeightCreateInfoQCOM::numPhases (%" PRIu32 ").",
layer_count, sample_weight_info->numPhases);
}
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (image_extent.width < sample_weight_info->filterSize.width)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06955", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"for a VK_IMAGE_VIEW_TYPE_2D_ARRAY but the created image extent.width (%" PRIu32
") is not greater than or equal to filerSize.width (%" PRIu32 ").",
image_extent.width, sample_weight_info->filterSize.width);
}
if ((pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (image_extent.width < sample_weight_info->filterSize.height)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06956", pCreateInfo->image, loc,
"pNext chain includes VkImageViewSampleWeightCreateInfoQCOM "
"for a VK_IMAGE_VIEW_TYPE_2D_ARRAY but the created image extent.height (%" PRIu32
") is not greater than or equal to filerSize.height (%" PRIu32 ").",
image_extent.height, sample_weight_info->filterSize.height);
}
if ((sample_weight_info->filterSize.height > phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.height)) {
skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06957", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::filterSize.height (%" PRIu32
") is not less than or equal to "
"VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterDimension.height (%" PRIu32 ").",
sample_weight_info->filterSize.height,
phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.height);
}
// Valiate VkImageViewSampleWeightCreateInfoQCOM
if ((sample_weight_info->filterSize.width > phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.width)) {
skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-filterSize-06958", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::filterSize.width (%" PRIu32
") is not less than or equal to "
"VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterDimension.width (%" PRIu32 ").",
sample_weight_info->filterSize.width,
phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.width);
}
if ((sample_weight_info->filterSize.height > phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.height)) {
skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-filterSize-06959", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::filterSize.height (%" PRIu32
") is not less than or equal to "
"VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterDimension.height (%" PRIu32 ").",
sample_weight_info->filterSize.width,
phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.width);
}
if ((static_cast<uint32_t>(sample_weight_info->filterCenter.x) >= sample_weight_info->filterSize.width)) {
skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-filterCenter-06960", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::filterCenter.x (%" PRId32
") is not less than "
"VkImageViewSampleWeightCreateInfoQCOM::filterSize.width (%" PRIu32 ").",
sample_weight_info->filterCenter.x, sample_weight_info->filterSize.width);
}
if ((static_cast<uint32_t>(sample_weight_info->filterCenter.y) >= sample_weight_info->filterSize.height)) {
skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-filterCenter-06961", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::filterCenter.y (%" PRId32
") is not less than "
"VkImageViewSampleWeightCreateInfoQCOM::filterSize.height (%" PRIu32 ").",
sample_weight_info->filterCenter.y, sample_weight_info->filterSize.height);
}
if (!IsPowerOfTwo(sample_weight_info->numPhases) || (MostSignificantBit(sample_weight_info->numPhases) % 2 != 0)) {
skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-numPhases-06962", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::numPhasaes (%" PRIu32
") is not a power of two squared "
"value (i.e., 1, 4, 16, 64, 256, etc.)",
sample_weight_info->numPhases);
}
if ((sample_weight_info->numPhases > phys_dev_ext_props.image_processing_props.maxWeightFilterPhases)) {
skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-numPhases-06963", pCreateInfo->image, loc,
"VkImageViewSampleWeightCreateInfoQCOM::numPhases (%" PRIu32
") is not less than or equal to "
"VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterPhases (%" PRIu32 ")",
sample_weight_info->numPhases, phys_dev_ext_props.image_processing_props.maxWeightFilterPhases);
}
return skip;
}