blob: eda335e1d0c6ad44043415c3ef30fec9b32633cf [file]
/* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Modifications Copyright (C) 2020 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 "best_practices/best_practices_validation.h"
#include "best_practices/bp_state.h"
#include "generated/error_location_helper.h"
#include "state_tracker/device_state.h"
#include "state_tracker/wsi_state.h"
bool bp_state::Instance::ValidateGetPhysicalDeviceDisplayPlaneProperties(VkPhysicalDevice physicalDevice,
const Location& loc) const {
bool skip = false;
if (const auto bp_pd_state = Get<vvl::PhysicalDevice>(physicalDevice)) {
if (bp_pd_state->WasUncalled(vvl::Func::vkGetPhysicalDeviceDisplayPlanePropertiesKHR) &&
bp_pd_state->WasUncalled(vvl::Func::vkGetPhysicalDeviceDisplayPlaneProperties2KHR)) {
skip |= LogWarning("BestPractices-vkGetDisplayPlaneSupportedDisplaysKHR-properties-not-retrieved", physicalDevice, loc,
"was called without first retrieving properties from "
"vkGetPhysicalDeviceDisplayPlanePropertiesKHR or vkGetPhysicalDeviceDisplayPlaneProperties2KHR.");
}
}
return skip;
}
bool bp_state::Instance::PreCallValidateGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice, uint32_t planeIndex,
uint32_t* pDisplayCount, VkDisplayKHR* pDisplays,
const ErrorObject& error_obj) const {
bool skip = false;
skip |= ValidateGetPhysicalDeviceDisplayPlaneProperties(physicalDevice, error_obj.location);
return skip;
}
bool bp_state::Instance::PreCallValidateGetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode,
uint32_t planeIndex,
VkDisplayPlaneCapabilitiesKHR* pCapabilities,
const ErrorObject& error_obj) const {
bool skip = false;
skip |= ValidateGetPhysicalDeviceDisplayPlaneProperties(physicalDevice, error_obj.location);
return skip;
}
bool bp_state::Instance::PreCallValidateGetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physicalDevice,
const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo,
VkDisplayPlaneCapabilities2KHR* pCapabilities,
const ErrorObject& error_obj) const {
bool skip = false;
skip |= ValidateGetPhysicalDeviceDisplayPlaneProperties(physicalDevice, error_obj.location);
return skip;
}
bool BestPractices::PreCallValidateCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain,
const ErrorObject& error_obj) const {
bool skip = false;
if (physical_device_state->WasUncalled(vvl::Func::vkGetPhysicalDeviceSurfaceCapabilitiesKHR) &&
physical_device_state->WasUncalled(vvl::Func::vkGetPhysicalDeviceSurfaceCapabilities2EXT) &&
physical_device_state->WasUncalled(vvl::Func::vkGetPhysicalDeviceSurfaceCapabilities2KHR)) {
skip |= LogWarning("BestPractices-vkCreateSwapchainKHR-capabilities-no-surface", device, error_obj.location,
"called before getting surface capabilities from "
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR or vkGetPhysicalDeviceSurfaceCapabilities2EXT or "
"vkGetPhysicalDeviceSurfaceCapabilities2KHR");
}
if (pCreateInfo->presentMode != VK_PRESENT_MODE_FIFO_KHR &&
physical_device_state->GetCallState(vvl::Func::vkGetPhysicalDeviceSurfacePresentModesKHR) != vvl::CallState::QueryDetails) {
skip |= LogWarning("BestPractices-vkCreateSwapchainKHR-present-mode-no-surface", device, error_obj.location,
"called before getting surface present mode(s) from "
"vkGetPhysicalDeviceSurfacePresentModesKHR.");
}
if (physical_device_state->GetCallState(vvl::Func::vkGetPhysicalDeviceSurfaceFormatsKHR) != vvl::CallState::QueryDetails &&
physical_device_state->GetCallState(vvl::Func::vkGetPhysicalDeviceSurfaceFormats2KHR) != vvl::CallState::QueryDetails) {
skip |= LogWarning("BestPractices-vkCreateSwapchainKHR-formats-no-surface", device, error_obj.location,
"called before getting surface format(s) from vkGetPhysicalDeviceSurfaceFormatsKHR or "
"vkGetPhysicalDeviceSurfaceFormats2KHR.");
}
if ((pCreateInfo->queueFamilyIndexCount > 1) && (pCreateInfo->imageSharingMode == VK_SHARING_MODE_EXCLUSIVE)) {
skip |= LogWarning("BestPractices-vkCreateSwapchainKHR-sharing-mode-exclusive", device, error_obj.location,
"A Swapchain is being created which specifies a sharing mode of VK_SHARING_MODE_EXCLUSIVE while "
"specifying multiple queues (queueFamilyIndexCount of %" PRIu32 ").",
pCreateInfo->queueFamilyIndexCount);
}
const auto present_mode = pCreateInfo->presentMode;
if (((present_mode == VK_PRESENT_MODE_MAILBOX_KHR) || (present_mode == VK_PRESENT_MODE_FIFO_KHR) ||
(present_mode == VK_PRESENT_MODE_FIFO_LATEST_READY_KHR)) &&
(pCreateInfo->minImageCount == 2)) {
skip |= LogPerformanceWarning(
"BestPractices-vkCreateSwapchainKHR-suboptimal-swapchain-image-count", device, error_obj.location,
"A Swapchain is being created with minImageCount set to %" PRIu32
", which means double buffering is going "
"to be used. Using double buffering and vsync locks rendering to an integer fraction of the vsync rate. In turn, "
"reducing the performance of the application if rendering is slower than vsync. Consider setting minImageCount to "
"3 to use triple buffering to maximize performance in such cases.",
pCreateInfo->minImageCount);
}
if ((IsExtEnabled(extensions.vk_khr_swapchain_maintenance1) || IsExtEnabled(extensions.vk_ext_swapchain_maintenance1)) &&
!vku::FindStructInPNextChain<VkSwapchainPresentModesCreateInfoKHR>(pCreateInfo->pNext)) {
skip |= LogWarning("BestPractices-vkCreateSwapchainKHR-no-VkSwapchainPresentModesCreateInfoKHR-provided", device,
error_obj.location,
"No VkSwapchainPresentModesCreateInfoKHR was provided to VkCreateSwapchainKHR. "
"When VK_KHR_swapchain_maintenance1 is enabled, a VkSwapchainPresentModesCreateInfoKHR should "
"be provided to inform the implementation that the application is aware of the new features "
"in a backward compatible way.");
}
if (VendorCheckEnabled(kBPVendorArm) && (pCreateInfo->presentMode != VK_PRESENT_MODE_FIFO_KHR)) {
skip |= LogWarning("BestPractices-Arm-vkCreateSwapchainKHR-swapchain-presentmode-not-fifo", device, error_obj.location,
"%s Swapchain is not being created with presentation mode \"VK_PRESENT_MODE_FIFO_KHR\". "
"Prefer using \"VK_PRESENT_MODE_FIFO_KHR\" to avoid unnecessary CPU and GPU load and save power. "
"Presentation modes which are not FIFO will present the latest available frame and discard other "
"frame(s) if any.",
VendorSpecificTag(kBPVendorArm));
}
return skip;
}
bool BestPractices::PreCallValidateCreateSharedSwapchainsKHR(VkDevice device, uint32_t swapchainCount,
const VkSwapchainCreateInfoKHR* pCreateInfos,
const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains,
const ErrorObject& error_obj) const {
bool skip = false;
for (uint32_t i = 0; i < swapchainCount; i++) {
if ((pCreateInfos[i].queueFamilyIndexCount > 1) && (pCreateInfos[i].imageSharingMode == VK_SHARING_MODE_EXCLUSIVE)) {
skip |= LogWarning("BestPractices-vkCreateSharedSwapchainsKHR-sharing-mode-exclusive", device,
error_obj.location.dot(Field::pCreateInfos, i),
"A shared swapchain is being created which specifies a sharing mode of VK_SHARING_MODE_EXCLUSIVE "
"while specifying multiple "
"queues (queueFamilyIndexCount of %" PRIu32 ").",
pCreateInfos[i].queueFamilyIndexCount);
}
}
return skip;
}
void BestPractices::ManualPostCallRecordQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo,
const RecordObject& record_obj) {
for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) {
auto swapchains_result = pPresentInfo->pResults ? pPresentInfo->pResults[i] : record_obj.result;
if (swapchains_result == VK_SUBOPTIMAL_KHR) {
LogPerformanceWarning(
"BestPractices-vkCreateSharedSwapchainsKHR-SuboptimalSwapchain", pPresentInfo->pSwapchains[i],
record_obj.location.dot(Field::pPresentInfo, i),
"VK_SUBOPTIMAL_KHR was returned. VK_SUBOPTIMAL_KHR - Presentation will still succeed, "
"subject to the window resize behavior, but the swapchain (%s) is no longer configured optimally for the surface "
"it targets.\nApplications should query updated surface information and recreate their swapchain at the next "
"convenient opportunity.",
FormatHandle(pPresentInfo->pSwapchains[i]).c_str());
}
}
// AMD best practice
// end-of-frame cleanup
num_queue_submissions_ = 0;
num_barriers_objects_ = 0;
ClearPipelinesUsedInFrame();
}
bool bp_state::Instance::PreCallValidateGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormatKHR* pSurfaceFormats,
const ErrorObject& error_obj) const {
bool skip = false;
const auto bp_pd_state = Get<vvl::PhysicalDevice>(physicalDevice);
if (!bp_pd_state || !pSurfaceFormats) return skip;
if (pSurfaceFormatCount && *pSurfaceFormatCount > bp_pd_state->surface_formats_count) {
skip |=
LogWarning("BestPractices-GetPhysicalDeviceSurfaceFormatsKHR-CountMismatch", physicalDevice,
error_obj.location.dot(Field::pSurfaceFormatCount),
"(%" PRIu32 ") is greater than the value (%" PRIu32 ") that was returned when pSurfaceFormatCount was NULL.",
*pSurfaceFormatCount, bp_pd_state->surface_formats_count);
}
return skip;
}
bool BestPractices::PreCallValidateQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo,
const ErrorObject& error_obj) const {
bool skip = false;
if (VendorCheckEnabled(kBPVendorAMD) || VendorCheckEnabled(kBPVendorNVIDIA)) {
auto num = num_queue_submissions_.load();
if (num > kNumberOfSubmissionWarningLimitAMD) {
skip |= LogPerformanceWarning("BestPractices-Submission-ReduceNumberOfSubmissions", device, error_obj.location,
"%s %s command buffers submitted %" PRId32
" times this frame. Submitting command buffers has a CPU "
"and GPU overhead. Submit fewer times to incur less overhead.",
VendorSpecificTag(kBPVendorAMD), VendorSpecificTag(kBPVendorNVIDIA), num);
}
}
return skip;
}
bool BestPractices::PreCallValidateAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout,
VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex,
const ErrorObject& error_obj) const {
auto swapchain_data = Get<vvl::Swapchain>(swapchain);
bool skip = false;
if (swapchain_data && swapchain_data->images.empty()) {
skip |= LogWarning("BestPractices-vkAcquireNextImageKHR-SwapchainImagesNotFound", swapchain, error_obj.location,
"No images found to acquire from. Application probably did not call "
"vkGetSwapchainImagesKHR after swapchain creation.");
}
return skip;
}