blob: ca25c204477b9bbb39869787731f9a06f82b0a52 [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 "state_tracker/queue_state.h"
#include "generated/dispatch_functions.h"
bool BestPractices::CheckDependencyInfo(const LogObjectList& objlist, const Location& dep_loc, const VkDependencyInfo& dep_info,
VkCommandBuffer commandBuffer) const {
bool skip = false;
for (uint32_t i = 0; i < dep_info.imageMemoryBarrierCount; ++i) {
skip |= ValidateImageMemoryBarrier(
dep_loc.dot(Field::pImageMemoryBarriers, i), commandBuffer, dep_info.pImageMemoryBarriers[i].image,
dep_info.pImageMemoryBarriers[i].oldLayout, dep_info.pImageMemoryBarriers[i].newLayout,
dep_info.pImageMemoryBarriers[i].srcAccessMask, dep_info.pImageMemoryBarriers[i].dstAccessMask,
dep_info.pImageMemoryBarriers[i].subresourceRange.aspectMask, dep_info.pImageMemoryBarriers[i].srcQueueFamilyIndex,
dep_info.pImageMemoryBarriers[i].dstQueueFamilyIndex);
}
for (uint32_t i = 0; i < dep_info.bufferMemoryBarrierCount; ++i) {
skip |= ValidateBufferMemoryBarrier(
dep_loc.dot(Field::pBufferMemoryBarriers, i), commandBuffer, dep_info.pBufferMemoryBarriers[i].buffer,
dep_info.pBufferMemoryBarriers[i].srcQueueFamilyIndex, dep_info.pBufferMemoryBarriers[i].dstQueueFamilyIndex);
}
return skip;
}
void BestPractices::PreCallRecordDestroyEvent(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator,
const RecordObject& record_obj) {
device_state->ForEachShared<vvl::CommandBuffer>([event](const std::shared_ptr<vvl::CommandBuffer>& command_buffer) {
auto guard = command_buffer->WriteLock();
auto& sub_state = bp_state::SubState(*command_buffer.get());
sub_state.event_signaling_state.erase(event);
});
}
bool BestPractices::CheckEventSignalingState(const bp_state::CommandBufferSubState& command_buffer, VkEvent event,
const Location& cb_loc) const {
bool skip = false;
if (auto* signaling_info = vvl::Find(command_buffer.event_signaling_state, event); signaling_info && signaling_info->signaled) {
const LogObjectList objlist(command_buffer.VkHandle(), event);
skip |= LogWarning("BestPractices-Event-SignalSignaledEvent", objlist, cb_loc,
"%s sets event %s which was already set (in this command buffer or in the executed secondary command "
"buffers). If this is not the desired behavior, the event must be reset before it is set again.",
FormatHandle(command_buffer.VkHandle()).c_str(), FormatHandle(event).c_str());
}
return skip;
}
bool BestPractices::PreCallValidateCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask,
const ErrorObject& error_obj) const {
bool skip = false;
auto cb_state = Get<vvl::CommandBuffer>(commandBuffer);
auto& sub_state = bp_state::SubState(*cb_state);
skip |= CheckEventSignalingState(sub_state, event, error_obj.location.dot(Field::commandBuffer));
return skip;
}
bool BestPractices::PreCallValidateCmdSetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event,
const VkDependencyInfoKHR* pDependencyInfo, const ErrorObject& error_obj) const {
return PreCallValidateCmdSetEvent2(commandBuffer, event, pDependencyInfo, error_obj);
}
bool BestPractices::PreCallValidateCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event,
const VkDependencyInfo* pDependencyInfo, const ErrorObject& error_obj) const {
bool skip = false;
skip |= CheckDependencyInfo(commandBuffer, error_obj.location.dot(Field::pDependencyInfo), *pDependencyInfo, commandBuffer);
auto cb_state = Get<vvl::CommandBuffer>(commandBuffer);
auto& sub_state = bp_state::SubState(*cb_state);
skip |= CheckEventSignalingState(sub_state, event, error_obj.location.dot(Field::commandBuffer));
return skip;
}
bool BestPractices::PreCallValidateCmdResetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event,
VkPipelineStageFlags2KHR stageMask, const ErrorObject& error_obj) const {
return PreCallValidateCmdResetEvent2(commandBuffer, event, stageMask, error_obj);
}
bool BestPractices::PreCallValidateCmdWaitEvents2KHR(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents,
const VkDependencyInfoKHR* pDependencyInfos,
const ErrorObject& error_obj) const {
return PreCallValidateCmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos, error_obj);
}
bool BestPractices::PreCallValidateCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents,
const VkDependencyInfo* pDependencyInfos, const ErrorObject& error_obj) const {
bool skip = false;
for (uint32_t i = 0; i < eventCount; i++) {
skip |= CheckDependencyInfo(commandBuffer, error_obj.location.dot(Field::pDependencyInfos, i), pDependencyInfos[i],
commandBuffer);
}
return skip;
}
bool BestPractices::ValidateAccessLayoutCombination(const Location& loc, VkImage image, VkAccessFlags2 access, VkImageLayout layout,
VkImageAspectFlags aspect) const {
bool skip = false;
const VkAccessFlags2 all = vvl::kU64Max; // core validation is responsible for detecting undefined flags.
VkAccessFlags2 allowed = 0;
// Combinations taken from https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2918
switch (layout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
allowed = all;
break;
case VK_IMAGE_LAYOUT_GENERAL:
allowed = all;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
allowed = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
allowed = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
allowed = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
allowed = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
allowed = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
allowed = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
allowed = VK_ACCESS_HOST_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
if (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) {
allowed |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
}
if (aspect & VK_IMAGE_ASPECT_STENCIL_BIT) {
allowed |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
break;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
if (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) {
allowed |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
if (aspect & VK_IMAGE_ASPECT_STENCIL_BIT) {
allowed |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
}
break;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
allowed = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL:
allowed = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
break;
case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL:
allowed = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL:
allowed = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
break;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
allowed = VK_ACCESS_NONE; // PR table says "Must be 0"
break;
case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR:
allowed = all;
break;
// alias VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
// alias VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV
allowed = VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
break;
case VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT:
allowed = VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT;
break;
default:
// If a new layout is added, will need to manually add it
return false;
}
if ((allowed | access) != allowed) {
skip |= LogWarning("BestPractices-ImageBarrierAccessLayout", image, loc,
"image is %s and accessMask is %s, but for layout %s expected accessMask are %s.",
FormatHandle(image).c_str(), string_VkAccessFlags2(access).c_str(), string_VkImageLayout(layout),
string_VkAccessFlags2(allowed).c_str());
}
return skip;
}
bool BestPractices::ValidateImageMemoryBarrier(const Location& loc, VkCommandBuffer commandBuffer, VkImage image,
VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags2 srcAccessMask,
VkAccessFlags2 dstAccessMask, VkImageAspectFlags aspectMask,
uint32_t srcQueueFamilyIndex, uint32_t dstQueueFamilyIndex) const {
bool skip = false;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && IsImageLayoutReadOnly(newLayout)) {
skip |= LogWarning("BestPractices-ImageMemoryBarrier-TransitionUndefinedToReadOnly", image, loc,
"VkImageMemoryBarrier is being submitted with oldLayout VK_IMAGE_LAYOUT_UNDEFINED and the contents "
"may be discarded, but the newLayout is %s, which is read only.",
string_VkImageLayout(newLayout));
}
if (device_state->special_supported.has_maintenance9 && srcQueueFamilyIndex != dstQueueFamilyIndex &&
srcQueueFamilyIndex != VK_QUEUE_FAMILY_FOREIGN_EXT && srcQueueFamilyIndex != VK_QUEUE_FAMILY_EXTERNAL &&
dstQueueFamilyIndex != VK_QUEUE_FAMILY_FOREIGN_EXT && dstQueueFamilyIndex != VK_QUEUE_FAMILY_EXTERNAL) {
auto image_state = Get<vvl::Image>(image);
const char* warning = enabled_features.maintenance9
? "is not required, because maintenance9 is enabled"
: "could be omitted, if maintenance9 (which is supported by the physical device) were enabled";
if (image_state->GetTiling() == VK_IMAGE_TILING_LINEAR) {
skip |= LogPerformanceWarning("BestPractices-PipelineBarrier-unneeded-QFOT", image, loc,
"A queue family ownership transfer is being performed on %s, but this %s. Image was "
"created with VK_IMAGE_TILING_LINEAR.",
FormatHandle(image).c_str(), warning);
} else if ((image_state->usage & (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 |
VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT |
VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) == 0) {
// The list of image usages not allowed comes from
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSharingMode.html
auto cb_state = Get<vvl::CommandBuffer>(commandBuffer);
VkQueueFamilyOwnershipTransferPropertiesKHR qfot_props = vku::InitStructHelper();
uint32_t qf_count = dstQueueFamilyIndex + 1;
std::vector<VkQueueFamilyProperties2> qf_props(qf_count);
qf_props.back().pNext = &qfot_props;
DispatchGetPhysicalDeviceQueueFamilyProperties2(cb_state->dev_data.physical_device, &qf_count, qf_props.data());
if (qfot_props.optimalImageTransferToQueueFamilies & (1 << dstQueueFamilyIndex)) {
skip |= LogPerformanceWarning(
"BestPractices-PipelineBarrier-unneeded-QFOT", image, loc,
"A queue family ownership transfer is being performed on %s, but this %s. Image was created with "
"VK_IMAGE_TILING_OPTIMAL, image usage does not contain any of 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 | VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT | "
"VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR and "
"VkQueueFamilyOwnershipTransferPropertiesKHR::optimalImageTransferToQueueFamilies (%" PRIu32
") has bit set for destination queue family index %" PRIu32 ".",
FormatHandle(image).c_str(), warning, qfot_props.optimalImageTransferToQueueFamilies, dstQueueFamilyIndex);
}
}
}
skip |= ValidateAccessLayoutCombination(loc, image, srcAccessMask, oldLayout, aspectMask);
skip |= ValidateAccessLayoutCombination(loc, image, dstAccessMask, newLayout, aspectMask);
return skip;
}
bool BestPractices::ValidateBufferMemoryBarrier(const Location& loc, VkCommandBuffer commandBuffer, VkBuffer buffer,
uint32_t srcQueueFamilyIndex, uint32_t dstQueueFamilyIndex) const {
bool skip = false;
if (device_state->special_supported.has_maintenance9 && srcQueueFamilyIndex != dstQueueFamilyIndex &&
srcQueueFamilyIndex != VK_QUEUE_FAMILY_FOREIGN_EXT && srcQueueFamilyIndex != VK_QUEUE_FAMILY_EXTERNAL &&
dstQueueFamilyIndex != VK_QUEUE_FAMILY_FOREIGN_EXT && dstQueueFamilyIndex != VK_QUEUE_FAMILY_EXTERNAL) {
const char* warning = enabled_features.maintenance9
? "is not required, because maintenance9 is enabled"
: "could be omitted, if maintenance9 (which is supported by the physical device) were enabled";
skip |= LogPerformanceWarning("BestPractices-PipelineBarrier-unneeded-QFOT", buffer, loc,
"A queue family ownership transfer is being performed on %s, but this %s.",
FormatHandle(buffer).c_str(), warning);
}
return skip;
}
bool BestPractices::PreCallValidateCmdPipelineBarrier(
VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers, const ErrorObject& error_obj) const {
bool skip = false;
for (uint32_t i = 0; i < imageMemoryBarrierCount; ++i) {
skip |= ValidateImageMemoryBarrier(
error_obj.location.dot(Field::pImageMemoryBarriers, i), commandBuffer, pImageMemoryBarriers[i].image,
pImageMemoryBarriers[i].oldLayout, pImageMemoryBarriers[i].newLayout, pImageMemoryBarriers[i].srcAccessMask,
pImageMemoryBarriers[i].dstAccessMask, pImageMemoryBarriers[i].subresourceRange.aspectMask,
pImageMemoryBarriers[i].srcQueueFamilyIndex, pImageMemoryBarriers[i].dstQueueFamilyIndex);
}
for (uint32_t i = 0; i < bufferMemoryBarrierCount; ++i) {
skip |= ValidateBufferMemoryBarrier(error_obj.location.dot(Field::pBufferMemoryBarriers, i), commandBuffer,
pBufferMemoryBarriers[i].buffer, pBufferMemoryBarriers[i].srcQueueFamilyIndex,
pBufferMemoryBarriers[i].dstQueueFamilyIndex);
}
if (VendorCheckEnabled(kBPVendorAMD)) {
const uint32_t num = num_barriers_objects_.load();
const uint32_t total_barriers = num + imageMemoryBarrierCount + bufferMemoryBarrierCount;
if (total_barriers > kMaxRecommendedBarriersSizeAMD) {
skip |= LogPerformanceWarning("BestPractices-AMD-CmdBuffer-highBarrierCount", commandBuffer, error_obj.location,
"%s In this frame, %" PRIu32 " barriers were already submitted (%" PRIu32
" if you include image and buffer barriers too). Barriers have a high cost and can "
"stall the GPU.\nTotal recommended max is %" PRIu32
". Consider consolidating and re-organizing the frame to use fewer barriers.",
VendorSpecificTag(kBPVendorAMD), num, total_barriers, kMaxRecommendedBarriersSizeAMD);
}
}
if (VendorCheckEnabled(kBPVendorAMD) || VendorCheckEnabled(kBPVendorNVIDIA)) {
static constexpr std::array<VkImageLayout, 3> read_layouts = {
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
};
for (uint32_t i = 0; i < imageMemoryBarrierCount; i++) {
// read to read barriers
const auto& image_barrier = pImageMemoryBarriers[i];
const bool old_is_read_layout =
std::find(read_layouts.begin(), read_layouts.end(), image_barrier.oldLayout) != read_layouts.end();
const bool new_is_read_layout =
std::find(read_layouts.begin(), read_layouts.end(), image_barrier.newLayout) != read_layouts.end();
if (old_is_read_layout && new_is_read_layout) {
skip |= LogPerformanceWarning("BestPractices-PipelineBarrier-readToReadBarrier", commandBuffer, error_obj.location,
"%s %s Don't issue read-to-read barriers. "
"Get the resource in the right state the first time you use it.",
VendorSpecificTag(kBPVendorAMD), VendorSpecificTag(kBPVendorNVIDIA));
}
// general with no storage
if (VendorCheckEnabled(kBPVendorAMD) && image_barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL) {
auto image_state = Get<vvl::Image>(pImageMemoryBarriers[i].image);
if (image_state && !(image_state->usage & VK_IMAGE_USAGE_STORAGE_BIT)) {
const LogObjectList objlist(commandBuffer, pImageMemoryBarriers[i].image);
skip |= LogPerformanceWarning("BestPractices-AMD-vkImage-AvoidGeneral", objlist,
error_obj.location.dot(Field::pImageMemoryBarriers, i).dot(Field::image),
"%s VK_IMAGE_LAYOUT_GENERAL should only be used with "
"VK_IMAGE_USAGE_STORAGE_BIT images.",
VendorSpecificTag(kBPVendorAMD));
}
}
}
}
for (uint32_t i = 0; i < imageMemoryBarrierCount; ++i) {
skip |= ValidateCmdPipelineBarrierImageBarrier(commandBuffer, pImageMemoryBarriers[i],
error_obj.location.dot(Field::pImageMemoryBarriers, i));
}
return skip;
}
bool BestPractices::PreCallValidateCmdPipelineBarrier2KHR(VkCommandBuffer commandBuffer, const VkDependencyInfoKHR* pDependencyInfo,
const ErrorObject& error_obj) const {
return PreCallValidateCmdPipelineBarrier2(commandBuffer, pDependencyInfo, error_obj);
}
bool BestPractices::PreCallValidateCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo,
const ErrorObject& error_obj) const {
bool skip = false;
const Location dep_info_loc = error_obj.location.dot(Field::pDependencyInfo);
skip |= CheckDependencyInfo(commandBuffer, dep_info_loc, *pDependencyInfo, commandBuffer);
for (uint32_t i = 0; i < pDependencyInfo->imageMemoryBarrierCount; ++i) {
skip |= ValidateCmdPipelineBarrierImageBarrier(commandBuffer, pDependencyInfo->pImageMemoryBarriers[i],
dep_info_loc.dot(Field::pImageMemoryBarriers, i));
}
return skip;
}
template <typename ImageMemoryBarrier>
bool BestPractices::ValidateCmdPipelineBarrierImageBarrier(VkCommandBuffer commandBuffer, const ImageMemoryBarrier& barrier,
const Location& loc) const {
bool skip = false;
const auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
if (VendorCheckEnabled(kBPVendorNVIDIA)) {
if (barrier.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && barrier.newLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
const auto& sub_state = bp_state::SubState(*cb_state);
skip |= ValidateZcull(sub_state, barrier.image, barrier.subresourceRange, loc);
}
}
return skip;
}
void BestPractices::PostCallRecordCmdPipelineBarrier(
VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier* pImageMemoryBarriers, const RecordObject& record_obj) {
num_barriers_objects_ += (memoryBarrierCount + imageMemoryBarrierCount + bufferMemoryBarrierCount);
}
bool BestPractices::PreCallValidateCreateSemaphore(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore,
const ErrorObject& error_obj) const {
bool skip = false;
if (VendorCheckEnabled(kBPVendorAMD) || VendorCheckEnabled(kBPVendorNVIDIA)) {
const size_t count = Count<vvl::Semaphore>();
if (count > kMaxRecommendedSemaphoreObjectsSizeAMD) {
skip |= LogPerformanceWarning("BestPractices-SyncObjects-HighNumberOfSemaphores", device, error_obj.location,
"%s %s High number of vkSemaphore objects created. "
"%zu created, but recommended max is %" PRIu32
".\nMinimize the amount of queue synchronization that is used. "
"Each semaphore has a CPU and GPU overhead cost with it.",
VendorSpecificTag(kBPVendorAMD), VendorSpecificTag(kBPVendorNVIDIA), count,
kMaxRecommendedSemaphoreObjectsSizeAMD);
}
}
return skip;
}
bool BestPractices::PreCallValidateCreateFence(VkDevice device, const VkFenceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkFence* pFence,
const ErrorObject& error_obj) const {
bool skip = false;
if (VendorCheckEnabled(kBPVendorAMD) || VendorCheckEnabled(kBPVendorNVIDIA)) {
const size_t count = Count<vvl::Fence>();
if (count > kMaxRecommendedFenceObjectsSizeAMD) {
skip |= LogPerformanceWarning("BestPractices-SyncObjects-HighNumberOfFences", device, error_obj.location,
"%s %s High number of VkFence objects created. "
"%zu created, but recommended max is %" PRIu32
".\nMinimize the amount of CPU-GPU synchronization that is used. "
"Each fence has a CPU and GPU overhead cost with it.",
VendorSpecificTag(kBPVendorAMD), VendorSpecificTag(kBPVendorNVIDIA), count,
kMaxRecommendedFenceObjectsSizeAMD);
}
}
return skip;
}