blob: cba45ad5bd39d8537caa4d38d3ef8d18e2781ce2 [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.
*
* 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 <algorithm>
#include <assert.h>
#include <sstream>
#include <vector>
#include <vulkan/vk_enum_string_helper.h>
#include "generated/chassis.h"
#include "core_validation.h"
static bool IsExtentInsideBounds(VkExtent2D extent, VkExtent2D min, VkExtent2D max) {
if ((extent.width < min.width) || (extent.width > max.width) || (extent.height < min.height) || (extent.height > max.height)) {
return false;
}
return true;
}
static VkImageCreateInfo GetSwapchainImpliedImageCreateInfo(VkSwapchainCreateInfoKHR const *pCreateInfo) {
VkImageCreateInfo result = vku::InitStructHelper();
if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) {
result.flags |= VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT;
}
if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) result.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
result.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
}
result.imageType = VK_IMAGE_TYPE_2D;
result.format = pCreateInfo->imageFormat;
result.extent.width = pCreateInfo->imageExtent.width;
result.extent.height = pCreateInfo->imageExtent.height;
result.extent.depth = 1;
result.mipLevels = 1;
result.arrayLayers = pCreateInfo->imageArrayLayers;
result.samples = VK_SAMPLE_COUNT_1_BIT;
result.tiling = VK_IMAGE_TILING_OPTIMAL;
result.usage = pCreateInfo->imageUsage;
result.sharingMode = pCreateInfo->imageSharingMode;
result.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount;
result.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices;
result.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
return result;
}
bool CoreChecks::ValidateSwapchainPresentModesCreateInfo(VkPresentModeKHR present_mode, const Location &create_info_loc,
VkSwapchainCreateInfoKHR const *create_info,
const SURFACE_STATE *surface_state) const {
bool skip = false;
std::vector<VkPresentModeKHR> present_modes{};
if (surface_state) {
present_modes = surface_state->GetPresentModes(physical_device, this);
} else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) {
present_modes = physical_device_state->surfaceless_query_state.present_modes;
}
if (std::find(present_modes.begin(), present_modes.end(), present_mode) == present_modes.end()) {
skip |= LogError("VUID-VkSwapchainCreateInfoKHR-presentMode-01281", device, create_info_loc,
"called with a non-supported presentMode (%s).", string_VkPresentModeKHR(present_mode));
}
// Validate VkSwapchainPresentModesCreateInfoEXT data
auto swapchain_present_modes_ci = vku::FindStructInPNextChain<VkSwapchainPresentModesCreateInfoEXT>(create_info->pNext);
if (swapchain_present_modes_ci) {
// Ensure surface state is non-null if not using surfaceless_query
assert(surface_state);
bool found_swapchain_modes_ci_present_mode = false;
const std::vector<VkPresentModeKHR> compatible_present_modes =
surface_state->GetCompatibleModes(physical_device, present_mode);
for (uint32_t i = 0; i < swapchain_present_modes_ci->presentModeCount; i++) {
VkPresentModeKHR swapchain_present_mode = swapchain_present_modes_ci->pPresentModes[i];
if (std::find(present_modes.begin(), present_modes.end(), swapchain_present_mode) == present_modes.end()) {
if (LogError("VUID-VkSwapchainPresentModesCreateInfoEXT-None-07762", device,
create_info_loc.pNext(Struct::VkSwapchainPresentModesCreateInfoEXT, Field::pPresentModes, i),
"%s is a non-supported presentMode.", string_VkPresentModeKHR(swapchain_present_mode))) {
skip |= true;
}
}
if (std::find(compatible_present_modes.begin(), compatible_present_modes.end(), swapchain_present_mode) ==
compatible_present_modes.end()) {
if (LogError("VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-07763", device,
create_info_loc.pNext(Struct::VkSwapchainPresentModesCreateInfoEXT, Field::pPresentModes, i),
"%s is a non-compatible presentMode.", string_VkPresentModeKHR(swapchain_present_mode))) {
skip |= true;
}
}
const bool has_present_mode = (swapchain_present_modes_ci->pPresentModes[i] == present_mode);
found_swapchain_modes_ci_present_mode |= has_present_mode;
}
if (!found_swapchain_modes_ci_present_mode) {
if (LogError("VUID-VkSwapchainPresentModesCreateInfoEXT-presentMode-07764", device, create_info_loc,
"was called with a present mode (%s) that was not included in the set of present modes specified in "
"the vkSwapchainPresentModesCreateInfoEXT structure included in its pNext chain.",
string_VkPresentModeKHR(present_mode))) {
skip |= true;
}
}
}
return skip;
}
bool CoreChecks::ValidateSwapchainPresentScalingCreateInfo(VkPresentModeKHR present_mode, const Location &create_info_loc,
const VkSurfaceCapabilitiesKHR *capabilities,
VkSwapchainCreateInfoKHR const *create_info,
const SURFACE_STATE *surface_state) const {
bool skip = false;
auto pres_scale_ci = vku::FindStructInPNextChain<VkSwapchainPresentScalingCreateInfoEXT>(create_info->pNext);
if ((!pres_scale_ci) || (pres_scale_ci && (pres_scale_ci->scalingBehavior == 0))) {
if (!IsExtentInsideBounds(create_info->imageExtent, capabilities->minImageExtent, capabilities->maxImageExtent)) {
skip |= LogError("VUID-VkSwapchainCreateInfoKHR-pNext-07781", device, create_info_loc.dot(Field::imageExtent),
"(%" PRIu32 ",%" PRIu32
"), which is outside the bounds returned by "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): currentExtent = (%" PRIu32 ",%" PRIu32
"), minImageExtent = (%" PRIu32 ",%" PRIu32 "), maxImageExtent = (%" PRIu32 ",%" PRIu32 ").",
create_info->imageExtent.width, create_info->imageExtent.height, capabilities->currentExtent.width,
capabilities->currentExtent.height, capabilities->minImageExtent.width,
capabilities->minImageExtent.height, capabilities->maxImageExtent.width,
capabilities->maxImageExtent.height);
}
}
if (pres_scale_ci) {
if ((pres_scale_ci->presentGravityX == 0) && (pres_scale_ci->presentGravityY != 0)) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07765", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityX),
"is zero but presentGravityY (%" PRIu32 ") is not zero.", pres_scale_ci->presentGravityY)) {
skip |= true;
}
}
if ((pres_scale_ci->presentGravityX != 0) && (pres_scale_ci->presentGravityY == 0)) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07766", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityY),
"is zero but presentGravityX (%" PRIu32 ") is not zero.", pres_scale_ci->presentGravityX)) {
skip |= true;
}
}
if (GetBitSetCount(pres_scale_ci->scalingBehavior) > 1) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07767", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::scalingBehavior),
"(%s) must not have more than one bit set.",
string_VkPresentScalingFlagsEXT(pres_scale_ci->scalingBehavior).c_str())) {
skip |= true;
}
}
if (GetBitSetCount(pres_scale_ci->presentGravityX) > 1) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07768", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityX),
"(%s) must not have more than one bit set.",
string_VkPresentGravityFlagsEXT(pres_scale_ci->presentGravityX).c_str())) {
skip |= true;
}
}
if (GetBitSetCount(pres_scale_ci->presentGravityY) > 1) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07769", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityY),
"(%s) must not have more than one bit set.",
string_VkPresentGravityFlagsEXT(pres_scale_ci->presentGravityY).c_str())) {
skip |= true;
}
}
assert(surface_state);
VkSurfacePresentScalingCapabilitiesEXT scaling_caps =
surface_state->GetPresentModeScalingCapabilities(physical_device, present_mode);
if ((scaling_caps.supportedPresentScaling != 0) &&
(scaling_caps.supportedPresentScaling & pres_scale_ci->scalingBehavior) == 0) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07770", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::scalingBehavior),
"(%s) is not among the scaling methods for the surface as returned in "
"VkSurfacePresentScalingCapabilitiesEXT::supportedPresentScaling for the specified presentMode: (%s).",
string_VkPresentScalingFlagsEXT(pres_scale_ci->scalingBehavior).c_str(),
string_VkPresentGravityFlagsEXT(scaling_caps.supportedPresentScaling).c_str())) {
skip |= true;
}
}
if ((scaling_caps.supportedPresentGravityX != 0) &&
(scaling_caps.supportedPresentGravityX & pres_scale_ci->presentGravityX) == 0) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07772", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityX),
"(%s) must "
"be a valid present gravity for the surface as returned in "
"VkSurfacePresentScalingCapabilitiesEXT::supportedPresentGravityX for the given presentMode (%s).",
string_VkPresentGravityFlagsEXT(pres_scale_ci->presentGravityX).c_str(),
string_VkPresentGravityFlagsEXT(scaling_caps.supportedPresentGravityX).c_str())) {
skip |= true;
}
}
if ((scaling_caps.supportedPresentGravityY != 0) &&
(scaling_caps.supportedPresentGravityY & pres_scale_ci->presentGravityY) == 0) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07774", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityY),
"(%s) must "
"be a valid present gravity for the surface as returned in "
"VkSurfacePresentScalingCapabilitiesEXT::supportedPresentGravityY for the given presentMode (%s).",
string_VkPresentGravityFlagsEXT(pres_scale_ci->presentGravityY).c_str(),
string_VkPresentGravityFlagsEXT(scaling_caps.supportedPresentGravityY).c_str())) {
skip |= true;
}
}
if ((pres_scale_ci->scalingBehavior != 0) &&
(!IsExtentInsideBounds(create_info->imageExtent, scaling_caps.minScaledImageExtent,
scaling_caps.maxScaledImageExtent))) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-pNext-07782", device, create_info_loc.dot(Field::imageExtent),
"(%" PRIu32 ",%" PRIu32 "), which is outside the bounds returned in "
"VkSurfacePresentScalingCapabilitiesEXT minScaledImageExtent = (%" PRIu32 ",%" PRIu32 "), "
"maxScaledImageExtent = (%" PRIu32 ",%" PRIu32 ").",
create_info->imageExtent.width, create_info->imageExtent.height, scaling_caps.minScaledImageExtent.width,
scaling_caps.minScaledImageExtent.height, scaling_caps.maxScaledImageExtent.width,
scaling_caps.maxScaledImageExtent.height)) {
skip |= true;
}
}
// Further validation for when a VkSwapchainPresentModesCreateInfoEXT struct is *also* in the pNext chain
const auto *present_modes_ci = vku::FindStructInPNextChain<VkSwapchainPresentModesCreateInfoEXT>(create_info->pNext);
if (present_modes_ci) {
for (uint32_t i = 0; i < present_modes_ci->presentModeCount; i++) {
const Location present_mode_loc =
create_info_loc.pNext(Struct::VkSwapchainPresentModesCreateInfoEXT, Field::pPresentModes, i);
scaling_caps =
surface_state->GetPresentModeScalingCapabilities(physical_device, present_modes_ci->pPresentModes[i]);
if ((scaling_caps.supportedPresentScaling != 0) &&
(scaling_caps.supportedPresentScaling & pres_scale_ci->scalingBehavior) == 0) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07771", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::scalingBehavior),
"(%s) is not a valid present scaling benavior as returned in "
"VkSurfacePresentScalingCapabilitiesEXT::supportedPresentScaling for %s (%s).",
string_VkPresentScalingFlagsEXT(pres_scale_ci->scalingBehavior).c_str(),
present_mode_loc.Fields().c_str(),
string_VkPresentScalingFlagsEXT(scaling_caps.supportedPresentScaling).c_str())) {
skip |= true;
}
}
if ((scaling_caps.supportedPresentGravityX != 0) &&
(scaling_caps.supportedPresentGravityX & pres_scale_ci->presentGravityX) == 0) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07773", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityX),
"(%s) is not a valid x-axis present gravity as returned in "
"VkSurfacePresentScalingCapabilitiesEXT::supportedPresentGravityX for %s (%s).",
string_VkPresentGravityFlagsEXT(pres_scale_ci->presentGravityX).c_str(),
present_mode_loc.Fields().c_str(),
string_VkPresentGravityFlagsEXT(scaling_caps.supportedPresentGravityX).c_str())) {
skip |= true;
}
}
if ((scaling_caps.supportedPresentGravityY != 0) &&
(scaling_caps.supportedPresentGravityY & pres_scale_ci->presentGravityY) == 0) {
if (LogError("VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07775", device,
create_info_loc.pNext(Struct::VkSwapchainPresentScalingCreateInfoEXT, Field::presentGravityY),
"(%s) is not a valid y-axis present gravity as returned in "
"VkSurfacePresentScalingCapabilitiesEXT::supportedPresentGravityY for %s (%s).",
string_VkPresentGravityFlagsEXT(pres_scale_ci->presentGravityY).c_str(),
present_mode_loc.Fields().c_str(),
string_VkPresentGravityFlagsEXT(scaling_caps.supportedPresentGravityY).c_str())) {
skip |= true;
}
}
}
}
}
return skip;
}
bool CoreChecks::ValidateCreateSwapchain(VkSwapchainCreateInfoKHR const *pCreateInfo, const SURFACE_STATE *surface_state,
const SWAPCHAIN_NODE *old_swapchain_state, const Location &create_info_loc) const {
// All physical devices and queue families are required to be able to present to any native window on Android; require the
// application to have established support on any other platform.
if (!instance_extensions.vk_khr_android_surface) {
// restrict search only to queue families of VkDeviceQueueCreateInfos, not the whole physical device
const bool is_supported = AnyOf<QUEUE_STATE>([this, surface_state](const QUEUE_STATE &queue_state) {
return surface_state->GetQueueSupport(physical_device, queue_state.queueFamilyIndex);
});
if (!is_supported) {
const LogObjectList objlist(device, surface_state->Handle());
if (LogError("VUID-VkSwapchainCreateInfoKHR-surface-01270", objlist, create_info_loc.dot(Field::surface),
"is not supported for presentation by this device.")) {
return true;
}
}
}
if (old_swapchain_state) {
if (old_swapchain_state->createInfo.surface != pCreateInfo->surface) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933", pCreateInfo->oldSwapchain,
create_info_loc.dot(Field::oldSwapchain), "surface is not pCreateInfo->surface")) {
return true;
}
}
if (old_swapchain_state->retired) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933", pCreateInfo->oldSwapchain,
create_info_loc.dot(Field::oldSwapchain), "is retired")) {
return true;
}
}
}
if ((pCreateInfo->imageExtent.width == 0) || (pCreateInfo->imageExtent.height == 0)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageExtent-01689", device, create_info_loc.dot(Field::imageExtent),
"width (%d) and height (%d) is invalid.", pCreateInfo->imageExtent.width, pCreateInfo->imageExtent.height)) {
return true;
}
}
void *surface_caps_query_pnext = nullptr;
#if defined(VK_USE_PLATFORM_WIN32_KHR)
VkSurfaceFullScreenExclusiveInfoEXT full_screen_info_copy = vku::InitStructHelper();
VkSurfaceFullScreenExclusiveWin32InfoEXT win32_full_screen_info_copy = vku::InitStructHelper();
const auto *full_screen_info = vku::FindStructInPNextChain<VkSurfaceFullScreenExclusiveInfoEXT>(pCreateInfo->pNext);
if (full_screen_info && full_screen_info->fullScreenExclusive == VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) {
full_screen_info_copy = *full_screen_info;
full_screen_info_copy.pNext = surface_caps_query_pnext;
surface_caps_query_pnext = &full_screen_info_copy;
if (IsExtEnabled(device_extensions.vk_khr_win32_surface)) {
const auto *win32_full_screen_info = vku::FindStructInPNextChain<VkSurfaceFullScreenExclusiveWin32InfoEXT>(pCreateInfo->pNext);
if (!win32_full_screen_info) {
const LogObjectList objlist(device, pCreateInfo->surface);
if (LogError("VUID-VkSwapchainCreateInfoKHR-pNext-02679", objlist, create_info_loc,
"pNext chain contains "
"VkSurfaceFullScreenExclusiveInfoEXT, but does not contain "
"VkSurfaceFullScreenExclusiveWin32InfoEXT.")) {
return true;
}
} else {
win32_full_screen_info_copy = *win32_full_screen_info;
win32_full_screen_info_copy.pNext = surface_caps_query_pnext;
surface_caps_query_pnext = &win32_full_screen_info_copy;
}
}
}
#endif
VkSurfacePresentModeEXT present_mode_info = vku::InitStructHelper();
if (IsExtEnabled(device_extensions.vk_ext_surface_maintenance1)) {
present_mode_info.presentMode = pCreateInfo->presentMode;
present_mode_info.pNext = surface_caps_query_pnext;
surface_caps_query_pnext = &present_mode_info;
}
const auto surface_caps2 = surface_state->GetCapabilities(IsExtEnabled(instance_extensions.vk_khr_get_surface_capabilities2),
physical_device_state->PhysDev(), surface_caps_query_pnext, this);
bool skip = false;
VkSurfaceTransformFlagBitsKHR current_transform = surface_caps2.surfaceCapabilities.currentTransform;
if ((pCreateInfo->preTransform & current_transform) != pCreateInfo->preTransform) {
skip |= LogPerformanceWarning(kVUID_Core_Swapchain_PreTransform, physical_device, create_info_loc.dot(Field::preTransform),
"(%s) doesn't match the currentTransform (%s) returned by "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR, the presentation engine will transform the image "
"content as part of the presentation operation.",
string_VkSurfaceTransformFlagBitsKHR(pCreateInfo->preTransform),
string_VkSurfaceTransformFlagBitsKHR(current_transform));
}
const VkPresentModeKHR present_mode = pCreateInfo->presentMode;
const bool shared_present_mode = (VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR == present_mode ||
VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR == present_mode);
// Validate pCreateInfo->minImageCount against VkSurfaceCapabilitiesKHR::{min|max}ImageCount:
// Shared Present Mode must have a minImageCount of 1
if ((pCreateInfo->minImageCount < surface_caps2.surfaceCapabilities.minImageCount) && !shared_present_mode) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-presentMode-02839", device, create_info_loc.dot(Field::minImageCount),
"%" PRIu32 ", which is outside the bounds returned by "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR() (i.e. minImageCount = %d, maxImageCount = %d).",
pCreateInfo->minImageCount, surface_caps2.surfaceCapabilities.minImageCount,
surface_caps2.surfaceCapabilities.maxImageCount)) {
return true;
}
}
if ((surface_caps2.surfaceCapabilities.maxImageCount > 0) &&
(pCreateInfo->minImageCount > surface_caps2.surfaceCapabilities.maxImageCount)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-minImageCount-01272", device, create_info_loc.dot(Field::minImageCount),
"%" PRIu32 ", which is outside the bounds returned by "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR() (i.e. minImageCount = %d, maxImageCount = %d).",
pCreateInfo->minImageCount, surface_caps2.surfaceCapabilities.minImageCount,
surface_caps2.surfaceCapabilities.maxImageCount)) {
return true;
}
}
// pCreateInfo->preTransform should have exactly one bit set, and that bit must also be set in
// VkSurfaceCapabilitiesKHR::supportedTransforms.
if (!pCreateInfo->preTransform || (pCreateInfo->preTransform & (pCreateInfo->preTransform - 1)) ||
!(pCreateInfo->preTransform & surface_caps2.surfaceCapabilities.supportedTransforms)) {
std::stringstream ss;
for (int i = 0; i < 32; i++) {
if ((1 << i) & surface_caps2.surfaceCapabilities.supportedTransforms) {
ss << " " << string_VkSurfaceTransformFlagBitsKHR(static_cast<VkSurfaceTransformFlagBitsKHR>(1 << i)) << "%s\n";
}
}
return LogError("VUID-VkSwapchainCreateInfoKHR-preTransform-01279", device, create_info_loc.dot(Field::preTransform),
"is not supported, support values are:\n%s.", ss.str().c_str());
}
// pCreateInfo->compositeAlpha should have exactly one bit set, and that bit must also be set in
// VkSurfaceCapabilitiesKHR::supportedCompositeAlpha
if (!pCreateInfo->compositeAlpha || (pCreateInfo->compositeAlpha & (pCreateInfo->compositeAlpha - 1)) ||
!((pCreateInfo->compositeAlpha) & surface_caps2.surfaceCapabilities.supportedCompositeAlpha)) {
std::stringstream ss;
for (int i = 0; i < 32; i++) {
if ((1 << i) & surface_caps2.surfaceCapabilities.supportedCompositeAlpha) {
ss << " " << string_VkCompositeAlphaFlagBitsKHR(static_cast<VkCompositeAlphaFlagBitsKHR>(1 << i)) << "%s\n";
}
}
return LogError("VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280", device, create_info_loc.dot(Field::compositeAlpha),
"is not supported, support values are:\n%s.", ss.str().c_str());
}
// Validate pCreateInfo->imageArrayLayers against VkSurfaceCapabilitiesKHR::maxImageArrayLayers:
if (pCreateInfo->imageArrayLayers > surface_caps2.surfaceCapabilities.maxImageArrayLayers) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275", device, create_info_loc.dot(Field::imageArrayLayers),
"%" PRIu32 " is more than maxImageArrayLayers %" PRIu32 ".", pCreateInfo->imageArrayLayers,
surface_caps2.surfaceCapabilities.maxImageArrayLayers)) {
return true;
}
}
const VkImageUsageFlags image_usage = pCreateInfo->imageUsage;
// Validate pCreateInfo->imageUsage against VkSurfaceCapabilitiesKHR::supportedUsageFlags:
// Shared Present Mode uses different set of capabilities to check imageUsage support
if ((image_usage != (image_usage & surface_caps2.surfaceCapabilities.supportedUsageFlags)) && !shared_present_mode) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-presentMode-01427", device, create_info_loc.dot(Field::imageUsage),
"(0x%08x) are not in supportedUsageFlags (0x%08x).", image_usage,
surface_caps2.surfaceCapabilities.supportedUsageFlags)) {
return true;
}
}
if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) {
const bool is_required_ext_enabled = IsExtEnabled(instance_extensions.vk_khr_surface_protected_capabilities);
// Assume that the "protected" flag is not supported if VK_KHR_surface_protected_capabilities is not enabled
bool log_error = !is_required_ext_enabled;
if (is_required_ext_enabled) {
VkPhysicalDeviceSurfaceInfo2KHR surface_info = vku::InitStructHelper();
surface_info.surface = pCreateInfo->surface;
VkSurfaceProtectedCapabilitiesKHR surface_protected_capabilities = vku::InitStructHelper();
VkSurfaceCapabilities2KHR surface_capabilities =
vku::InitStructHelper(&surface_protected_capabilities);
const VkResult result = DispatchGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device_state->PhysDev(),
&surface_info, &surface_capabilities);
log_error = (result == VK_SUCCESS) && !surface_protected_capabilities.supportsProtected;
}
if (log_error) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-flags-03187", device, create_info_loc.dot(Field::flags),
"contains VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR but the surface "
"capabilities does not have VkSurfaceProtectedCapabilitiesKHR.supportsProtected set to VK_TRUE.")) {
return true;
}
}
}
// Validate pCreateInfo values with the results of vkGetPhysicalDeviceSurfaceFormats2KHR():
{
// Validate pCreateInfo->imageFormat against VkSurfaceFormatKHR::format:
bool found_format = false;
bool found_color_space = false;
bool found_match = false;
vvl::span<const safe_VkSurfaceFormat2KHR> formats{};
if (surface_state) {
formats = surface_state->GetFormats(IsExtEnabled(instance_extensions.vk_khr_get_surface_capabilities2),
physical_device_state->PhysDev(), surface_caps_query_pnext, this);
} else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) {
formats = physical_device_state->surfaceless_query_state.formats;
}
for (const auto &format : formats) {
if (pCreateInfo->imageFormat == format.surfaceFormat.format) {
// Validate pCreateInfo->imageColorSpace against VkSurfaceFormatKHR::colorSpace:
found_format = true;
if (pCreateInfo->imageColorSpace == format.surfaceFormat.colorSpace) {
found_match = true;
break;
}
} else {
if (pCreateInfo->imageColorSpace == format.surfaceFormat.colorSpace) {
found_color_space = true;
}
}
}
if (!found_match) {
if (!found_format) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01273", device, create_info_loc.dot(Field::imageFormat),
"is %s.", string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
}
if (!found_color_space) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01273", device, create_info_loc.dot(Field::imageColorSpace),
"is %s.", string_VkColorSpaceKHR(pCreateInfo->imageColorSpace))) {
return true;
}
}
}
}
if (!IsExtEnabled(device_extensions.vk_ext_swapchain_maintenance1)) {
// Validate pCreateInfo->imageExtent against VkSurfaceCapabilitiesKHR::{current|min|max}ImageExtent:
if (!IsExtentInsideBounds(pCreateInfo->imageExtent, surface_caps2.surfaceCapabilities.minImageExtent,
surface_caps2.surfaceCapabilities.maxImageExtent)) {
safe_VkSurfaceCapabilities2KHR cached_capabilities{};
if (surface_state) {
cached_capabilities =
surface_state->GetCapabilities(IsExtEnabled(instance_extensions.vk_khr_get_surface_capabilities2),
physical_device_state->PhysDev(), surface_caps_query_pnext, this);
} else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) {
cached_capabilities = physical_device_state->surfaceless_query_state.capabilities;
}
if (!IsExtentInsideBounds(pCreateInfo->imageExtent, cached_capabilities.surfaceCapabilities.minImageExtent,
cached_capabilities.surfaceCapabilities.maxImageExtent)) {
// TODO - Combine VUs with other same VUID
skip |= LogError(
"VUID-VkSwapchainCreateInfoKHR-pNext-07781", device, create_info_loc.dot(Field::imageExtent),
"(%" PRIu32 ", %" PRIu32
"), which is outside the bounds returned by "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): currentExtent = (%" PRIu32 ",%" PRIu32
"), minImageExtent = (%" PRIu32 ",%" PRIu32 "), maxImageExtent = (%" PRIu32 ",%" PRIu32 ").",
pCreateInfo->imageExtent.width, pCreateInfo->imageExtent.height,
surface_caps2.surfaceCapabilities.currentExtent.width, surface_caps2.surfaceCapabilities.currentExtent.height,
surface_caps2.surfaceCapabilities.minImageExtent.width, surface_caps2.surfaceCapabilities.minImageExtent.height,
surface_caps2.surfaceCapabilities.maxImageExtent.width,
surface_caps2.surfaceCapabilities.maxImageExtent.height);
}
}
} else {
skip |= ValidateSwapchainPresentModesCreateInfo(present_mode, create_info_loc, pCreateInfo, surface_state);
skip |= ValidateSwapchainPresentScalingCreateInfo(present_mode, create_info_loc, &surface_caps2.surfaceCapabilities,
pCreateInfo, surface_state);
}
// Validate state for shared presentable case
if (shared_present_mode) {
if (!IsExtEnabled(device_extensions.vk_khr_shared_presentable_image)) {
if (LogError(kVUID_Core_DrawState_ExtensionNotEnabled, device, create_info_loc,
"called with presentMode %s which requires the VK_KHR_shared_presentable_image extension, which has not "
"been enabled.",
string_VkPresentModeKHR(present_mode))) {
return true;
}
} else if (pCreateInfo->minImageCount != 1) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-minImageCount-01383", device, create_info_loc,
"called with presentMode %s, but minImageCount value is %d. For shared presentable image, minImageCount "
"must be 1.",
string_VkPresentModeKHR(present_mode), pCreateInfo->minImageCount)) {
return true;
}
}
VkSharedPresentSurfaceCapabilitiesKHR shared_present_capabilities = vku::InitStructHelper();
VkSurfaceCapabilities2KHR capabilities2 = vku::InitStructHelper(&shared_present_capabilities);
VkPhysicalDeviceSurfaceInfo2KHR surface_info = vku::InitStructHelper();
surface_info.surface = pCreateInfo->surface;
DispatchGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device_state->PhysDev(), &surface_info, &capabilities2);
if (image_usage != (image_usage & shared_present_capabilities.sharedPresentSupportedUsageFlags)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageUsage-01384", device, create_info_loc.dot(Field::imageUsage),
"(0x%08x), but the supported flag bits for %s "
"present mode are 0x%08x.",
image_usage, string_VkPresentModeKHR(pCreateInfo->presentMode),
shared_present_capabilities.sharedPresentSupportedUsageFlags)) {
return true;
}
}
}
if ((pCreateInfo->imageSharingMode == VK_SHARING_MODE_CONCURRENT) && pCreateInfo->pQueueFamilyIndices) {
bool skip1 = ValidatePhysicalDeviceQueueFamilies(pCreateInfo->queueFamilyIndexCount, pCreateInfo->pQueueFamilyIndices,
create_info_loc, "VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428");
if (skip1) return true;
}
// Validate pCreateInfo->imageUsage against GetPhysicalDeviceFormatProperties
const VkFormatProperties3KHR format_properties = GetPDFormatProperties(pCreateInfo->imageFormat);
const VkFormatFeatureFlags2KHR tiling_features = format_properties.optimalTilingFeatures;
if (tiling_features == 0) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageFormat),
"%s with tiling VK_IMAGE_TILING_OPTIMAL has no supported format features on this "
"physical device.",
string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
} else if ((image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(tiling_features & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageFormat),
"%s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes "
"VK_IMAGE_USAGE_SAMPLED_BIT.",
string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
} else if ((image_usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(tiling_features & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageFormat),
"%s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes "
"VK_IMAGE_USAGE_STORAGE_BIT.",
string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
} else if ((image_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) &&
!(tiling_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageFormat),
"%s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes "
"VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.",
string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
} else if ((image_usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) &&
!(tiling_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageFormat),
"%s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes "
"VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT.",
string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
} else if ((image_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) &&
!(tiling_features &
(VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR | VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR))) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageFormat),
"%s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes "
"VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT or VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT.",
string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
}
const VkImageCreateInfo image_create_info = GetSwapchainImpliedImageCreateInfo(pCreateInfo);
VkImageFormatProperties image_properties = {};
const VkResult image_properties_result = DispatchGetPhysicalDeviceImageFormatProperties(
physical_device, image_create_info.format, image_create_info.imageType, image_create_info.tiling, image_create_info.usage,
image_create_info.flags, &image_properties);
if (image_properties_result != VK_SUCCESS) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc,
"vkGetPhysicalDeviceImageFormatProperties() unexpectedly failed, "
"with following params: "
"format: %s, imageType: %s, "
"tiling: %s, usage: %s, "
"flags: %s.",
string_VkFormat(image_create_info.format), string_VkImageType(image_create_info.imageType),
string_VkImageTiling(image_create_info.tiling), string_VkImageUsageFlags(image_create_info.usage).c_str(),
string_VkImageCreateFlags(image_create_info.flags).c_str())) {
return true;
}
}
// Validate pCreateInfo->imageArrayLayers against VkImageFormatProperties::maxArrayLayers
if (pCreateInfo->imageArrayLayers > image_properties.maxArrayLayers) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageArrayLayers),
"%" PRIu32 ", but Maximum value returned by vkGetPhysicalDeviceImageFormatProperties() is %d "
"for imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL.",
pCreateInfo->imageArrayLayers, image_properties.maxArrayLayers, string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
}
// Validate pCreateInfo->imageExtent against VkImageFormatProperties::maxExtent
if ((pCreateInfo->imageExtent.width > image_properties.maxExtent.width) ||
(pCreateInfo->imageExtent.height > image_properties.maxExtent.height)) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", device, create_info_loc.dot(Field::imageExtent),
"(%d,%d), which is bigger than max extent (%d,%d)"
"returned by vkGetPhysicalDeviceImageFormatProperties(): "
"for imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL.",
pCreateInfo->imageExtent.width, pCreateInfo->imageExtent.height, image_properties.maxExtent.width,
image_properties.maxExtent.height, string_VkFormat(pCreateInfo->imageFormat))) {
return true;
}
}
if ((pCreateInfo->flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) && physical_device_count == 1) {
if (LogError("VUID-VkSwapchainCreateInfoKHR-physicalDeviceCount-01429", device, create_info_loc.dot(Field::flags),
"containing VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR"
"but logical device was created with VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1."
"The logical device may have been created without explicitly using VkDeviceGroupDeviceCreateInfo, or with"
"VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to zero. "
"It is equivalent to using VkDeviceGroupDeviceCreateInfo with "
"VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1.")) {
return true;
}
}
return skip;
}
bool CoreChecks::PreCallValidateCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain,
const ErrorObject &error_obj) const {
auto surface_state = Get<SURFACE_STATE>(pCreateInfo->surface);
auto old_swapchain_state = Get<SWAPCHAIN_NODE>(pCreateInfo->oldSwapchain);
return ValidateCreateSwapchain(pCreateInfo, surface_state.get(), old_swapchain_state.get(),
error_obj.location.dot(Field::pCreateInfo));
}
void CoreChecks::PreCallRecordDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain,
const VkAllocationCallbacks *pAllocator) {
if (swapchain) {
auto swapchain_data = Get<SWAPCHAIN_NODE>(swapchain);
if (swapchain_data) {
for (const auto &swapchain_image : swapchain_data->images) {
if (!swapchain_image.image_state) continue;
qfo_release_image_barrier_map.erase(swapchain_image.image_state->image());
}
}
}
StateTracker::PreCallRecordDestroySwapchainKHR(device, swapchain, pAllocator);
}
void CoreChecks::PostCallRecordGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount,
VkImage *pSwapchainImages, const RecordObject &record_obj) {
// This function will run twice. The first is to get pSwapchainImageCount. The second is to get pSwapchainImages.
// The first time in StateTracker::PostCallRecordGetSwapchainImagesKHR only generates the container's size.
// The second time in StateTracker::PostCallRecordGetSwapchainImagesKHR will create VKImage and IMAGE_STATE.
// So GlobalImageLayoutMap saving new IMAGE_STATEs has to run in the second time.
// pSwapchainImages is not nullptr and it needs to wait until StateTracker::PostCallRecordGetSwapchainImagesKHR.
uint32_t new_swapchain_image_index = 0;
if (((record_obj.result == VK_SUCCESS) || (record_obj.result == VK_INCOMPLETE)) && pSwapchainImages) {
auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain);
const auto image_vector_size = swapchain_state->images.size();
for (; new_swapchain_image_index < *pSwapchainImageCount; ++new_swapchain_image_index) {
if ((new_swapchain_image_index >= image_vector_size) ||
!swapchain_state->images[new_swapchain_image_index].image_state) {
break;
}
}
}
StateTracker::PostCallRecordGetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages, record_obj);
if (((record_obj.result == VK_SUCCESS) || (record_obj.result == VK_INCOMPLETE)) && pSwapchainImages) {
for (; new_swapchain_image_index < *pSwapchainImageCount; ++new_swapchain_image_index) {
auto image_state = Get<IMAGE_STATE>(pSwapchainImages[new_swapchain_image_index]);
image_state->SetInitialLayoutMap();
}
}
}
bool CoreChecks::PreCallValidateQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo,
const ErrorObject &error_obj) const {
bool skip = false;
auto queue_state = Get<QUEUE_STATE>(queue);
SemaphoreSubmitState sem_submit_state(this, queue,
physical_device_state->queue_family_properties[queue_state->queueFamilyIndex].queueFlags);
const Location present_info_loc = error_obj.location.dot(Struct::VkPresentInfoKHR, Field::pPresentInfo);
for (uint32_t i = 0; i < pPresentInfo->waitSemaphoreCount; ++i) {
auto semaphore_state = Get<SEMAPHORE_STATE>(pPresentInfo->pWaitSemaphores[i]);
if (semaphore_state && semaphore_state->type != VK_SEMAPHORE_TYPE_BINARY) {
skip |= LogError("VUID-vkQueuePresentKHR-pWaitSemaphores-03267", pPresentInfo->pWaitSemaphores[i],
present_info_loc.dot(Field::pWaitSemaphores, i), "(%s) is not a VK_SEMAPHORE_TYPE_BINARY",
FormatHandle(pPresentInfo->pWaitSemaphores[i]).c_str());
continue;
}
skip |= sem_submit_state.ValidateWaitSemaphore(present_info_loc.dot(Field::pWaitSemaphores, i),
pPresentInfo->pWaitSemaphores[i], 0);
}
for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) {
auto swapchain_data = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]);
if (swapchain_data) {
const Location swapchain_loc = present_info_loc.dot(Field::pSwapchains, i);
// Check if index is even possible to be acquired to give better error message
if (pPresentInfo->pImageIndices[i] >= swapchain_data->images.size()) {
skip |= LogError("VUID-VkPresentInfoKHR-pImageIndices-01430", pPresentInfo->pSwapchains[i], swapchain_loc,
"image index is too large (%" PRIu32 "), There are only %" PRIu32 " images in this swapchain.",
pPresentInfo->pImageIndices[i], static_cast<uint32_t>(swapchain_data->images.size()));
} else if (!swapchain_data->images[pPresentInfo->pImageIndices[i]].image_state ||
!swapchain_data->images[pPresentInfo->pImageIndices[i]].acquired) {
skip |= LogError("VUID-VkPresentInfoKHR-pImageIndices-01430", pPresentInfo->pSwapchains[i], swapchain_loc,
"image at index %" PRIu32 " was not acquired from the swapchain.", pPresentInfo->pImageIndices[i]);
} else {
const auto *image_state = swapchain_data->images[pPresentInfo->pImageIndices[i]].image_state;
assert(image_state);
std::vector<VkImageLayout> layouts;
if (FindLayouts(*image_state, layouts)) {
for (auto layout : layouts) {
if ((layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) &&
(!IsExtEnabled(device_extensions.vk_khr_shared_presentable_image) ||
(layout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR))) {
skip |= LogError("VUID-VkPresentInfoKHR-pImageIndices-01430", queue, swapchain_loc,
"images passed to present must be in layout "
"VK_IMAGE_LAYOUT_PRESENT_SRC_KHR or "
"VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but is in %s.",
string_VkImageLayout(layout));
}
}
}
const auto *display_present_info = vku::FindStructInPNextChain<VkDisplayPresentInfoKHR>(pPresentInfo->pNext);
if (display_present_info) {
if (display_present_info->srcRect.offset.x < 0 || display_present_info->srcRect.offset.y < 0 ||
display_present_info->srcRect.offset.x + display_present_info->srcRect.extent.width >
image_state->createInfo.extent.width ||
display_present_info->srcRect.offset.y + display_present_info->srcRect.extent.height >
image_state->createInfo.extent.height) {
skip |= LogError("VUID-VkDisplayPresentInfoKHR-srcRect-01257", queue, swapchain_loc,
"vkQueuePresentKHR(): VkDisplayPresentInfoKHR::srcRect (offset (%" PRIu32 ", %" PRIu32
"), extent (%" PRIu32 ", %" PRIu32
")) in the pNext chain of VkPresentInfoKHR is not a subset of the image begin presented "
"(extent (%" PRIu32 ", %" PRIu32 ")).",
display_present_info->srcRect.offset.x, display_present_info->srcRect.offset.y,
display_present_info->srcRect.extent.width, display_present_info->srcRect.extent.height,
image_state->createInfo.extent.width, image_state->createInfo.extent.height);
}
}
}
// All physical devices and queue families are required to be able to present to any native window on Android
if (!instance_extensions.vk_khr_android_surface) {
auto surface_state = Get<SURFACE_STATE>(swapchain_data->createInfo.surface);
if (!surface_state->GetQueueSupport(physical_device, queue_state->queueFamilyIndex)) {
skip |= LogError("VUID-vkQueuePresentKHR-pSwapchains-01292", pPresentInfo->pSwapchains[i], swapchain_loc,
"image on queue that cannot present to this surface.");
}
}
}
}
if (pPresentInfo->pNext) {
// Verify ext struct
const auto *present_regions = vku::FindStructInPNextChain<VkPresentRegionsKHR>(pPresentInfo->pNext);
if (present_regions) {
for (uint32_t i = 0; i < present_regions->swapchainCount; ++i) {
auto swapchain_data = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]);
assert(swapchain_data);
VkPresentRegionKHR region = present_regions->pRegions[i];
const Location region_loc = present_info_loc.pNext(Struct::VkPresentRegionsKHR, Field::pRegions, i);
for (uint32_t j = 0; j < region.rectangleCount; ++j) {
const Location rect_loc = region_loc.dot(Field::pRectangles, j);
VkRectLayerKHR rect = region.pRectangles[j];
// Swap offsets and extents for 90 or 270 degree preTransform rotation
if (swapchain_data->createInfo.preTransform &
(VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR)) {
std::swap(rect.offset.x, rect.offset.y);
std::swap(rect.extent.width, rect.extent.height);
}
if ((rect.offset.x + rect.extent.width) > swapchain_data->createInfo.imageExtent.width) {
skip |= LogError("VUID-VkRectLayerKHR-offset-04864", pPresentInfo->pSwapchains[i], rect_loc,
"sum of offset.x (%" PRId32 ") and extent.width (%" PRIu32
") after applying preTransform (%s) is greater "
"than the corresponding swapchain's imageExtent.width (%" PRIu32 ").",
rect.offset.x, rect.extent.width,
string_VkSurfaceTransformFlagBitsKHR(swapchain_data->createInfo.preTransform),
swapchain_data->createInfo.imageExtent.width);
}
if ((rect.offset.y + rect.extent.height) > swapchain_data->createInfo.imageExtent.height) {
skip |= LogError("VUID-VkRectLayerKHR-offset-04864", pPresentInfo->pSwapchains[i], rect_loc,
"sum of offset.y (%" PRId32 ") and extent.height (%" PRIu32
") after applying preTransform (%s) is greater "
"than the corresponding swapchain's imageExtent.height (%" PRIu32 ").",
rect.offset.y, rect.extent.height,
string_VkSurfaceTransformFlagBitsKHR(swapchain_data->createInfo.preTransform),
swapchain_data->createInfo.imageExtent.height);
}
if (rect.layer > swapchain_data->createInfo.imageArrayLayers) {
skip |= LogError(
"VUID-VkRectLayerKHR-layer-01262", pPresentInfo->pSwapchains[i], rect_loc.dot(Field::layer),
"layer (%" PRIu32 ") is greater than the corresponding swapchain's imageArrayLayers (%" PRIu32 ").",
rect.layer, swapchain_data->createInfo.imageArrayLayers);
}
}
}
}
const auto *present_times_info = vku::FindStructInPNextChain<VkPresentTimesInfoGOOGLE>(pPresentInfo->pNext);
if (present_times_info) {
if (pPresentInfo->swapchainCount != present_times_info->swapchainCount) {
skip |= LogError("VUID-VkPresentTimesInfoGOOGLE-swapchainCount-01247", pPresentInfo->pSwapchains[0],
present_info_loc.pNext(Struct::VkPresentTimesInfoGOOGLE, Field::swapchainCount),
"(%" PRIu32 ") is not equal to pPresentInfo->swapchainCount (%" PRIu32 ").",
present_times_info->swapchainCount, pPresentInfo->swapchainCount);
}
}
const auto *present_id_info = vku::FindStructInPNextChain<VkPresentIdKHR>(pPresentInfo->pNext);
if (present_id_info) {
if (!enabled_features.present_id_features.presentId) {
for (uint32_t i = 0; i < present_id_info->swapchainCount; i++) {
if (present_id_info->pPresentIds[i] != 0) {
skip |= LogError("VUID-VkPresentInfoKHR-pNext-06235", pPresentInfo->pSwapchains[0],
present_info_loc.pNext(Struct::VkPresentIdKHR, Field::pPresentIds, i),
"%" PRIu64 " is not NULL but presentId feature is not enabled.",
present_id_info->pPresentIds[i]);
}
}
}
if (pPresentInfo->swapchainCount != present_id_info->swapchainCount) {
skip |= LogError("VUID-VkPresentIdKHR-swapchainCount-04998", pPresentInfo->pSwapchains[0],
present_info_loc.pNext(Struct::VkPresentIdKHR, Field::swapchainCount),
"(%" PRIu32 ") is not equal to pPresentInfo->swapchainCount (%" PRIu32 ").",
present_id_info->swapchainCount, pPresentInfo->swapchainCount);
}
for (uint32_t i = 0; i < present_id_info->swapchainCount; i++) {
auto swapchain_state = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]);
if ((present_id_info->pPresentIds[i] != 0) &&
(present_id_info->pPresentIds[i] <= swapchain_state->max_present_id)) {
skip |= LogError("VUID-VkPresentIdKHR-presentIds-04999", pPresentInfo->pSwapchains[i],
present_info_loc.pNext(Struct::VkPresentIdKHR, Field::pPresentIds, i),
"%" PRIu64 " and the largest presentId sent for this swapchain is %" PRIu64
". Each presentIds entry must be greater than any previous presentIds entry passed for the "
"associated pSwapchains entry",
present_id_info->pPresentIds[i], swapchain_state->max_present_id);
}
}
}
const auto *swapchain_present_fence_info = vku::FindStructInPNextChain<VkSwapchainPresentFenceInfoEXT>(pPresentInfo->pNext);
if (swapchain_present_fence_info) {
if (pPresentInfo->swapchainCount != swapchain_present_fence_info->swapchainCount) {
skip |= LogError("VUID-VkSwapchainPresentFenceInfoEXT-swapchainCount-07757", pPresentInfo->pSwapchains[0],
present_info_loc.pNext(Struct::VkSwapchainPresentFenceInfoEXT, Field::swapchainCount),
"(%" PRIu32 ") is not equal to pPresentInfo->swapchainCount (%" PRIu32 ").",
swapchain_present_fence_info->swapchainCount, pPresentInfo->swapchainCount);
}
for (uint32_t i = 0; i < swapchain_present_fence_info->swapchainCount; i++) {
if (swapchain_present_fence_info->pFences[i]) {
const auto fence_state = Get<FENCE_STATE>(swapchain_present_fence_info->pFences[i]);
const LogObjectList objlist(queue, swapchain_present_fence_info->pFences[i]);
skip |=
ValidateFenceForSubmit(fence_state.get(), "VUID-VkSwapchainPresentFenceInfoEXT-pFences-07759",
"VUID-VkSwapchainPresentFenceInfoEXT-pFences-07758", objlist,
present_info_loc.pNext(Struct::VkSwapchainPresentFenceInfoEXT, Field::pFences, i));
}
}
}
const auto *swapchain_present_mode_info = vku::FindStructInPNextChain<VkSwapchainPresentModeInfoEXT>(pPresentInfo->pNext);
if (swapchain_present_mode_info) {
if (pPresentInfo->swapchainCount != swapchain_present_mode_info->swapchainCount) {
skip |= LogError("VUID-VkSwapchainPresentModeInfoEXT-swapchainCount-07760", pPresentInfo->pSwapchains[0],
present_info_loc.pNext(Struct::VkSwapchainPresentModeInfoEXT, Field::swapchainCount),
"(%" PRIu32 ") is not equal to pPresentInfo->swapchainCount (%" PRIu32 ").",
swapchain_present_mode_info->swapchainCount, pPresentInfo->swapchainCount);
}
for (uint32_t i = 0; i < swapchain_present_mode_info->swapchainCount; i++) {
const VkPresentModeKHR present_mode = swapchain_present_mode_info->pPresentModes[i];
const auto swapchain_state = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]);
if (!swapchain_state->present_modes.empty()) {
bool found_match = std::find(swapchain_state->present_modes.begin(), swapchain_state->present_modes.end(),
present_mode) != swapchain_state->present_modes.end();
if (!found_match) {
skip |= LogError("VUID-VkSwapchainPresentModeInfoEXT-pPresentModes-07761", pPresentInfo->pSwapchains[i],
present_info_loc.pNext(Struct::VkSwapchainPresentModeInfoEXT, Field::presentMode),
"(%s) that was not specified in a VkSwapchainPresentModesCreateInfoEXT "
"structure extending VkCreateSwapchainsKHR.",
string_VkPresentModeKHR(present_mode));
}
} else {
skip |= LogError("VUID-VkSwapchainPresentModeInfoEXT-pPresentModes-07761", pPresentInfo->pSwapchains[i],
present_info_loc.pNext(Struct::VkSwapchainPresentModeInfoEXT, Field::presentMode),
"(%s), but a VkSwapchainPresentModesCreateInfoEXT structure was not included in the "
"pNext chain of VkCreateSwapchainsKHR.",
string_VkPresentModeKHR(present_mode));
}
}
}
}
return skip;
}
bool CoreChecks::PreCallValidateReleaseSwapchainImagesEXT(VkDevice device, const VkReleaseSwapchainImagesInfoEXT *pReleaseInfo,
const ErrorObject &error_obj) const {
bool skip = false;
bool image_in_use = false;
auto swapchain_state = Get<SWAPCHAIN_NODE>(pReleaseInfo->swapchain);
if (swapchain_state) {
const Location release_info_loc = error_obj.location.dot(Field::pReleaseInfo);
for (uint32_t i = 0; i < pReleaseInfo->imageIndexCount; i++) {
if (pReleaseInfo->pImageIndices[i] >= swapchain_state->images.size()) {
skip |= LogError("VUID-VkReleaseSwapchainImagesInfoEXT-pImageIndices-07785", pReleaseInfo->swapchain,
release_info_loc.dot(Field::pImageIndices, i),
"%" PRIu32 " is too large, there are only %" PRIu32 " images in this swapchain.",
pReleaseInfo->pImageIndices[i], static_cast<uint32_t>(swapchain_state->images.size()));
} else if (!swapchain_state->images[pReleaseInfo->pImageIndices[i]].image_state ||
!swapchain_state->images[pReleaseInfo->pImageIndices[i]].acquired) {
skip |= LogError("VUID-VkReleaseSwapchainImagesInfoEXT-pImageIndices-07785", pReleaseInfo->swapchain,
release_info_loc.dot(Field::pImageIndices, i), "%" PRIu32 " was not acquired from the swapchain.",
pReleaseInfo->pImageIndices[i]);
}
if (swapchain_state->images[i].image_state->InUse()) {
image_in_use = true;
}
}
if (image_in_use) {
skip |= LogError("VUID-VkReleaseSwapchainImagesInfoEXT-pImageIndices-07786", pReleaseInfo->swapchain, release_info_loc,
"One or more of the images in this swapchain is still in use.");
}
}
return skip;
}
bool CoreChecks::PreCallValidateCreateSharedSwapchainsKHR(VkDevice device, uint32_t swapchainCount,
const VkSwapchainCreateInfoKHR *pCreateInfos,
const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchains,
const ErrorObject &error_obj) const {
bool skip = false;
if (pCreateInfos) {
for (uint32_t i = 0; i < swapchainCount; i++) {
auto surface_state = Get<SURFACE_STATE>(pCreateInfos[i].surface);
auto old_swapchain_state = Get<SWAPCHAIN_NODE>(pCreateInfos[i].oldSwapchain);
skip |= ValidateCreateSwapchain(&pCreateInfos[i], surface_state.get(), old_swapchain_state.get(),
error_obj.location.dot(Field::pCreateInfos, i));
}
}
return skip;
}
bool CoreChecks::ValidateAcquireNextImage(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore,
VkFence fence, uint32_t *pImageIndex, const Location &loc,
const char *semaphore_type_vuid) const {
bool skip = false;
const bool version_2 = loc.function != Func::vkAcquireNextImageKHR;
auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore);
if (semaphore_state) {
if (semaphore_state->type != VK_SEMAPHORE_TYPE_BINARY) {
skip |= LogError(semaphore_type_vuid, semaphore, loc, "%s is not a VK_SEMAPHORE_TYPE_BINARY.",
FormatHandle(semaphore).c_str());
} else if (semaphore_state->Scope() == kSyncScopeInternal) {
// TODO: VUIDs 01779 and 01781 cover the case where there are pending wait or signal operations on the
// semaphore. But we don't currently have a good enough way to track when acquire & present operations
// are completed. So it is possible to get in a condition where the semaphore is doing
// acquire / wait / acquire and the first acquire (and thus the wait) have completed, but our state
// isn't aware of it yet. This results in MANY false positives.
if (!semaphore_state->CanBeSignaled()) {
const char *vuid =
version_2 ? "VUID-VkAcquireNextImageInfoKHR-semaphore-01288" : "VUID-vkAcquireNextImageKHR-semaphore-01286";
skip |= LogError(vuid, semaphore, loc, "Semaphore must not be currently signaled.");
}
}
}
auto fence_state = Get<FENCE_STATE>(fence);
if (fence_state) {
const LogObjectList objlist(device, fence);
skip |= ValidateFenceForSubmit(fence_state.get(), "VUID-vkAcquireNextImageKHR-fence-01287",
"VUID-vkAcquireNextImageKHR-fence-01287", objlist, loc);
}
auto swapchain_data = Get<SWAPCHAIN_NODE>(swapchain);
if (swapchain_data) {
if (swapchain_data->retired) {
const char *vuid =
version_2 ? "VUID-VkAcquireNextImageInfoKHR-swapchain-01675" : "VUID-vkAcquireNextImageKHR-swapchain-01285";
skip |= LogError(vuid, swapchain, loc,
"This swapchain has been retired. The application can still present any images it "
"has acquired, but cannot acquire any more.");
}
const uint32_t acquired_images = swapchain_data->acquired_images;
const uint32_t swapchain_image_count = static_cast<uint32_t>(swapchain_data->images.size());
safe_VkSurfaceCapabilities2KHR surface_caps2{};
if (swapchain_data->surface) {
surface_caps2 = swapchain_data->surface->GetCapabilities(
IsExtEnabled(device_extensions.vk_khr_get_surface_capabilities2), physical_device, nullptr, this);
} else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) {
surface_caps2 = physical_device_state->surfaceless_query_state.capabilities;
}
auto min_image_count = surface_caps2.surfaceCapabilities.minImageCount;
const VkSwapchainPresentModesCreateInfoEXT *present_modes_ci =
vku::FindStructInPNextChain<VkSwapchainPresentModesCreateInfoEXT>(swapchain_data->createInfo.pNext);
if (present_modes_ci) {
auto surface_state = Get<SURFACE_STATE>(swapchain_data->createInfo.surface);
// If a SwapchainPresentModesCreateInfo struct was included, min_image_count becomes the max of the
// minImageCount values returned via VkSurfaceCapabilitiesKHR for each of the present modes in
// SwapchainPresentModesCreateInfo
VkSurfaceCapabilitiesKHR surface_capabilities{};
min_image_count = 0;
for (uint32_t i = 0; i < present_modes_ci->presentModeCount; i++) {
surface_capabilities =
surface_state->GetPresentModeSurfaceCapabilities(physical_device, present_modes_ci->pPresentModes[i]);
if (surface_capabilities.minImageCount > min_image_count) {
min_image_count = surface_capabilities.minImageCount;
}
}
}
const bool too_many_already_acquired = acquired_images > swapchain_image_count - min_image_count;
if (timeout == vvl::kU64Max && too_many_already_acquired) {
const char *vuid = version_2 ? "VUID-vkAcquireNextImage2KHR-surface-07784" : "VUID-vkAcquireNextImageKHR-surface-07783";
const uint32_t acquirable = swapchain_image_count - min_image_count + 1;
skip |= LogError(vuid, swapchain, loc,
"Application has already previously acquired %" PRIu32 " image%s from swapchain. Only %" PRIu32
" %s available to be acquired using a timeout of UINT64_MAX (given the swapchain has %" PRIu32
", and VkSurfaceCapabilitiesKHR::minImageCount is %" PRIu32 ").",
acquired_images, acquired_images > 1 ? "s" : "", acquirable, acquirable > 1 ? "are" : "is",
swapchain_image_count, min_image_count);
}
}
return skip;
}
bool CoreChecks::PreCallValidateAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout,
VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex,
const ErrorObject &error_obj) const {
return ValidateAcquireNextImage(device, swapchain, timeout, semaphore, fence, pImageIndex, error_obj.location,
"VUID-vkAcquireNextImageKHR-semaphore-03265");
}
bool CoreChecks::PreCallValidateAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR *pAcquireInfo,
uint32_t *pImageIndex, const ErrorObject &error_obj) const {
bool skip = false;
const LogObjectList objlist(pAcquireInfo->swapchain);
const Location acquire_info_loc = error_obj.location.dot(Field::pAcquireInfo);
skip |= ValidateDeviceMaskToPhysicalDeviceCount(pAcquireInfo->deviceMask, objlist, acquire_info_loc.dot(Field::deviceMask),
"VUID-VkAcquireNextImageInfoKHR-deviceMask-01290");
skip |= ValidateDeviceMaskToZero(pAcquireInfo->deviceMask, objlist, acquire_info_loc.dot(Field::deviceMask),
"VUID-VkAcquireNextImageInfoKHR-deviceMask-01291");
skip |= ValidateAcquireNextImage(device, pAcquireInfo->swapchain, pAcquireInfo->timeout, pAcquireInfo->semaphore,
pAcquireInfo->fence, pImageIndex, error_obj.location,
"VUID-VkAcquireNextImageInfoKHR-semaphore-03266");
return skip;
}
bool CoreChecks::PreCallValidateWaitForPresentKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t presentId, uint64_t timeout,
const ErrorObject &error_obj) const {
bool skip = false;
if (!enabled_features.present_wait_features.presentWait) {
skip |= LogError("VUID-vkWaitForPresentKHR-presentWait-06234", swapchain, error_obj.location,
"presentWait feature is not enabled.");
}
auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain);
if (swapchain_state) {
if (swapchain_state->retired) {
skip |= LogError("VUID-vkWaitForPresentKHR-swapchain-04997", swapchain, error_obj.location,
"called with a retired swapchain.");
}
}
return skip;
}
bool CoreChecks::PreCallValidateDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,
const VkAllocationCallbacks *pAllocator, const ErrorObject &error_obj) const {
auto surface_state = Get<SURFACE_STATE>(surface);
bool skip = false;
if ((surface_state) && (surface_state->swapchain)) {
skip |= LogError("VUID-vkDestroySurfaceKHR-surface-01266", instance, error_obj.location,
"called before its associated VkSwapchainKHR was destroyed.");
}
return skip;
}
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool CoreChecks::PreCallValidateGetPhysicalDeviceWaylandPresentationSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
struct wl_display *display,
const ErrorObject &error_obj) const {
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex,
"VUID-vkGetPhysicalDeviceWaylandPresentationSupportKHR-queueFamilyIndex-01306",
error_obj.location.dot(Field::queueFamilyIndex));
}
#endif // VK_USE_PLATFORM_WAYLAND_KHR
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool CoreChecks::PreCallValidateGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
const ErrorObject &error_obj) const {
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex,
"VUID-vkGetPhysicalDeviceWin32PresentationSupportKHR-queueFamilyIndex-01309",
error_obj.location.dot(Field::queueFamilyIndex));
}
#endif // VK_USE_PLATFORM_WIN32_KHR
#ifdef VK_USE_PLATFORM_XCB_KHR
bool CoreChecks::PreCallValidateGetPhysicalDeviceXcbPresentationSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex, xcb_connection_t *connection,
xcb_visualid_t visual_id,
const ErrorObject &error_obj) const {
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex,
"VUID-vkGetPhysicalDeviceXcbPresentationSupportKHR-queueFamilyIndex-01312",
error_obj.location.dot(Field::queueFamilyIndex));
}
#endif // VK_USE_PLATFORM_XCB_KHR
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool CoreChecks::PreCallValidateGetPhysicalDeviceXlibPresentationSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex, Display *dpy,
VisualID visualID, const ErrorObject &error_obj) const {
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex,
"VUID-vkGetPhysicalDeviceXlibPresentationSupportKHR-queueFamilyIndex-01315",
error_obj.location.dot(Field::queueFamilyIndex));
}
#endif // VK_USE_PLATFORM_XLIB_KHR
#ifdef VK_USE_PLATFORM_SCREEN_QNX
bool CoreChecks::PreCallValidateGetPhysicalDeviceScreenPresentationSupportQNX(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
struct _screen_window *window,
const ErrorObject &error_obj) const {
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex,
"VUID-vkGetPhysicalDeviceScreenPresentationSupportQNX-queueFamilyIndex-04743",
error_obj.location.dot(Field::queueFamilyIndex));
}
#endif // VK_USE_PLATFORM_SCREEN_QNX
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex,
VkSurfaceKHR surface, VkBool32 *pSupported,
const ErrorObject &error_obj) const {
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex,
"VUID-vkGetPhysicalDeviceSurfaceSupportKHR-queueFamilyIndex-01269",
error_obj.location.dot(Field::queueFamilyIndex));
}
bool CoreChecks::PreCallValidateGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice, uint32_t planeIndex,
uint32_t *pDisplayCount, VkDisplayKHR *pDisplays,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, planeIndex,
error_obj.location.dot(Field::planeIndex));
return skip;
}
bool CoreChecks::PreCallValidateGetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode,
uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR *pCapabilities,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, planeIndex,
error_obj.location.dot(Field::planeIndex));
return skip;
}
bool CoreChecks::PreCallValidateGetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physicalDevice,
const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
VkDisplayPlaneCapabilities2KHR *pCapabilities,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(
physicalDevice, pDisplayPlaneInfo->planeIndex, error_obj.location.dot(Field::pDisplayPlaneInfo).dot(Field::planeIndex));
return skip;
}
bool CoreChecks::PreCallValidateCreateDisplayPlaneSurfaceKHR(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface,
const ErrorObject &error_obj) const {
bool skip = false;
const VkDisplayModeKHR display_mode = pCreateInfo->displayMode;
const uint32_t plane_index = pCreateInfo->planeIndex;
const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo);
if (pCreateInfo->alphaMode == VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR) {
const float global_alpha = pCreateInfo->globalAlpha;
if ((global_alpha > 1.0f) || (global_alpha < 0.0f)) {
skip |= LogError("VUID-VkDisplaySurfaceCreateInfoKHR-alphaMode-01254", display_mode,
create_info_loc.dot(Field::globalAlpha),
"is %f, but alphaMode is VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR.", global_alpha);
}
}
auto dm_state = Get<DISPLAY_MODE_STATE>(display_mode);
if (dm_state != nullptr) {
// Get physical device from VkDisplayModeKHR state tracking
const VkPhysicalDevice physical_device = dm_state->physical_device;
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physical_device);
VkPhysicalDeviceProperties device_properties = {};
DispatchGetPhysicalDeviceProperties(physical_device, &device_properties);
const uint32_t width = pCreateInfo->imageExtent.width;
const uint32_t height = pCreateInfo->imageExtent.height;
if (width >= device_properties.limits.maxImageDimension2D) {
skip |= LogError("VUID-VkDisplaySurfaceCreateInfoKHR-width-01256", display_mode,
create_info_loc.dot(Field::imageExtent).dot(Field::width),
"(%" PRIu32 ") exceeds device limit maxImageDimension2D (%" PRIu32 ").", width,
device_properties.limits.maxImageDimension2D);
}
if (height >= device_properties.limits.maxImageDimension2D) {
skip |= LogError("VUID-VkDisplaySurfaceCreateInfoKHR-width-01256", display_mode,
create_info_loc.dot(Field::imageExtent).dot(Field::height),
"(%" PRIu32 ") exceeds device limit maxImageDimension2D (%" PRIu32 ").", height,
device_properties.limits.maxImageDimension2D);
}
if (pd_state->vkGetPhysicalDeviceDisplayPlanePropertiesKHR_called) {
if (plane_index >= pd_state->display_plane_property_count) {
skip |= LogError("VUID-VkDisplaySurfaceCreateInfoKHR-planeIndex-01252", display_mode,
create_info_loc.dot(Field::planeIndex),
"(%" PRIu32 ") must be in the range [0, %" PRIu32
"] that was returned by "
"vkGetPhysicalDeviceDisplayPlanePropertiesKHR "
"or vkGetPhysicalDeviceDisplayPlaneProperties2KHR. Do you have the plane index hardcoded?",
plane_index, pd_state->display_plane_property_count - 1);
} else {
// call here once we know the plane index used is a valid plane index
VkDisplayPlaneCapabilitiesKHR plane_capabilities;
DispatchGetDisplayPlaneCapabilitiesKHR(physical_device, display_mode, plane_index, &plane_capabilities);
if ((pCreateInfo->alphaMode & plane_capabilities.supportedAlpha) == 0) {
skip |= LogError("VUID-VkDisplaySurfaceCreateInfoKHR-alphaMode-01255", display_mode, create_info_loc,
"alphaMode is %s but planeIndex %" PRIu32
" supportedAlpha (0x%x) "
"does not support the mode.",
string_VkDisplayPlaneAlphaFlagBitsKHR(pCreateInfo->alphaMode), plane_index,
plane_capabilities.supportedAlpha);
}
}
}
}
return skip;
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool CoreChecks::PreCallValidateAcquireFullScreenExclusiveModeEXT(VkDevice device, VkSwapchainKHR swapchain,
const ErrorObject &error_obj) const {
bool skip = false;
auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain);
if (swapchain_state) {
if (swapchain_state->retired) {
skip |= LogError("VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02674", device, error_obj.location,
"swapchain %s is retired.", FormatHandle(swapchain).c_str());
}
const auto *surface_full_screen_exclusive_info =
vku::FindStructInPNextChain<VkSurfaceFullScreenExclusiveInfoEXT>(swapchain_state->createInfo.pNext);
if (!surface_full_screen_exclusive_info ||
surface_full_screen_exclusive_info->fullScreenExclusive != VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) {
skip |=
LogError("VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02675", device, error_obj.location,
"swapchain %s was not created with VkSurfaceFullScreenExclusiveInfoEXT in "
"the pNext chain with fullScreenExclusive equal to VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT.",
FormatHandle(swapchain).c_str());
}
if (swapchain_state->exclusive_full_screen_access) {
skip |= LogError("VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02676", device, error_obj.location,
"swapchain %s already has exclusive full-screen access.", FormatHandle(swapchain).c_str());
}
}
return skip;
}
bool CoreChecks::PreCallValidateReleaseFullScreenExclusiveModeEXT(VkDevice device, VkSwapchainKHR swapchain,
const ErrorObject &error_obj) const {
bool skip = false;
const auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain);
if (swapchain_state) {
if (swapchain_state->retired) {
skip |= LogError("VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02677", device, error_obj.location,
"swapchain %s is retired.", FormatHandle(swapchain).c_str());
}
const auto *surface_full_screen_exclusive_info =
vku::FindStructInPNextChain<VkSurfaceFullScreenExclusiveInfoEXT>(swapchain_state->createInfo.pNext);
if (!surface_full_screen_exclusive_info ||
surface_full_screen_exclusive_info->fullScreenExclusive != VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) {
skip |=
LogError("VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02678", device, error_obj.location,
"swapchain %s was not created with VkSurfaceFullScreenExclusiveInfoEXT in "
"the pNext chain with fullScreenExclusive equal to VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT.",
FormatHandle(swapchain).c_str());
}
}
return skip;
}
#endif
bool CoreChecks::ValidatePhysicalDeviceSurfaceSupport(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, const char *vuid,
const Location &loc) const {
bool skip = false;
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
auto surface_state = Get<SURFACE_STATE>(surface);
if (pd_state && surface_state) {
bool is_supported = false;
for (uint32_t i = 0; i < pd_state->queue_family_properties.size(); i++) {
if (surface_state->GetQueueSupport(physicalDevice, i)) {
is_supported = true;
break;
}
}
if (!is_supported) {
skip |= LogError(vuid, physicalDevice, loc, "surface is not supported by the physicalDevice.");
}
}
return skip;
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool CoreChecks::PreCallValidateGetDeviceGroupSurfacePresentModes2EXT(VkDevice device,
const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
VkDeviceGroupPresentModeFlagsKHR *pModes,
const ErrorObject &error_obj) const {
bool skip = false;
if (physical_device_count == 1) {
ValidationObject *device_object = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
skip |= ValidatePhysicalDeviceSurfaceSupport(device_object->physical_device, pSurfaceInfo->surface,
"VUID-vkGetDeviceGroupSurfacePresentModes2EXT-pSurfaceInfo-06213",
error_obj.location);
} else {
for (uint32_t i = 0; i < physical_device_count; ++i) {
skip |= ValidatePhysicalDeviceSurfaceSupport(device_group_create_info.pPhysicalDevices[i], pSurfaceInfo->surface,
"VUID-vkGetDeviceGroupSurfacePresentModes2EXT-pSurfaceInfo-06213",
error_obj.location);
}
}
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfacePresentModes2EXT(VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
uint32_t *pPresentModeCount,
VkPresentModeKHR *pPresentModes,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, pSurfaceInfo->surface,
"VUID-vkGetPhysicalDeviceSurfacePresentModes2EXT-pSurfaceInfo-06522",
error_obj.location);
return skip;
}
#endif
bool CoreChecks::PreCallValidateGetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface,
VkDeviceGroupPresentModeFlagsKHR *pModes,
const ErrorObject &error_obj) const {
bool skip = false;
if (physical_device_count == 1) {
ValidationObject *device_object = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
skip |=
ValidatePhysicalDeviceSurfaceSupport(device_object->physical_device, surface,
"VUID-vkGetDeviceGroupSurfacePresentModesKHR-surface-06212", error_obj.location);
} else {
for (uint32_t i = 0; i < physical_device_count; ++i) {
skip |= ValidatePhysicalDeviceSurfaceSupport(device_group_create_info.pPhysicalDevices[i], surface,
"VUID-vkGetDeviceGroupSurfacePresentModesKHR-surface-06212",
error_obj.location);
}
}
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
uint32_t *pRectCount, VkRect2D *pRects,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface,
"VUID-vkGetPhysicalDevicePresentRectanglesKHR-surface-06211", error_obj.location);
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceCapabilities2EXT(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
VkSurfaceCapabilities2EXT *pSurfaceCapabilities,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(
physicalDevice, surface, "VUID-vkGetPhysicalDeviceSurfaceCapabilities2EXT-surface-06211", error_obj.location);
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
VkSurfaceCapabilities2KHR *pSurfaceCapabilities,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, pSurfaceInfo->surface,
"VUID-vkGetPhysicalDeviceSurfaceCapabilities2KHR-pSurfaceInfo-06522",
error_obj.location);
const auto surface_state = Get<SURFACE_STATE>(pSurfaceInfo->surface);
if (IsExtEnabled(device_extensions.vk_ext_surface_maintenance1)) {
const auto *surface_present_mode = vku::FindStructInPNextChain<VkSurfacePresentModeEXT>(pSurfaceInfo->pNext);
if (surface_present_mode) {
VkPresentModeKHR present_mode = surface_present_mode->presentMode;
std::vector<VkPresentModeKHR> present_modes{};
if (surface_state) {
present_modes = surface_state->GetPresentModes(physicalDevice, this);
}
bool found_match = std::find(present_modes.begin(), present_modes.end(), present_mode) != present_modes.end();
if (!found_match) {
skip |=
LogError("VUID-VkSurfacePresentModeEXT-presentMode-07780", device, error_obj.location,
"is called with VK_EXT_surface_maintenance1 enabled and "
"a VkSurfacePresentModeEXT structure included in "
"the pNext chain of VkPhysicalDeviceSurfaceInfo2KHR, but the specified presentMode (%s) is not among "
"those returned by vkGetPhysicalDevicePresentModesKHR().",
string_VkPresentModeKHR(present_mode));
}
}
}
#if defined(VK_USE_PLATFORM_WIN32_KHR)
if (IsExtEnabled(device_extensions.vk_khr_win32_surface) && IsExtEnabled(device_extensions.vk_ext_full_screen_exclusive)) {
if (surface_state) {
if (const auto *full_screen_info = vku::FindStructInPNextChain<VkSurfaceFullScreenExclusiveInfoEXT>(pSurfaceInfo->pNext);
full_screen_info && full_screen_info->fullScreenExclusive == VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) {
if (const auto *win32_full_screen_info =
vku::FindStructInPNextChain<VkSurfaceFullScreenExclusiveWin32InfoEXT>(pSurfaceInfo->pNext);
!win32_full_screen_info) {
const LogObjectList objlist(device, pSurfaceInfo->surface);
skip |= LogError("VUID-VkPhysicalDeviceSurfaceInfo2KHR-pNext-02672", objlist,
error_obj.location.dot(Field::pSurfaceInfo)
.pNext(Struct::VkSurfaceFullScreenExclusiveInfoEXT, Field::fullScreenExclusive),
"is VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT, but does not contain "
"a VkSurfaceFullScreenExclusiveWin32InfoEXT structure.");
}
}
}
}
#endif
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
VkSurfaceCapabilitiesKHR *pSurfaceCapabilities,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(
physicalDevice, surface, "VUID-vkGetPhysicalDeviceSurfaceCapabilitiesKHR-surface-06211", error_obj.location);
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
uint32_t *pSurfaceFormatCount,
VkSurfaceFormat2KHR *pSurfaceFormats,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(
physicalDevice, pSurfaceInfo->surface, "VUID-vkGetPhysicalDeviceSurfaceFormats2KHR-pSurfaceInfo-06522", error_obj.location);
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
uint32_t *pSurfaceFormatCount,
VkSurfaceFormatKHR *pSurfaceFormats,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface, "VUID-vkGetPhysicalDeviceSurfaceFormatsKHR-surface-06525",
error_obj.location);
return skip;
}
bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
uint32_t *pPresentModeCount,
VkPresentModeKHR *pPresentModes,
const ErrorObject &error_obj) const {
bool skip = false;
skip |= ValidatePhysicalDeviceSurfaceSupport(
physicalDevice, surface, "VUID-vkGetPhysicalDeviceSurfacePresentModesKHR-surface-06525", error_obj.location);
return skip;
}
bool CoreChecks::ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(VkPhysicalDevice physicalDevice, uint32_t planeIndex,
const Location &loc) const {
bool skip = false;
auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice);
if (pd_state->vkGetPhysicalDeviceDisplayPlanePropertiesKHR_called) {
if (planeIndex >= pd_state->display_plane_property_count) {
skip |= LogError("VUID-vkGetDisplayPlaneSupportedDisplaysKHR-planeIndex-01249", physicalDevice, loc,
"is %" PRIu32 ", but vkGetPhysicalDeviceDisplayPlaneProperties(2)KHR returned %" PRIu32
". (Do you have the plane index hardcoded?).",
planeIndex, pd_state->display_plane_property_count);
}
}
return skip;
}