blob: 17883ac468609afaffb094ebc123d859c723df39 [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 <assert.h>
#include <string>
#include <sstream>
#include <vulkan/vk_enum_string_helper.h>
#include "generated/chassis.h"
#include "core_validation.h"
#include "generated/enum_flag_bits.h"
bool CoreChecks::ReportInvalidCommandBuffer(const CMD_BUFFER_STATE &cb_state, const Location &loc) const {
bool skip = false;
for (const auto &entry : cb_state.broken_bindings) {
const auto &obj = entry.first;
const char *cause_str = (obj.type == kVulkanObjectTypeDescriptorSet) ? " or updated"
: (obj.type == kVulkanObjectTypeCommandBuffer) ? " or rerecorded"
: "";
std::string vuid;
std::ostringstream str;
str << kVUID_Core_DrawState_InvalidCommandBuffer << "-" << object_string[obj.type];
vuid = str.str();
auto objlist = entry.second; // intentional copy
objlist.add(cb_state.commandBuffer());
skip |= LogError(vuid, objlist, loc, "was called in %s which is invalid because bound %s was destroyed%s.",
FormatHandle(cb_state).c_str(), FormatHandle(obj).c_str(), cause_str);
}
return skip;
}
bool CoreChecks::PreCallValidateFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount,
const VkCommandBuffer *pCommandBuffers, const ErrorObject &error_obj) const {
bool skip = false;
for (uint32_t i = 0; i < commandBufferCount; i++) {
auto cb_state = GetRead<CMD_BUFFER_STATE>(pCommandBuffers[i]);
// Delete CB information structure, and remove from commandBufferMap
if (cb_state && cb_state->InUse()) {
const LogObjectList objlist(pCommandBuffers[i], commandPool);
skip |= LogError("VUID-vkFreeCommandBuffers-pCommandBuffers-00047", objlist,
error_obj.location.dot(Field::pCommandBuffers, i), "(%s) is in use.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
return skip;
}
bool CoreChecks::PreCallValidateBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo *pBeginInfo,
const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state) return false;
bool skip = false;
if (cb_state->InUse()) {
skip |= LogError("VUID-vkBeginCommandBuffer-commandBuffer-00049", commandBuffer, error_obj.location,
"on active %s before it has completed. You must check "
"command buffer fence before this call.",
FormatHandle(commandBuffer).c_str());
}
const Location begin_info_loc = error_obj.location.dot(Field::pBeginInfo);
if (cb_state->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) {
// Primary Command Buffer
const VkCommandBufferUsageFlags invalid_usage =
(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT);
if ((pBeginInfo->flags & invalid_usage) == invalid_usage) {
skip |= LogError("VUID-vkBeginCommandBuffer-commandBuffer-02840", commandBuffer, begin_info_loc.dot(Field::flags),
"is %s for Primary %s (can't have both ONE_TIME_SUBMIT and SIMULTANEOUS_USE).",
string_VkCommandBufferUsageFlags(pBeginInfo->flags).c_str(), FormatHandle(commandBuffer).c_str());
}
} else {
// Secondary Command Buffer
const VkCommandBufferInheritanceInfo *info = pBeginInfo->pInheritanceInfo;
const Location inheritance_loc = begin_info_loc.dot(Field::pInheritanceInfo);
if (!info) {
skip |= LogError("VUID-vkBeginCommandBuffer-commandBuffer-00051", commandBuffer, inheritance_loc,
"is null for Secondary %s.", FormatHandle(commandBuffer).c_str());
} else {
auto p_inherited_rendering_info = vku::FindStructInPNextChain<VkCommandBufferInheritanceRenderingInfo>(info->pNext);
if (pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) {
auto framebuffer = Get<FRAMEBUFFER_STATE>(info->framebuffer);
if (framebuffer) {
if (framebuffer->createInfo.renderPass != info->renderPass) {
auto render_pass = Get<RENDER_PASS_STATE>(info->renderPass);
// renderPass that framebuffer was created with must be compatible with local renderPass
skip |= ValidateRenderPassCompatibility("framebuffer", *framebuffer->rp_state.get(), "command buffer",
*render_pass.get(), inheritance_loc,
"VUID-VkCommandBufferBeginInfo-flags-00055");
}
}
if (info->renderPass != VK_NULL_HANDLE) {
auto render_pass = Get<RENDER_PASS_STATE>(info->renderPass);
if (!render_pass) {
skip |= LogError("VUID-VkCommandBufferBeginInfo-flags-06000", commandBuffer,
inheritance_loc.dot(Field::renderPass), "is not a valid VkRenderPass.");
} else {
if (info->subpass >= render_pass->createInfo.subpassCount) {
skip |= LogError("VUID-VkCommandBufferBeginInfo-flags-06001", commandBuffer,
inheritance_loc.dot(Field::subpass),
"(%" PRIu32 ") is not valid, renderPass was created with subpassCount %" PRIu32 ".",
info->subpass, render_pass->createInfo.subpassCount);
}
}
} else {
if (!p_inherited_rendering_info) {
skip |= LogError("VUID-VkCommandBufferBeginInfo-flags-06002", commandBuffer, inheritance_loc,
"pNext chain of pInheritanceInfo must include a "
"VkCommandBufferInheritanceRenderingInfo structure.");
}
}
}
if (p_inherited_rendering_info) {
auto p_attachment_sample_count_info_amd = vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(info->pNext);
if (p_attachment_sample_count_info_amd &&
p_attachment_sample_count_info_amd->colorAttachmentCount != p_inherited_rendering_info->colorAttachmentCount) {
skip |= LogError(
"VUID-VkCommandBufferBeginInfo-flags-06003", commandBuffer,
inheritance_loc.pNext(Struct::VkAttachmentSampleCountInfoAMD, Field::colorAttachmentCount),
"(%" PRIu32 ") must equal VkCommandBufferInheritanceRenderingInfo::colorAttachmentCount (%" PRIu32 ").",
p_attachment_sample_count_info_amd->colorAttachmentCount, p_inherited_rendering_info->colorAttachmentCount);
}
if ((p_inherited_rendering_info->colorAttachmentCount != 0) &&
(p_inherited_rendering_info->rasterizationSamples & AllVkSampleCountFlagBits) == 0) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-colorAttachmentCount-06004", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::colorAttachmentCount),
"(%" PRIu32 ") is not 0, so rasterizationSamples (0x%" PRIx32
") must be a valid VkSampleCountFlagBits value.",
p_inherited_rendering_info->colorAttachmentCount, p_inherited_rendering_info->rasterizationSamples);
}
if ((!enabled_features.core.variableMultisampleRate) &&
(p_inherited_rendering_info->rasterizationSamples & AllVkSampleCountFlagBits) == 0) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-variableMultisampleRate-06005", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::rasterizationSamples),
"is not valid (0x%" PRIx32 ") and the variableMultisampleRate feature was not enabled.",
p_inherited_rendering_info->rasterizationSamples);
}
for (uint32_t i = 0; i < p_inherited_rendering_info->colorAttachmentCount; ++i) {
if (p_inherited_rendering_info->pColorAttachmentFormats != nullptr) {
const VkFormat attachment_format = p_inherited_rendering_info->pColorAttachmentFormats[i];
if (attachment_format != VK_FORMAT_UNDEFINED) {
const VkFormatFeatureFlags2 potential_format_features = GetPotentialFormatFeatures(attachment_format);
if ((potential_format_features & (VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR |
VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV)) == 0) {
skip |= LogError("VUID-VkCommandBufferInheritanceRenderingInfo-pColorAttachmentFormats-06492",
commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo,
Field::pColorAttachmentFormats, i),
"is %s with potential format features %s.", string_VkFormat(attachment_format),
string_VkFormatFeatureFlags2(potential_format_features).c_str());
}
}
}
}
const VkFormatFeatureFlags2 valid_depth_stencil_format = VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR;
const VkFormat depth_format = p_inherited_rendering_info->depthAttachmentFormat;
if (depth_format != VK_FORMAT_UNDEFINED) {
const VkFormatFeatureFlags2 potential_format_features = GetPotentialFormatFeatures(depth_format);
if ((potential_format_features & valid_depth_stencil_format) == 0) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06007", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::depthAttachmentFormat),
"is %s with potential format features %s.", string_VkFormat(depth_format),
string_VkFormatFeatureFlags2(potential_format_features).c_str());
}
if (!vkuFormatHasDepth(depth_format)) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06540", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::depthAttachmentFormat),
"%s is not a depth format.", string_VkFormat(depth_format));
}
}
const VkFormat stencil_format = p_inherited_rendering_info->stencilAttachmentFormat;
if (stencil_format != VK_FORMAT_UNDEFINED) {
const VkFormatFeatureFlags2 potential_format_features = GetPotentialFormatFeatures(stencil_format);
if ((potential_format_features & valid_depth_stencil_format) == 0) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-06199", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::stencilAttachmentFormat),
"is %s with potential format features %s.", string_VkFormat(stencil_format),
string_VkFormatFeatureFlags2(potential_format_features).c_str());
}
if (!vkuFormatHasStencil(stencil_format)) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-06541", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::stencilAttachmentFormat),
"%s is not a stencil format.", string_VkFormat(stencil_format));
}
}
if ((depth_format != VK_FORMAT_UNDEFINED && stencil_format != VK_FORMAT_UNDEFINED) &&
(depth_format != stencil_format)) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06200", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::depthAttachmentFormat),
"(%s) is not the same as stencilAttachmentFormat (%s).", string_VkFormat(depth_format),
string_VkFormat(stencil_format));
}
if ((enabled_features.core11.multiview == VK_FALSE) && (p_inherited_rendering_info->viewMask != 0)) {
skip |= LogError("VUID-VkCommandBufferInheritanceRenderingInfo-multiview-06008", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::viewMask),
"is %" PRIu32 ", but the multiview feature was not enabled.",
p_inherited_rendering_info->viewMask);
}
if (MostSignificantBit(p_inherited_rendering_info->viewMask) >=
static_cast<int32_t>(phys_dev_props_core11.maxMultiviewViewCount)) {
skip |= LogError("VUID-VkCommandBufferInheritanceRenderingInfo-viewMask-06009", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceRenderingInfo, Field::viewMask),
"(0x%" PRIx32 ") most significant bit is superior or equal to maxMultiviewViewCount (%" PRIu32
").",
p_inherited_rendering_info->viewMask, phys_dev_props_core11.maxMultiviewViewCount);
}
}
}
if (info) {
if ((info->occlusionQueryEnable == VK_FALSE || enabled_features.core.occlusionQueryPrecise == VK_FALSE) &&
(info->queryFlags & VK_QUERY_CONTROL_PRECISE_BIT)) {
skip |= LogError("VUID-vkBeginCommandBuffer-commandBuffer-00052", commandBuffer, inheritance_loc,
"Secondary %s must not have VK_QUERY_CONTROL_PRECISE_BIT if "
"occulusionQuery is disabled or the device does not support precise occlusion queries.",
FormatHandle(commandBuffer).c_str());
}
auto p_inherited_viewport_scissor_info = vku::FindStructInPNextChain<VkCommandBufferInheritanceViewportScissorInfoNV>(info->pNext);
if (p_inherited_viewport_scissor_info != nullptr && p_inherited_viewport_scissor_info->viewportScissor2D) {
if (!enabled_features.inherited_viewport_scissor_features.inheritedViewportScissor2D) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceViewportScissorInfoNV-viewportScissor2D-04782", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceViewportScissorInfoNV, Field::viewportScissor2D),
"is VK_TRUE, but the inheritedViewportScissor2D feature was not enabled.");
}
if (!(pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) {
skip |=
LogError("VUID-VkCommandBufferInheritanceViewportScissorInfoNV-viewportScissor2D-04786", commandBuffer,
begin_info_loc.dot(Field::flags), "is %s for Secondary %s (and viewportScissor2D is VK_TRUE).",
string_VkCommandBufferUsageFlags(pBeginInfo->flags).c_str(), FormatHandle(commandBuffer).c_str());
}
if (p_inherited_viewport_scissor_info->viewportDepthCount == 0) {
skip |= LogError(
"VUID-VkCommandBufferInheritanceViewportScissorInfoNV-viewportScissor2D-04784", commandBuffer,
inheritance_loc.pNext(Struct::VkCommandBufferInheritanceViewportScissorInfoNV, Field::viewportDepthCount),
"is zero (but viewportScissor2D is VK_TRUE).");
}
}
// Check for dynamic rendering feature enabled or 1.3
if ((api_version < VK_API_VERSION_1_3) && (!enabled_features.core13.dynamicRendering)) {
if (pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) {
if (info->renderPass == VK_NULL_HANDLE) {
skip |=
LogError("VUID-VkCommandBufferBeginInfo-flags-06000", commandBuffer, begin_info_loc.dot(Field::flags),
"includes VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT "
"but the renderpass member of pInheritanceInfo is VK_NULL_HANDLE.");
}
}
}
}
}
if (CbState::Recording == cb_state->state) {
skip |= LogError("VUID-vkBeginCommandBuffer-commandBuffer-00049", commandBuffer, error_obj.location,
"Cannot call Begin on %s in the RECORDING state. Must first call "
"vkEndCommandBuffer().",
FormatHandle(commandBuffer).c_str());
} else if (CbState::Recorded == cb_state->state || CbState::InvalidComplete == cb_state->state) {
VkCommandPool cmd_pool = cb_state->createInfo.commandPool;
const auto *pool = cb_state->command_pool;
if (!(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT & pool->createFlags)) {
const LogObjectList objlist(commandBuffer, cmd_pool);
skip |= LogError("VUID-vkBeginCommandBuffer-commandBuffer-00050", objlist, error_obj.location,
"%s attempts to implicitly reset cmdBuffer created from "
"%s that does NOT have the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit set.",
FormatHandle(commandBuffer).c_str(), FormatHandle(cmd_pool).c_str());
}
}
auto chained_device_group_struct = vku::FindStructInPNextChain<VkDeviceGroupCommandBufferBeginInfo>(pBeginInfo->pNext);
if (chained_device_group_struct) {
const LogObjectList objlist(commandBuffer);
skip |= ValidateDeviceMaskToPhysicalDeviceCount(
chained_device_group_struct->deviceMask, objlist,
begin_info_loc.pNext(Struct::VkDeviceGroupCommandBufferBeginInfo, Field::deviceMask),
"VUID-VkDeviceGroupCommandBufferBeginInfo-deviceMask-00106");
skip |= ValidateDeviceMaskToZero(chained_device_group_struct->deviceMask, objlist,
begin_info_loc.pNext(Struct::VkDeviceGroupCommandBufferBeginInfo, Field::deviceMask),
"VUID-VkDeviceGroupCommandBufferBeginInfo-deviceMask-00107");
}
if ((pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) != 0) {
if ((cb_state->command_pool->queue_flags & VK_QUEUE_GRAPHICS_BIT) == 0) {
const LogObjectList objlist(commandBuffer, cb_state->command_pool->Handle());
skip |= LogError("VUID-VkCommandBufferBeginInfo-flags-09123", objlist, begin_info_loc.dot(Field::flags),
"contain VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, but the command pool (created with "
"queueFamilyIndex %" PRIu32 ") the command buffer %s was allocated from only supports %s.",
cb_state->command_pool->queueFamilyIndex, FormatHandle(commandBuffer).c_str(),
string_VkQueueFlags(cb_state->command_pool->queue_flags).c_str());
}
}
return skip;
}
bool CoreChecks::PreCallValidateEndCommandBuffer(VkCommandBuffer commandBuffer, const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state_ptr) {
return skip;
}
const CMD_BUFFER_STATE &cb_state = *cb_state_ptr;
if ((VK_COMMAND_BUFFER_LEVEL_PRIMARY == cb_state.createInfo.level) ||
!(cb_state.beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) {
// This needs spec clarification to update valid usage, see comments in PR:
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/165
skip |= InsideRenderPass(cb_state, error_obj.location, "VUID-vkEndCommandBuffer-commandBuffer-00060");
}
if (cb_state.state == CbState::InvalidComplete || cb_state.state == CbState::InvalidIncomplete) {
skip |= ReportInvalidCommandBuffer(cb_state, error_obj.location);
} else if (CbState::Recording != cb_state.state) {
skip |= LogError("VUID-vkEndCommandBuffer-commandBuffer-00059", commandBuffer, error_obj.location,
"Cannot call End on %s when not in the RECORDING state. Must first call vkBeginCommandBuffer().",
FormatHandle(commandBuffer).c_str());
}
for (const auto &query_obj : cb_state.activeQueries) {
skip |= LogError("VUID-vkEndCommandBuffer-commandBuffer-00061", commandBuffer, error_obj.location,
"Ending command buffer with in progress query: %s, query %d.", FormatHandle(query_obj.pool).c_str(),
query_obj.slot);
}
if (cb_state.conditional_rendering_active) {
skip |= LogError("VUID-vkEndCommandBuffer-None-01978", commandBuffer, error_obj.location,
"Ending command buffer with active conditional rendering.");
}
skip |= InsideVideoCodingScope(cb_state, error_obj.location, "VUID-vkEndCommandBuffer-None-06991");
return skip;
}
bool CoreChecks::PreCallValidateResetCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags,
const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state) return false;
VkCommandPool cmd_pool = cb_state->createInfo.commandPool;
const auto *pool = cb_state->command_pool;
if (!(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT & pool->createFlags)) {
const LogObjectList objlist(commandBuffer, cmd_pool);
skip |= LogError("VUID-vkResetCommandBuffer-commandBuffer-00046", objlist, error_obj.location,
"%s was created from %s which was created with %s.", FormatHandle(commandBuffer).c_str(),
FormatHandle(cmd_pool).c_str(), string_VkCommandPoolCreateFlags(pool->createFlags).c_str());
}
if (cb_state->InUse()) {
const LogObjectList objlist(commandBuffer, cmd_pool);
skip |= LogError("VUID-vkResetCommandBuffer-commandBuffer-00045", objlist, error_obj.location, "(%s) is in use.",
FormatHandle(commandBuffer).c_str());
}
return skip;
}
bool CoreChecks::ValidateCmdBindIndexBuffer(const CMD_BUFFER_STATE &cb_state, const BUFFER_STATE &buffer_state, VkDeviceSize offset,
VkIndexType indexType, const Location &loc) const {
bool skip = false;
const bool is_2 = loc.function == Func::vkCmdBindIndexBuffer2KHR;
const LogObjectList objlist(cb_state.commandBuffer(), buffer_state.buffer());
skip |= ValidateCmd(cb_state, loc);
const char *vuid = is_2 ? "VUID-vkCmdBindIndexBuffer2KHR-buffer-08784" : "VUID-vkCmdBindIndexBuffer-buffer-08784";
skip |= ValidateBufferUsageFlags(objlist, buffer_state, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, true, vuid, loc.dot(Field::buffer));
vuid = is_2 ? "VUID-vkCmdBindIndexBuffer2KHR-buffer-08785" : "VUID-vkCmdBindIndexBuffer-buffer-08785";
skip |= ValidateMemoryIsBoundToBuffer(cb_state.commandBuffer(), buffer_state, loc.dot(Field::buffer), vuid);
const VkDeviceSize offset_align = static_cast<VkDeviceSize>(GetIndexAlignment(indexType));
if (!IsIntegerMultipleOf(offset, offset_align)) {
vuid = is_2 ? "VUID-vkCmdBindIndexBuffer2KHR-offset-08783" : "VUID-vkCmdBindIndexBuffer-offset-08783";
skip |= LogError(vuid, objlist, loc.dot(Field::offset), "(%" PRIu64 ") does not fall on alignment (%s) boundary.", offset,
string_VkIndexType(indexType));
}
if (offset >= buffer_state.createInfo.size) {
vuid = is_2 ? "VUID-vkCmdBindIndexBuffer2KHR-offset-08782" : "VUID-vkCmdBindIndexBuffer-offset-08782";
skip |= LogError(vuid, objlist, loc.dot(Field::offset), "(%" PRIu64 ") is not less than the size (%" PRIu64 ").", offset,
buffer_state.createInfo.size);
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkIndexType indexType, const ErrorObject &error_obj) const {
auto buffer_state = Get<BUFFER_STATE>(buffer);
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
return ValidateCmdBindIndexBuffer(*cb_state, *buffer_state, offset, indexType, error_obj.location);
}
bool CoreChecks::PreCallValidateCmdBindIndexBuffer2KHR(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkDeviceSize size, VkIndexType indexType,
const ErrorObject &error_obj) const {
auto buffer_state = Get<BUFFER_STATE>(buffer);
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
bool skip = false;
skip |= ValidateCmdBindIndexBuffer(*cb_state, *buffer_state, offset, indexType, error_obj.location);
if (size != VK_WHOLE_SIZE) {
const VkDeviceSize offset_align = static_cast<VkDeviceSize>(GetIndexAlignment(indexType));
if (!IsIntegerMultipleOf(size, offset_align)) {
skip |= LogError("VUID-vkCmdBindIndexBuffer2KHR-size-08767", commandBuffer, error_obj.location.dot(Field::size),
"(%" PRIu64 ") does not fall on alignment (%s) boundary.", size, string_VkIndexType(indexType));
}
if ((offset + size) > buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, buffer);
skip |= LogError("VUID-vkCmdBindIndexBuffer2KHR-size-08768", commandBuffer, error_obj.location.dot(Field::size),
"(%" PRIu64 ") + offset (%" PRIu64 ") is larger than the buffer size (%" PRIu64 ").", size, offset,
buffer_state->createInfo.size);
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount,
const VkBuffer *pBuffers, const VkDeviceSize *pOffsets,
const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
bool skip = false;
skip |= ValidateCmd(*cb_state, error_obj.location);
for (uint32_t i = 0; i < bindingCount; ++i) {
auto buffer_state = Get<BUFFER_STATE>(pBuffers[i]);
if (!buffer_state) {
continue;
}
const LogObjectList objlist(commandBuffer, buffer_state->buffer());
skip |= ValidateBufferUsageFlags(objlist, *buffer_state, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, true,
"VUID-vkCmdBindVertexBuffers-pBuffers-00627", error_obj.location.dot(Field::pBuffers, i));
skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *buffer_state, error_obj.location.dot(Field::pBuffers, i),
"VUID-vkCmdBindVertexBuffers-pBuffers-00628");
if (pOffsets[i] >= buffer_state->createInfo.size) {
skip |=
LogError("VUID-vkCmdBindVertexBuffers-pOffsets-00626", objlist, error_obj.location.dot(Field::pOffsets, i),
"(%" PRIu64 ") is larger than the buffer size (%" PRIu64 ").", pOffsets[i], buffer_state->createInfo.size);
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset,
VkDeviceSize dataSize, const void *pData, const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state_ptr = GetRead<CMD_BUFFER_STATE>(commandBuffer);
auto dst_buffer_state = Get<BUFFER_STATE>(dstBuffer);
if (!cb_state_ptr || !dst_buffer_state) {
return skip;
}
const CMD_BUFFER_STATE &cb_state = *cb_state_ptr;
const LogObjectList objlist(commandBuffer, dstBuffer);
const Location buffer_loc = error_obj.location.dot(Field::dstBuffer);
skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_buffer_state, buffer_loc, "VUID-vkCmdUpdateBuffer-dstBuffer-00035");
// Validate that DST buffer has correct usage flags set
skip |= ValidateBufferUsageFlags(objlist, *dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true,
"VUID-vkCmdUpdateBuffer-dstBuffer-00034", buffer_loc);
skip |= ValidateCmd(cb_state, error_obj.location);
skip |= ValidateProtectedBuffer(cb_state, *dst_buffer_state, buffer_loc, "VUID-vkCmdUpdateBuffer-commandBuffer-01813");
skip |= ValidateUnprotectedBuffer(cb_state, *dst_buffer_state, buffer_loc, "VUID-vkCmdUpdateBuffer-commandBuffer-01814");
if (dstOffset >= dst_buffer_state->createInfo.size) {
skip |= LogError("VUID-vkCmdUpdateBuffer-dstOffset-00032", objlist, error_obj.location.dot(Field::dstOffset),
"(%" PRIu64 ") is not less than the size (%" PRIu64 ").", dstOffset, dst_buffer_state->createInfo.size);
} else if (dataSize > dst_buffer_state->createInfo.size - dstOffset) {
skip |= LogError("VUID-vkCmdUpdateBuffer-dataSize-00033", objlist, error_obj.location.dot(Field::dataSize),
"(%" PRIu64 ") is not less than the buffer size (%" PRIu64 ") minus dstOffset (%" PRIu64 ").", dataSize,
dst_buffer_state->createInfo.size, dstOffset);
}
return skip;
}
bool CoreChecks::ValidatePrimaryCommandBuffer(const CMD_BUFFER_STATE &cb_state, const Location &loc, const char *vuid) const {
bool skip = false;
if (cb_state.createInfo.level != VK_COMMAND_BUFFER_LEVEL_PRIMARY) {
skip |= LogError(vuid, cb_state.commandBuffer(), loc, "command can't be executed on a secondary command buffer.");
}
return skip;
}
bool CoreChecks::ValidateSecondaryCommandBufferState(const CMD_BUFFER_STATE &cb_state, const CMD_BUFFER_STATE &sub_cb_state,
const Location &cb_loc) const {
bool skip = false;
vvl::unordered_set<int> active_types;
if (!disabled[query_validation]) {
for (const auto &query_object : cb_state.activeQueries) {
auto query_pool_state = Get<QUERY_POOL_STATE>(query_object.pool);
if (!query_pool_state) {
continue;
}
if (query_pool_state->createInfo.queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS &&
sub_cb_state.beginInfo.pInheritanceInfo) {
VkQueryPipelineStatisticFlags cmd_buf_statistics = sub_cb_state.beginInfo.pInheritanceInfo->pipelineStatistics;
if ((cmd_buf_statistics & query_pool_state->createInfo.pipelineStatistics) != cmd_buf_statistics) {
const LogObjectList objlist(cb_state.commandBuffer(), sub_cb_state.commandBuffer(), query_object.pool);
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-00104", objlist, cb_loc,
"was created with pInheritanceInfo::pipelineStatistics %s but the active query pool (%s) was "
"created with %s.",
string_VkQueryPipelineStatisticFlags(cmd_buf_statistics).c_str(),
FormatHandle(query_object.pool).c_str(),
string_VkQueryPipelineStatisticFlags(query_pool_state->createInfo.pipelineStatistics).c_str());
}
}
active_types.insert(query_pool_state->createInfo.queryType);
}
for (const auto &query_object : sub_cb_state.startedQueries) {
auto query_pool_state = Get<QUERY_POOL_STATE>(query_object.pool);
if (query_pool_state && active_types.count(query_pool_state->createInfo.queryType)) {
const LogObjectList objlist(cb_state.commandBuffer(), sub_cb_state.commandBuffer(), query_object.pool);
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00105", objlist, cb_loc,
"called with invalid %s which has invalid active %s"
" of type %s but a query of that type has been started on secondary command buffer %s.",
FormatHandle(cb_state).c_str(), FormatHandle(query_object.pool).c_str(),
string_VkQueryType(query_pool_state->createInfo.queryType), FormatHandle(sub_cb_state).c_str());
}
}
}
const auto primary_pool = cb_state.command_pool;
const auto secondary_pool = sub_cb_state.command_pool;
if (primary_pool && secondary_pool && (primary_pool->queueFamilyIndex != secondary_pool->queueFamilyIndex)) {
const LogObjectList objlist(sub_cb_state.commandBuffer(), cb_state.commandBuffer());
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00094", objlist, cb_loc,
"Primary command buffer %s created in queue family %" PRIu32
" has secondary command buffer %s created in queue family %" PRIu32 ".",
FormatHandle(cb_state).c_str(), primary_pool->queueFamilyIndex, FormatHandle(sub_cb_state).c_str(),
secondary_pool->queueFamilyIndex);
}
return skip;
}
// Object that simulates the inherited viewport/scissor state as the device executes the called secondary command buffers.
// Visit the calling primary command buffer first, then the called secondaries in order.
// Contact David Zhao Akeley <dakeley@nvidia.com> for clarifications and bug fixes.
class CoreChecks::ViewportScissorInheritanceTracker {
static_assert(4 == sizeof(CMD_BUFFER_STATE::viewportMask), "Adjust max_viewports to match viewportMask bit width");
static constexpr uint32_t kMaxViewports = 32, kNotTrashed = uint32_t(-2), kTrashedByPrimary = uint32_t(-1);
const ValidationObject &validation_;
const CMD_BUFFER_STATE *primary_state_ = nullptr;
uint32_t viewport_mask_;
uint32_t scissor_mask_;
uint32_t viewport_trashed_by_[kMaxViewports]; // filled in VisitPrimary.
uint32_t scissor_trashed_by_[kMaxViewports];
VkViewport viewports_to_inherit_[kMaxViewports];
uint32_t viewport_count_to_inherit_; // 0 if viewport count (EXT state) has never been defined (but not trashed)
uint32_t scissor_count_to_inherit_; // 0 if scissor count (EXT state) has never been defined (but not trashed)
uint32_t viewport_count_trashed_by_;
uint32_t scissor_count_trashed_by_;
public:
ViewportScissorInheritanceTracker(const ValidationObject &validation) : validation_(validation) {}
bool VisitPrimary(const CMD_BUFFER_STATE &primary_state) {
assert(!primary_state_);
primary_state_ = &primary_state;
viewport_mask_ = primary_state.viewportMask | primary_state.viewportWithCountMask;
scissor_mask_ = primary_state.scissorMask | primary_state.scissorWithCountMask;
for (uint32_t n = 0; n < kMaxViewports; ++n) {
uint32_t bit = uint32_t(1) << n;
viewport_trashed_by_[n] = primary_state.trashedViewportMask & bit ? kTrashedByPrimary : kNotTrashed;
scissor_trashed_by_[n] = primary_state.trashedScissorMask & bit ? kTrashedByPrimary : kNotTrashed;
if (n < primary_state.dynamic_state_value.viewports.size() && viewport_mask_ & bit) {
viewports_to_inherit_[n] = primary_state.dynamic_state_value.viewports[n];
}
}
viewport_count_to_inherit_ = primary_state.dynamic_state_value.viewport_count;
scissor_count_to_inherit_ = primary_state.dynamic_state_value.scissor_count;
viewport_count_trashed_by_ = primary_state.trashedViewportCount ? kTrashedByPrimary : kNotTrashed;
scissor_count_trashed_by_ = primary_state.trashedScissorCount ? kTrashedByPrimary : kNotTrashed;
return false;
}
bool VisitSecondary(uint32_t cmd_buffer_idx, const CMD_BUFFER_STATE &secondary_state) {
bool skip = false;
if (secondary_state.inheritedViewportDepths.empty()) {
skip |= VisitSecondaryNoInheritance(cmd_buffer_idx, secondary_state);
} else {
skip |= VisitSecondaryInheritance(cmd_buffer_idx, secondary_state);
}
// See note at end of VisitSecondaryNoInheritance.
if (secondary_state.trashedViewportCount) {
viewport_count_trashed_by_ = cmd_buffer_idx;
}
if (secondary_state.trashedScissorCount) {
scissor_count_trashed_by_ = cmd_buffer_idx;
}
return skip;
}
private:
// Track state inheritance as specified by VK_NV_inherited_scissor_viewport, including states
// overwritten to undefined value by bound pipelines with non-dynamic state.
bool VisitSecondaryNoInheritance(uint32_t cmd_buffer_idx, const CMD_BUFFER_STATE &secondary_state) {
viewport_mask_ |= secondary_state.viewportMask | secondary_state.viewportWithCountMask;
scissor_mask_ |= secondary_state.scissorMask | secondary_state.scissorWithCountMask;
for (uint32_t n = 0; n < kMaxViewports; ++n) {
uint32_t bit = uint32_t(1) << n;
if ((secondary_state.viewportMask | secondary_state.viewportWithCountMask) & bit) {
if (n < secondary_state.dynamic_state_value.viewports.size()) {
viewports_to_inherit_[n] = secondary_state.dynamic_state_value.viewports[n];
}
viewport_trashed_by_[n] = kNotTrashed;
}
if ((secondary_state.scissorMask | secondary_state.scissorWithCountMask) & bit) {
scissor_trashed_by_[n] = kNotTrashed;
}
if (secondary_state.dynamic_state_value.viewport_count != 0) {
viewport_count_to_inherit_ = secondary_state.dynamic_state_value.viewport_count;
viewport_count_trashed_by_ = kNotTrashed;
}
if (secondary_state.dynamic_state_value.scissor_count != 0) {
scissor_count_to_inherit_ = secondary_state.dynamic_state_value.scissor_count;
scissor_count_trashed_by_ = kNotTrashed;
}
// Order of above vs below matters here.
if (secondary_state.trashedViewportMask & bit) {
viewport_trashed_by_[n] = cmd_buffer_idx;
}
if (secondary_state.trashedScissorMask & bit) {
scissor_trashed_by_[n] = cmd_buffer_idx;
}
// Check trashing dynamic viewport/scissor count in VisitSecondary (at end) as even secondary command buffers enabling
// viewport/scissor state inheritance may define this state statically in bound graphics pipelines.
}
return false;
}
// Validate needed inherited state as specified by VK_NV_inherited_scissor_viewport.
bool VisitSecondaryInheritance(uint32_t cmd_buffer_idx, const CMD_BUFFER_STATE &secondary_state) {
bool skip = false;
uint32_t check_viewport_count = 0, check_scissor_count = 0;
// Common code for reporting missing inherited state (for a myriad of reasons).
auto check_missing_inherit = [&](uint32_t was_ever_defined, uint32_t trashed_by, VkDynamicState state, uint32_t index = 0,
uint32_t static_use_count = 0, const VkViewport *inherited_viewport = nullptr,
const VkViewport *expected_viewport_depth = nullptr) {
if (was_ever_defined && trashed_by == kNotTrashed) {
if (state != VK_DYNAMIC_STATE_VIEWPORT) return false;
assert(inherited_viewport != nullptr && expected_viewport_depth != nullptr);
if (inherited_viewport->minDepth != expected_viewport_depth->minDepth ||
inherited_viewport->maxDepth != expected_viewport_depth->maxDepth) {
return validation_.LogError(primary_state_->commandBuffer(), "VUID-vkCmdDraw-None-07850",
"vkCmdExecuteCommands(): Draw commands in pCommandBuffers[%" PRIu32
"] (%s) consume inherited viewport %" PRIu32
" %s"
"but this state was not inherited as its depth range [%f, %f] does not match "
"pViewportDepths[%" PRIu32 "] = [%f, %f]",
unsigned(cmd_buffer_idx),
validation_.FormatHandle(secondary_state.commandBuffer()).c_str(), unsigned(index),
index >= static_use_count ? "(with count) " : "", inherited_viewport->minDepth,
inherited_viewport->maxDepth, unsigned(cmd_buffer_idx),
expected_viewport_depth->minDepth, expected_viewport_depth->maxDepth);
// akeley98 note: This VUID is not ideal; however, there isn't a more relevant VUID as
// it isn't illegal in itself to have mismatched inherited viewport depths.
// The error only occurs upon attempting to consume the viewport.
} else {
return false;
}
}
const char *state_name;
bool format_index = false;
switch (state) {
case VK_DYNAMIC_STATE_SCISSOR:
state_name = "scissor";
format_index = true;
break;
case VK_DYNAMIC_STATE_VIEWPORT:
state_name = "viewport";
format_index = true;
break;
case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
state_name = "dynamic viewport count";
break;
case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
state_name = "dynamic scissor count";
break;
default:
assert(0);
state_name = "<unknown state, report bug>";
break;
}
std::stringstream ss;
ss << "vkCmdExecuteCommands(): Draw commands in pCommandBuffers[" << cmd_buffer_idx << "] ("
<< validation_.FormatHandle(secondary_state.commandBuffer()).c_str() << ") consume inherited " << state_name << " ";
if (format_index) {
if (index >= static_use_count) {
ss << "(with count) ";
}
ss << index << " ";
}
ss << "but this state ";
if (!was_ever_defined) {
ss << "was never defined.";
} else if (trashed_by == kTrashedByPrimary) {
ss << "was left undefined after vkCmdExecuteCommands or vkCmdBindPipeline (with non-dynamic state) in "
"the calling primary command buffer.";
} else {
ss << "was left undefined after vkCmdBindPipeline (with non-dynamic state) in pCommandBuffers[" << trashed_by
<< "].";
}
return validation_.LogError(primary_state_->commandBuffer(), "VUID-vkCmdDraw-None-07850", "%s", ss.str().c_str());
};
// Check if secondary command buffer uses viewport/scissor-with-count state, and validate this state if so.
if (secondary_state.usedDynamicViewportCount) {
if (viewport_count_to_inherit_ == 0 || viewport_count_trashed_by_ != kNotTrashed) {
skip |= check_missing_inherit(viewport_count_to_inherit_, viewport_count_trashed_by_,
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT);
} else {
check_viewport_count = viewport_count_to_inherit_;
}
}
if (secondary_state.usedDynamicScissorCount) {
if (scissor_count_to_inherit_ == 0 || scissor_count_trashed_by_ != kNotTrashed) {
skip |= check_missing_inherit(scissor_count_to_inherit_, scissor_count_trashed_by_,
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT);
} else {
check_scissor_count = scissor_count_to_inherit_;
}
}
// Check the maximum of (viewports used by pipelines with static viewport count, "" dynamic viewport count)
// but limit to length of inheritedViewportDepths array and uint32_t bit width (validation layer limit).
check_viewport_count = std::min(std::min(kMaxViewports, uint32_t(secondary_state.inheritedViewportDepths.size())),
std::max(check_viewport_count, secondary_state.usedViewportScissorCount));
check_scissor_count = std::min(kMaxViewports, std::max(check_scissor_count, secondary_state.usedViewportScissorCount));
if (secondary_state.usedDynamicViewportCount &&
viewport_count_to_inherit_ > secondary_state.inheritedViewportDepths.size()) {
skip |= validation_.LogError(
primary_state_->commandBuffer(), "VUID-vkCmdDraw-None-07850",
"vkCmdExecuteCommands(): "
"Draw commands in pCommandBuffers[%" PRIu32
"] (%s) consume inherited dynamic viewport with count state "
"but the dynamic viewport count (%" PRIu32 ") exceeds the inheritance limit (viewportDepthCount=%" PRIu32 ").",
unsigned(cmd_buffer_idx), validation_.FormatHandle(secondary_state.commandBuffer()).c_str(),
unsigned(viewport_count_to_inherit_), unsigned(secondary_state.inheritedViewportDepths.size()));
}
for (uint32_t n = 0; n < check_viewport_count; ++n) {
skip |= check_missing_inherit(viewport_mask_ & uint32_t(1) << n, viewport_trashed_by_[n], VK_DYNAMIC_STATE_VIEWPORT, n,
secondary_state.usedViewportScissorCount, &viewports_to_inherit_[n],
&secondary_state.inheritedViewportDepths[n]);
}
for (uint32_t n = 0; n < check_scissor_count; ++n) {
skip |= check_missing_inherit(scissor_mask_ & uint32_t(1) << n, scissor_trashed_by_[n], VK_DYNAMIC_STATE_SCISSOR, n,
secondary_state.usedViewportScissorCount);
}
return skip;
}
};
constexpr uint32_t CoreChecks::ViewportScissorInheritanceTracker::kMaxViewports;
constexpr uint32_t CoreChecks::ViewportScissorInheritanceTracker::kNotTrashed;
constexpr uint32_t CoreChecks::ViewportScissorInheritanceTracker::kTrashedByPrimary;
bool CoreChecks::PreCallValidateCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBuffersCount,
const VkCommandBuffer *pCommandBuffers, const ErrorObject &error_obj) const {
const auto &cb_state = *GetRead<CMD_BUFFER_STATE>(commandBuffer);
bool skip = false;
vvl::unordered_set<const CMD_BUFFER_STATE *> linked_command_buffers;
ViewportScissorInheritanceTracker viewport_scissor_inheritance{*this};
if (enabled_features.inherited_viewport_scissor_features.inheritedViewportScissor2D) {
skip |= viewport_scissor_inheritance.VisitPrimary(cb_state);
}
const QueryObject *active_occlusion_query = nullptr;
for (const auto &active_query : cb_state.activeQueries) {
auto query_pool_state = Get<QUERY_POOL_STATE>(active_query.pool);
const auto queryType = query_pool_state->createInfo.queryType;
if (queryType == VK_QUERY_TYPE_OCCLUSION) {
active_occlusion_query = &active_query;
}
if (queryType != VK_QUERY_TYPE_OCCLUSION && queryType != VK_QUERY_TYPE_PIPELINE_STATISTICS) {
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-07594", commandBuffer, error_obj.location,
"query with type %s is active.", string_VkQueryType(queryType));
}
}
if (cb_state.activeRenderPass) {
if (!cb_state.activeRenderPass->UsesDynamicRendering() && cb_state.IsPrimary() &&
(cb_state.activeSubpassContents != VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS &&
cb_state.activeSubpassContents != VK_SUBPASS_CONTENTS_INLINE_AND_SECONDARY_COMMAND_BUFFERS_EXT)) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-contents-06018", objlist, error_obj.location,
"contents must be set to VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS or "
"VK_SUBPASS_CONTENTS_INLINE_AND_SECONDARY_COMMAND_BUFFERS_EXT "
"when calling vkCmdExecuteCommands() within a render pass instance begun with "
"vkCmdBeginRenderPass().");
}
if (cb_state.activeRenderPass->UsesDynamicRendering() &&
!((cb_state.activeRenderPass->use_dynamic_rendering &&
(cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info.flags &
VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR)) ||
(cb_state.activeRenderPass->use_dynamic_rendering_inherited &&
(cb_state.activeRenderPass->inheritance_rendering_info.flags &
VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR)))) {
const LogObjectList objlist(commandBuffer, cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-flags-06024", objlist, error_obj.location,
"VkRenderingInfo::flags must include "
"VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR when calling vkCmdExecuteCommands() within a "
"render pass instance begun with vkCmdBeginRendering().");
}
}
for (uint32_t i = 0; i < commandBuffersCount; i++) {
const auto &sub_cb_state = *GetRead<CMD_BUFFER_STATE>(pCommandBuffers[i]);
const Location cb_loc = error_obj.location.dot(Field::pCommandBuffers, i);
if (enabled_features.inherited_viewport_scissor_features.inheritedViewportScissor2D) {
skip |= viewport_scissor_inheritance.VisitSecondary(i, sub_cb_state);
}
if (VK_COMMAND_BUFFER_LEVEL_SECONDARY != sub_cb_state.createInfo.level) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00088", objlist, cb_loc,
"(%s) is not VK_COMMAND_BUFFER_LEVEL_SECONDARY.", FormatHandle(pCommandBuffers[i]).c_str());
} else {
if (!cb_state.activeRenderPass) {
if (sub_cb_state.beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00100", objlist, cb_loc,
"(%s) is executed outside a render pass "
"instance scope, but the Secondary Command Buffer does have the "
"VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT set in VkCommandBufferBeginInfo::flags when "
"the vkBeginCommandBuffer() was called.",
FormatHandle(pCommandBuffers[i]).c_str());
}
} else if (sub_cb_state.beginInfo.pInheritanceInfo != nullptr) {
const uint32_t inheritance_subpass = sub_cb_state.beginInfo.pInheritanceInfo->subpass;
const VkRenderPass inheritance_render_pass = sub_cb_state.beginInfo.pInheritanceInfo->renderPass;
auto secondary_rp_state = Get<RENDER_PASS_STATE>(inheritance_render_pass);
if (!(sub_cb_state.beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) {
const LogObjectList objlist(pCommandBuffers[i], cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00096", objlist, cb_loc,
"(%s) is executed within a %s "
"instance scope, but the Secondary Command Buffer does not have the "
"VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT set in VkCommandBufferBeginInfo::flags when "
"the vkBeginCommandBuffer() was called.",
FormatHandle(pCommandBuffers[i]).c_str(),
FormatHandle(cb_state.activeRenderPass->renderPass()).c_str());
} else if (sub_cb_state.beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) {
if (!cb_state.activeRenderPass->UsesDynamicRendering()) {
// Make sure render pass is compatible with parent command buffer pass if secondary command buffer has
// "render pass continue" usage flag
if (cb_state.activeRenderPass->renderPass() != secondary_rp_state->renderPass()) {
skip |= ValidateRenderPassCompatibility("primary command buffer", *cb_state.activeRenderPass.get(),
"secondary command buffer", *secondary_rp_state.get(), cb_loc,
"VUID-vkCmdExecuteCommands-pBeginInfo-06020");
}
// If framebuffer for secondary CB is not NULL, then it must match active FB from primaryCB
skip |= ValidateInheritanceInfoFramebuffer(commandBuffer, cb_state, pCommandBuffers[i], sub_cb_state,
error_obj.location);
}
// Inherit primary's activeFramebuffer, or null if using dynamic rendering,
// and while running validate functions
for (auto &function : sub_cb_state.cmd_execute_commands_functions) {
skip |= function(sub_cb_state, &cb_state, cb_state.activeFramebuffer.get());
}
}
if (!cb_state.activeRenderPass->UsesDynamicRendering() && (cb_state.GetActiveSubpass() != inheritance_subpass)) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i], cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-06019", objlist, cb_loc,
"(%s) is executed within a %s "
"instance scope begun by vkCmdBeginRenderPass(), but "
"VkCommandBufferInheritanceInfo::subpass (%" PRIu32
") does not "
"match the current subpass (%" PRIu32 ").",
FormatHandle(pCommandBuffers[i]).c_str(),
FormatHandle(cb_state.activeRenderPass->renderPass()).c_str(), inheritance_subpass,
cb_state.GetActiveSubpass());
} else if (cb_state.activeRenderPass->UsesDynamicRendering()) {
if (inheritance_render_pass != VK_NULL_HANDLE) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i], cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pBeginInfo-06025", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance scope begun "
"by vkCmdBeginRendering(), but "
"VkCommandBufferInheritanceInfo::pInheritanceInfo::renderPass is not VK_NULL_HANDLE.",
FormatHandle(pCommandBuffers[i]).c_str());
}
if (cb_state.activeRenderPass->use_dynamic_rendering && sub_cb_state.activeRenderPass &&
sub_cb_state.activeRenderPass->use_dynamic_rendering_inherited) {
const auto rendering_info = cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info;
const auto inheritance_rendering_info = sub_cb_state.activeRenderPass->inheritance_rendering_info;
if ((inheritance_rendering_info.flags &
~(VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR | VK_RENDERING_CONTENTS_INLINE_BIT_EXT)) !=
(rendering_info.flags &
~(VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR | VK_RENDERING_CONTENTS_INLINE_BIT_EXT))) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i], cb_state.activeRenderPass->renderPass());
skip |=
LogError("VUID-vkCmdExecuteCommands-flags-06026", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance scope begun "
"by vkCmdBeginRendering(), but VkCommandBufferInheritanceRenderingInfo::flags (%s) does "
"not match VkRenderingInfo::flags (%s) (excluding "
"VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR or "
"VK_RENDERING_CONTENTS_INLINE_BIT_EXT).",
FormatHandle(pCommandBuffers[i]).c_str(),
string_VkRenderingFlags(inheritance_rendering_info.flags).c_str(),
string_VkRenderingFlags(rendering_info.flags).c_str());
}
if (inheritance_rendering_info.colorAttachmentCount != rendering_info.colorAttachmentCount) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i], cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-colorAttachmentCount-06027", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance scope begun "
"by vkCmdBeginRendering(), but "
"VkCommandBufferInheritanceRenderingInfo::colorAttachmentCount (%" PRIu32
") does "
"not match VkRenderingInfo::colorAttachmentCount (%" PRIu32 ").",
FormatHandle(pCommandBuffers[i]).c_str(),
inheritance_rendering_info.colorAttachmentCount, rendering_info.colorAttachmentCount);
}
for (uint32_t color_i = 0, count = std::min(inheritance_rendering_info.colorAttachmentCount,
rendering_info.colorAttachmentCount);
color_i < count; color_i++) {
if (rendering_info.pColorAttachments[color_i].imageView == VK_NULL_HANDLE) {
if (inheritance_rendering_info.pColorAttachmentFormats[color_i] != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-imageView-07606", objlist, cb_loc,
"(%s) is executed within a dynamic render pass instance "
"scope begun "
"by vkCmdBeginRendering(), VkRenderingInfo::pColorAttachments[%" PRIu32
"].imageView is VK_NULL_HANDLE but "
"VkCommandBufferInheritanceRenderingInfo::pColorAttachmentFormats[%" PRIu32
"] is %s.",
FormatHandle(pCommandBuffers[i]).c_str(), color_i, color_i,
string_VkFormat(inheritance_rendering_info.pColorAttachmentFormats[color_i]));
}
} else {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[color_i].imageView);
if (image_view_state->create_info.format !=
inheritance_rendering_info.pColorAttachmentFormats[color_i]) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-imageView-06028", objlist, cb_loc,
"(%s) is executed within a dynamic render pass instance "
"scope begun "
"by vkCmdBeginRendering(), VkRenderingInfo::pColorAttachments[%" PRIu32
"].imageView format is %s but "
"VkCommandBufferInheritanceRenderingInfo::pColorAttachmentFormats[%" PRIu32
"] is %s.",
FormatHandle(pCommandBuffers[i]).c_str(), color_i,
string_VkFormat(image_view_state->create_info.format), color_i,
string_VkFormat(inheritance_rendering_info.pColorAttachmentFormats[color_i]));
}
}
}
if ((rendering_info.pDepthAttachment != nullptr) &&
rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView);
if (image_view_state->create_info.format != inheritance_rendering_info.depthAttachmentFormat) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pDepthAttachment-06029", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass "
"instance scope begun "
"by vkCmdBeginRendering(), but "
"VkCommandBufferInheritanceRenderingInfo::depthAttachmentFormat does "
"not match the format of the imageView in VkRenderingInfo::pDepthAttachment.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
if ((rendering_info.pStencilAttachment != nullptr) &&
rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView);
if (image_view_state->create_info.format != inheritance_rendering_info.stencilAttachmentFormat) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pStencilAttachment-06030", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass "
"instance scope begun "
"by vkCmdBeginRendering(), but "
"VkCommandBufferInheritanceRenderingInfo::stencilAttachmentFormat does "
"not match the format of the imageView in VkRenderingInfo::pStencilAttachment.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
if (rendering_info.pDepthAttachment == nullptr ||
rendering_info.pDepthAttachment->imageView == VK_NULL_HANDLE) {
VkFormat format = inheritance_rendering_info.depthAttachmentFormat;
if (format != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pDepthAttachment-06774", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass "
"instance scope begun by vkCmdBeginRendering(), and "
"VkRenderingInfo::pDepthAttachment does not define an "
"image view but VkCommandBufferInheritanceRenderingInfo::depthAttachmentFormat "
"is %s instead of VK_FORMAT_UNDEFINED.",
FormatHandle(pCommandBuffers[i]).c_str(), string_VkFormat(format));
}
}
if (rendering_info.pStencilAttachment == nullptr ||
rendering_info.pStencilAttachment->imageView == VK_NULL_HANDLE) {
VkFormat format = inheritance_rendering_info.stencilAttachmentFormat;
if (format != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pStencilAttachment-06775", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass "
"instance scope begun by vkCmdBeginRendering(), and "
"VkRenderingInfo::pStencilAttachment does not define an "
"image view but VkCommandBufferInheritanceRenderingInfo::stencilAttachmentFormat "
"is %s instead of VK_FORMAT_UNDEFINED.",
FormatHandle(pCommandBuffers[i]).c_str(), string_VkFormat(format));
}
}
if (rendering_info.viewMask != inheritance_rendering_info.viewMask) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i], cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-viewMask-06031", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance scope begun "
"by vkCmdBeginRendering(), but "
"VkCommandBufferInheritanceRenderingInfo::viewMask (%" PRIu32
") does "
"not match VkRenderingInfo::viewMask (%" PRIu32 ").",
FormatHandle(pCommandBuffers[i]).c_str(), inheritance_rendering_info.viewMask,
rendering_info.viewMask);
}
// VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV
const auto amd_sample_count =
vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(inheritance_rendering_info.pNext);
if (amd_sample_count) {
for (uint32_t index = 0; index < rendering_info.colorAttachmentCount; index++) {
if (rendering_info.pColorAttachments[index].imageView == VK_NULL_HANDLE) {
continue;
}
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[index].imageView);
if (image_view_state->samples != amd_sample_count->pColorAttachmentSamples[index]) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError(
"VUID-vkCmdExecuteCommands-pNext-06032", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance "
"scope begun "
"by vkCmdBeginRenderingKHR(), but "
"VkAttachmentSampleCountInfo(AMD/NV)::pColorAttachmentSamples at index (%" PRIu32
") "
"does "
"not match the sample count of the imageView in VkRenderingInfoKHR::pColorAttachments.",
FormatHandle(pCommandBuffers[i]).c_str(), index);
}
}
if ((rendering_info.pDepthAttachment != nullptr) &&
rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView);
if (image_view_state->samples != amd_sample_count->depthStencilAttachmentSamples) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError(
"VUID-vkCmdExecuteCommands-pNext-06033", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance "
"scope begun "
"by vkCmdBeginRenderingKHR(), but "
"VkAttachmentSampleCountInfo(AMD/NV)::depthStencilAttachmentSamples does "
"not match the sample count of the imageView in VkRenderingInfoKHR::pDepthAttachment.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
if ((rendering_info.pStencilAttachment != nullptr) &&
rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView);
if (image_view_state->samples != amd_sample_count->depthStencilAttachmentSamples) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError(
"VUID-vkCmdExecuteCommands-pNext-06034", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance "
"scope begun "
"by vkCmdBeginRenderingKHR(), but "
"VkAttachmentSampleCountInfo(AMD/NV)::depthStencilAttachmentSamples does "
"not match the sample count of the imageView in VkRenderingInfoKHR::pStencilAttachment.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
} else {
for (uint32_t index = 0; index < rendering_info.colorAttachmentCount; index++) {
if (rendering_info.pColorAttachments[index].imageView == VK_NULL_HANDLE) {
continue;
}
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[index].imageView);
if (image_view_state->samples != inheritance_rendering_info.rasterizationSamples) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError(
"VUID-vkCmdExecuteCommands-pNext-06035", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass instance "
"scope begun "
"by vkCmdBeginRenderingKHR(), but the sample count of the image view at index (%" PRIu32
") of "
"VkRenderingInfoKHR::pColorAttachments does not match "
"VkCommandBufferInheritanceRenderingInfo::rasterizationSamples.",
FormatHandle(pCommandBuffers[i]).c_str(), index);
}
}
if ((rendering_info.pDepthAttachment != nullptr) &&
rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView);
if (image_view_state->samples != inheritance_rendering_info.rasterizationSamples) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pNext-06036", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass "
"instance scope begun "
"by vkCmdBeginRenderingKHR(), but the sample count of the image view for "
"VkRenderingInfoKHR::pDepthAttachment does not match "
"VkCommandBufferInheritanceRenderingInfo::rasterizationSamples.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
if ((rendering_info.pStencilAttachment != nullptr) &&
rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) {
auto image_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView);
if (image_view_state->samples != inheritance_rendering_info.rasterizationSamples) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i],
cb_state.activeRenderPass->renderPass());
skip |= LogError("VUID-vkCmdExecuteCommands-pNext-06037", objlist, cb_loc,
"(%s) is executed within a dynamic renderpass "
"instance scope begun "
"by vkCmdBeginRenderingKHR(), but the sample count of the image view for "
"VkRenderingInfoKHR::pStencilAttachment does not match "
"VkCommandBufferInheritanceRenderingInfo::rasterizationSamples.",
FormatHandle(pCommandBuffers[i]).c_str());
}
}
}
}
}
}
}
// TODO(mlentine): Move more logic into this method
skip |= ValidateSecondaryCommandBufferState(cb_state, sub_cb_state, cb_loc);
skip |= ValidateCommandBufferState(sub_cb_state, cb_loc, 0, "VUID-vkCmdExecuteCommands-pCommandBuffers-00089");
if (!(sub_cb_state.beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) {
if (sub_cb_state.InUse()) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00091", objlist, cb_loc,
"Cannot execute pending %s without VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set.",
FormatHandle(pCommandBuffers[i]).c_str());
}
// We use an const_cast, because one cannot query a container keyed on a non-const pointer using a const pointer
if (cb_state.linkedCommandBuffers.count(const_cast<CMD_BUFFER_STATE *>(&sub_cb_state))) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00092", objlist, cb_loc,
"Cannot execute %s without VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT "
"set if previously executed in %s",
FormatHandle(pCommandBuffers[i]).c_str(), FormatHandle(commandBuffer).c_str());
}
const auto insert_pair = linked_command_buffers.insert(&sub_cb_state);
if (!insert_pair.second) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-pCommandBuffers-00093", objlist, cb_loc,
"Cannot duplicate %s in pCommandBuffers without "
"VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set.",
FormatHandle(commandBuffer).c_str());
}
}
if (!cb_state.activeQueries.empty() && !enabled_features.core.inheritedQueries) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-00101", objlist, cb_loc,
"cannot be submitted with a query in flight and "
"inherited queries not supported on this device.");
}
// Validate initial layout uses vs. the primary cmd buffer state
// Novel Valid usage: "UNASSIGNED-vkCmdExecuteCommands-commandBuffer-00001"
// initial layout usage of secondary command buffers resources must match parent command buffer
for (const auto &sub_layout_map_entry : sub_cb_state.image_layout_map) {
const auto *image_state = sub_layout_map_entry.first;
const auto image = image_state->image();
const auto *cb_subres_map = cb_state.GetImageSubresourceLayoutMap(*image_state);
// Const getter can be null in which case we have nothing to check against for this image...
if (!cb_subres_map) continue;
const auto &sub_layout_map = sub_layout_map_entry.second->GetLayoutMap();
const auto &cb_layout_map = cb_subres_map->GetLayoutMap();
for (sparse_container::parallel_iterator<const ImageSubresourceLayoutMap::LayoutMap> iter(sub_layout_map, cb_layout_map,
0);
!iter->range.empty(); ++iter) {
VkImageLayout cb_layout = kInvalidLayout, sub_layout = kInvalidLayout;
const char *layout_type;
if (!iter->pos_A->valid || !iter->pos_B->valid) continue;
// pos_A denotes the sub CB map in the parallel iterator
sub_layout = iter->pos_A->lower_bound->second.initial_layout;
if (VK_IMAGE_LAYOUT_UNDEFINED == sub_layout) continue; // secondary doesn't care about current or initial
// pos_B denotes the main CB map in the parallel iterator
const auto &cb_layout_state = iter->pos_B->lower_bound->second;
if (cb_layout_state.current_layout != kInvalidLayout) {
layout_type = "current";
cb_layout = cb_layout_state.current_layout;
} else if (cb_layout_state.initial_layout != kInvalidLayout) {
layout_type = "initial";
cb_layout = cb_layout_state.initial_layout;
} else {
continue;
}
if (sub_layout != cb_layout) {
// We can report all the errors for the intersected range directly
for (auto index = iter->range.begin; index < iter->range.end; index++) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
const auto subresource = image_state->subresource_encoder.Decode(index);
skip |= LogError("UNASSIGNED-vkCmdExecuteCommands-commandBuffer-00001", objlist, cb_loc,
"was executed using %s (subresource: aspectMask 0x%x array layer %" PRIu32
", "
"mip level %" PRIu32 ") which expects layout %s--instead, image %s layout is %s.",
FormatHandle(image).c_str(), subresource.aspectMask, subresource.arrayLayer,
subresource.mipLevel, string_VkImageLayout(sub_layout), layout_type,
string_VkImageLayout(cb_layout));
}
}
}
}
// All commands buffers involved must be protected or unprotected
if ((cb_state.unprotected == false) && (sub_cb_state.unprotected == true)) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-01820", objlist, cb_loc,
"(%s) is a unprotected while primary command buffer (%s) is protected.",
FormatHandle(pCommandBuffers[i]).c_str(), FormatHandle(commandBuffer).c_str());
} else if ((cb_state.unprotected == true) && (sub_cb_state.unprotected == false)) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-01821", objlist, cb_loc,
"(%s) is a protected while primary command buffer (%s) is unprotected.",
FormatHandle(pCommandBuffers[i]).c_str(), FormatHandle(commandBuffer).c_str());
}
if (active_occlusion_query) {
if (sub_cb_state.inheritanceInfo.occlusionQueryEnable != VK_TRUE) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-00102", objlist, cb_loc,
"(%s) was recorded with VkCommandBufferInheritanceInfo::occlusionQueryEnable set to VK_FALSE, but "
"primary command buffer %s has an active occlusion query",
FormatHandle(pCommandBuffers[i]).c_str(), FormatHandle(commandBuffer).c_str());
}
if ((sub_cb_state.inheritanceInfo.queryFlags & active_occlusion_query->control_flags) !=
active_occlusion_query->control_flags) {
const LogObjectList objlist(commandBuffer, pCommandBuffers[i]);
skip |= LogError("VUID-vkCmdExecuteCommands-commandBuffer-00103", objlist, cb_loc,
"(%s) was recorded with VkCommandBufferInheritanceInfo::queryFlags 0X%" PRIx32
", but primary command buffer %s has an active occlusion query with VkQueryControlFlags 0X%" PRIx32
".",
FormatHandle(pCommandBuffers[i]).c_str(), sub_cb_state.inheritanceInfo.queryFlags,
FormatHandle(commandBuffer).c_str(), active_occlusion_query->control_flags);
}
}
}
if (cb_state.transform_feedback_active) {
skip |=
LogError("VUID-vkCmdExecuteCommands-None-02286", commandBuffer, error_obj.location, "transform feedback is active.");
}
skip |= ValidateCmd(cb_state, error_obj.location);
return skip;
}
bool CoreChecks::PreCallValidateCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT *pMarkerInfo,
const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
return ValidateCmd(*cb_state, error_obj.location);
}
bool CoreChecks::PreCallValidateCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer, const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
return ValidateCmd(*cb_state, error_obj.location);
}
bool CoreChecks::ValidateCmdDrawStrideWithStruct(const CMD_BUFFER_STATE &cb_state, const std::string &vuid, const uint32_t stride,
Struct struct_name, const uint32_t struct_size, const Location &loc) const {
bool skip = false;
static const int condition_multiples = 0b0011;
if ((stride & condition_multiples) || (stride < struct_size)) {
skip |= LogError(vuid, cb_state.GetObjectList(VK_PIPELINE_BIND_POINT_GRAPHICS), loc.dot(Field::stride),
"%" PRIu32 " is invalid or less than sizeof(%s) %" PRIu32 ".", stride, String(struct_name), struct_size);
}
return skip;
}
bool CoreChecks::ValidateCmdDrawStrideWithBuffer(const CMD_BUFFER_STATE &cb_state, const std::string &vuid, const uint32_t stride,
Struct struct_name, const uint32_t struct_size, const uint32_t drawCount,
const VkDeviceSize offset, const BUFFER_STATE *buffer_state,
const Location &loc) const {
bool skip = false;
uint64_t validation_value = stride * (drawCount - 1) + offset + struct_size;
if (validation_value > buffer_state->createInfo.size) {
LogObjectList objlist = cb_state.GetObjectList(VK_PIPELINE_BIND_POINT_GRAPHICS);
objlist.add(buffer_state->buffer());
skip |=
LogError(vuid, objlist, loc,
"stride (%" PRIu32 ") * [drawCount (%" PRIu32 ") - 1] + offset (%" PRIu64 ") + sizeof(%s) (%" PRIu32
") is %" PRIu64 ", which is greater than the buffer size (%" PRIu64 ").",
stride, drawCount, offset, String(struct_name), struct_size, validation_value, buffer_state->createInfo.size);
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBindTransformFeedbackBuffersEXT(VkCommandBuffer commandBuffer, uint32_t firstBinding,
uint32_t bindingCount, const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes,
const ErrorObject &error_obj) const {
bool skip = false;
if (!enabled_features.transform_feedback_features.transformFeedback) {
skip |= LogError("VUID-vkCmdBindTransformFeedbackBuffersEXT-transformFeedback-02355", commandBuffer, error_obj.location,
"transformFeedback feature was not enabled.");
}
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (cb_state->transform_feedback_active) {
skip |= LogError("VUID-vkCmdBindTransformFeedbackBuffersEXT-None-02365", commandBuffer, error_obj.location,
"transform feedback is active.");
}
for (uint32_t i = 0; i < bindingCount; ++i) {
const Location buffer_loc = error_obj.location.dot(Field::pBuffers, i);
auto buffer_state = Get<BUFFER_STATE>(pBuffers[i]);
assert(buffer_state != nullptr);
if (pOffsets[i] >= buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, pBuffers[i]);
skip |= LogError("VUID-vkCmdBindTransformFeedbackBuffersEXT-pOffsets-02358", objlist,
error_obj.location.dot(Field::pOffsets, i),
"(%" PRIu64 ") is greater than or equal to the size of pBuffers[%" PRIu32 "] (%" PRIu64 ").",
pOffsets[i], i, buffer_state->createInfo.size);
}
if ((buffer_state->usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT) == 0) {
const LogObjectList objlist(commandBuffer, pBuffers[i]);
skip |= LogError("VUID-vkCmdBindTransformFeedbackBuffersEXT-pBuffers-02360", objlist, buffer_loc,
"was created with %s.", string_VkBufferUsageFlags2KHR(buffer_state->usage).c_str());
}
// pSizes is optional and may be nullptr. Also might be VK_WHOLE_SIZE which VU don't apply
if ((pSizes != nullptr) && (pSizes[i] != VK_WHOLE_SIZE)) {
// only report one to prevent redundant error if the size is larger since adding offset will be as well
if (pSizes[i] > buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, pBuffers[i]);
skip |= LogError("VUID-vkCmdBindTransformFeedbackBuffersEXT-pSizes-02362", objlist,
error_obj.location.dot(Field::pSizes, i),
"(%" PRIu64 ") is greater than the size of pBuffers[%" PRIu32 "](%" PRIu64 ").", pSizes[i], i,
buffer_state->createInfo.size);
} else if (pOffsets[i] + pSizes[i] > buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, pBuffers[i]);
skip |= LogError("VUID-vkCmdBindTransformFeedbackBuffersEXT-pOffsets-02363", objlist, error_obj.location,
"The sum of pOffsets[%" PRIu32 "] (%" PRIu64 ") and pSizes[%" PRIu32 "] (%" PRIu64
") is greater than the size of pBuffers[%" PRIu32 "] (%" PRIu64 ").",
i, pOffsets[i], i, pSizes[i], i, buffer_state->createInfo.size);
}
}
skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *buffer_state, buffer_loc,
"VUID-vkCmdBindTransformFeedbackBuffersEXT-pBuffers-02364");
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBeginTransformFeedbackEXT(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer,
uint32_t counterBufferCount, const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets,
const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
skip |= ValidateCmd(*cb_state, error_obj.location);
if (skip) return skip; // basic validation failed, might have null pointers
if (!enabled_features.transform_feedback_features.transformFeedback) {
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-transformFeedback-02366", commandBuffer, error_obj.location,
"transformFeedback feature was not enabled.");
}
const auto *pipe = cb_state->lastBound[VK_PIPELINE_BIND_POINT_GRAPHICS].pipeline_state;
if (!pipe) {
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-None-06233", commandBuffer, error_obj.location,
"No graphics pipeline has been bound yet.");
} else if (pipe->pre_raster_state) {
for (const auto &stage_state : pipe->stage_states) {
if (!stage_state.entrypoint || stage_state.GetStage() != pipe->pre_raster_state->last_stage) {
continue;
}
if (!stage_state.entrypoint->execution_mode.Has(ExecutionModeSet::xfb_bit)) {
const LogObjectList objlist(commandBuffer, pipe->Handle());
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-None-04128", objlist, error_obj.location,
"The last bound pipeline (%s) has no Xfb Execution Mode for stage %s.",
FormatHandle(pipe->Handle()).c_str(),
string_VkShaderStageFlagBits(pipe->pre_raster_state->last_stage));
}
}
}
if (cb_state->transform_feedback_active) {
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-None-02367", commandBuffer, error_obj.location,
"transform feedback is active.");
}
const auto &rp_ci = cb_state->activeRenderPass->createInfo;
for (uint32_t i = 0; i < rp_ci.subpassCount; ++i) {
// When a subpass uses a non-zero view mask, multiview functionality is considered to be enabled
if (rp_ci.pSubpasses[i].viewMask > 0) {
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-None-02373", commandBuffer, error_obj.location,
"active render pass (%s) has multiview enabled.",
FormatHandle(cb_state->activeRenderPass->renderPass()).c_str());
break;
}
}
// pCounterBuffers and pCounterBufferOffsets are optional and may be nullptr. Additionaly, pCounterBufferOffsets must be nullptr
// if pCounterBuffers is nullptr.
if (pCounterBuffers == nullptr) {
if (pCounterBufferOffsets != nullptr) {
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-pCounterBuffer-02371", commandBuffer, error_obj.location,
"pCounterBuffers is NULL and pCounterBufferOffsets is not NULL.");
}
} else {
for (uint32_t i = 0; i < counterBufferCount; ++i) {
if (pCounterBuffers[i] == VK_NULL_HANDLE) {
continue;
}
auto buffer_state = Get<BUFFER_STATE>(pCounterBuffers[i]);
assert(buffer_state != nullptr);
if (pCounterBufferOffsets != nullptr && pCounterBufferOffsets[i] + 4 > buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, pCounterBuffers[i]);
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-pCounterBufferOffsets-02370", objlist,
error_obj.location.dot(Field::pCounterBuffers, i),
"is not large enough to hold 4 bytes at pCounterBufferOffsets[%" PRIu32 "](0x%" PRIx64 ").", i,
pCounterBufferOffsets[i]);
}
if ((buffer_state->usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT) == 0) {
const LogObjectList objlist(commandBuffer, pCounterBuffers[i]);
skip |= LogError("VUID-vkCmdBeginTransformFeedbackEXT-pCounterBuffers-02372", objlist,
error_obj.location.dot(Field::pCounterBuffers, i), "was created with %s.",
string_VkBufferUsageFlags2KHR(buffer_state->usage).c_str());
}
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdEndTransformFeedbackEXT(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer,
uint32_t counterBufferCount, const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets,
const ErrorObject &error_obj) const {
bool skip = false;
if (!enabled_features.transform_feedback_features.transformFeedback) {
skip |= LogError("VUID-vkCmdEndTransformFeedbackEXT-transformFeedback-02374", commandBuffer, error_obj.location,
"transformFeedback feature was not enabled.");
}
{
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state->transform_feedback_active) {
skip |= LogError("VUID-vkCmdEndTransformFeedbackEXT-None-02375", commandBuffer, error_obj.location,
"transform feedback is not active.");
}
}
// pCounterBuffers and pCounterBufferOffsets are optional and may be nullptr. Additionaly, pCounterBufferOffsets must be nullptr
// if pCounterBuffers is nullptr.
if (pCounterBuffers == nullptr) {
if (pCounterBufferOffsets != nullptr) {
skip |= LogError("VUID-vkCmdEndTransformFeedbackEXT-pCounterBuffer-02379", commandBuffer, error_obj.location,
"pCounterBuffers is NULL and pCounterBufferOffsets is not NULL.");
}
} else {
for (uint32_t i = 0; i < counterBufferCount; ++i) {
if (pCounterBuffers[i] == VK_NULL_HANDLE) {
continue;
}
auto buffer_state = Get<BUFFER_STATE>(pCounterBuffers[i]);
assert(buffer_state != nullptr);
if (pCounterBufferOffsets != nullptr && pCounterBufferOffsets[i] + 4 > buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, pCounterBuffers[i]);
skip |= LogError("VUID-vkCmdEndTransformFeedbackEXT-pCounterBufferOffsets-02378", objlist,
error_obj.location.dot(Field::pCounterBuffers, i),
"is not large enough to hold 4 bytes at pCounterBufferOffsets[%" PRIu32 "](0x%" PRIx64 ").", i,
pCounterBufferOffsets[i]);
}
if ((buffer_state->usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT) == 0) {
const LogObjectList objlist(commandBuffer, pCounterBuffers[i]);
skip |= LogError("VUID-vkCmdEndTransformFeedbackEXT-pCounterBuffers-02380", objlist,
error_obj.location.dot(Field::pCounterBuffers, i), "was created with %s.",
string_VkBufferUsageFlags2KHR(buffer_state->usage).c_str());
}
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount,
const VkBuffer *pBuffers, const VkDeviceSize *pOffsets,
const VkDeviceSize *pSizes, const VkDeviceSize *pStrides,
const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
bool skip = false;
skip |= ValidateCmd(*cb_state, error_obj.location);
for (uint32_t i = 0; i < bindingCount; ++i) {
auto buffer_state = Get<BUFFER_STATE>(pBuffers[i]);
if (!buffer_state) {
continue; // Can be null handle if using nullDescriptor
}
const LogObjectList objlist(commandBuffer, pBuffers[i]);
const Location buffer_loc = error_obj.location.dot(Field::pBuffers, i);
skip |= ValidateBufferUsageFlags(objlist, *buffer_state, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, true,
"VUID-vkCmdBindVertexBuffers2-pBuffers-03359", buffer_loc);
skip |=
ValidateMemoryIsBoundToBuffer(commandBuffer, *buffer_state, buffer_loc, "VUID-vkCmdBindVertexBuffers2-pBuffers-03360");
const VkDeviceSize offset = pOffsets[i];
if (pSizes) {
if (offset >= buffer_state->createInfo.size) {
skip |= LogError("VUID-vkCmdBindVertexBuffers2-pOffsets-03357", objlist, error_obj.location.dot(Field::pOffsets, i),
"(0x%" PRIu64 ") is beyond the end of the buffer of size (%" PRIu64 ").", offset,
buffer_state->createInfo.size);
}
const VkDeviceSize size = pSizes[i];
if (size == VK_WHOLE_SIZE) {
if (!enabled_features.maintenance5_features.maintenance5) {
skip |= LogError("VUID-vkCmdBindVertexBuffers2-pSizes-03358", objlist, error_obj.location.dot(Field::pSizes, i),
"is VK_WHOLE_SIZE, which is not valid in this context. This can be fixed by enabling the "
"VkPhysicalDeviceMaintenance5FeaturesKHR::maintenance5 feature.");
}
} else if (offset + size > buffer_state->createInfo.size) {
skip |= LogError("VUID-vkCmdBindVertexBuffers2-pSizes-03358", objlist, error_obj.location.dot(Field::pOffsets, i),
"(%" PRIu64 ") + pSizes[%" PRIu32 "] (%" PRIu64
") is beyond the end of the buffer of size (%" PRIu64 ").",
offset, i, size, buffer_state->createInfo.size);
}
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBindVertexBuffers2EXT(VkCommandBuffer commandBuffer, uint32_t firstBinding,
uint32_t bindingCount, const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes,
const VkDeviceSize *pStrides, const ErrorObject &error_obj) const {
return PreCallValidateCmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides,
error_obj);
}
bool CoreChecks::PreCallValidateCmdBeginConditionalRenderingEXT(
VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT *pConditionalRenderingBegin,
const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (cb_state && cb_state->conditional_rendering_active) {
skip |= LogError("VUID-vkCmdBeginConditionalRenderingEXT-None-01980", commandBuffer, error_obj.location,
"Conditional rendering is already active.");
}
if (pConditionalRenderingBegin) {
auto buffer_state = Get<BUFFER_STATE>(pConditionalRenderingBegin->buffer);
if (buffer_state) {
if ((buffer_state->usage & VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT) == 0) {
const LogObjectList objlist(commandBuffer, buffer_state->buffer());
skip |= LogError("VUID-VkConditionalRenderingBeginInfoEXT-buffer-01982", objlist,
error_obj.location.dot(Field::pConditionalRenderingBegin).dot(Field::buffer),
"(%s) was created with %s.", FormatHandle(pConditionalRenderingBegin->buffer).c_str(),
string_VkBufferUsageFlags2KHR(buffer_state->usage).c_str());
}
if (pConditionalRenderingBegin->offset + 4 > buffer_state->createInfo.size) {
const LogObjectList objlist(commandBuffer, buffer_state->buffer());
skip |= LogError("VUID-VkConditionalRenderingBeginInfoEXT-offset-01983", objlist,
error_obj.location.dot(Field::pConditionalRenderingBegin).dot(Field::offset),
"(%" PRIu64 ") + 4 bytes is not less than the size of pConditionalRenderingBegin->buffer (%" PRIu64
").",
pConditionalRenderingBegin->offset, buffer_state->createInfo.size);
}
}
}
return skip;
}
bool CoreChecks::PreCallValidateCmdEndConditionalRenderingEXT(VkCommandBuffer commandBuffer, const ErrorObject &error_obj) const {
bool skip = false;
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
if (!cb_state) {
return skip;
}
if (!cb_state->conditional_rendering_active) {
skip |= LogError("VUID-vkCmdEndConditionalRenderingEXT-None-01985", commandBuffer, error_obj.location,
"Conditional rendering is not active.");
}
if (!cb_state->conditional_rendering_inside_render_pass && cb_state->activeRenderPass != nullptr) {
skip |= LogError("VUID-vkCmdEndConditionalRenderingEXT-None-01986", commandBuffer, error_obj.location,
"Conditional rendering was begun outside outside of a render "
"pass instance, but a render pass instance is currently active in the command buffer.");
}
if (cb_state->conditional_rendering_inside_render_pass && cb_state->activeRenderPass != nullptr &&
cb_state->conditional_rendering_subpass != cb_state->GetActiveSubpass()) {
skip |= LogError("VUID-vkCmdEndConditionalRenderingEXT-None-01987", commandBuffer, error_obj.location,
"Conditional rendering was begun in subpass %" PRIu32 ", but the current subpass is %" PRIu32 ".",
cb_state->conditional_rendering_subpass, cb_state->GetActiveSubpass());
}
return skip;
}
bool CoreChecks::PreCallValidateCmdBindShadingRateImageNV(VkCommandBuffer commandBuffer, VkImageView imageView,
VkImageLayout imageLayout, const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
bool skip = false;
skip |= ValidateCmd(*cb_state, error_obj.location);
if (!enabled_features.shading_rate_image_features.shadingRateImage) {
skip |= LogError("VUID-vkCmdBindShadingRateImageNV-None-02058", commandBuffer, error_obj.location,
"The shadingRateImage feature is disabled.");
}
if (imageView == VK_NULL_HANDLE) {
return skip;
}
auto view_state = Get<IMAGE_VIEW_STATE>(imageView);
if (!view_state) {
const LogObjectList objlist(commandBuffer, imageView);
skip |= LogError("VUID-vkCmdBindShadingRateImageNV-imageView-02059", objlist, error_obj.location,
"If imageView is not VK_NULL_HANDLE, it must be a valid "
"VkImageView handle.");
return skip;
}
const auto &ivci = view_state->create_info;
if (ivci.viewType != VK_IMAGE_VIEW_TYPE_2D && ivci.viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
const LogObjectList objlist(commandBuffer, imageView);
skip |= LogError("VUID-vkCmdBindShadingRateImageNV-imageView-02059", objlist, error_obj.location,
"If imageView is not VK_NULL_HANDLE, it must be a valid "
"VkImageView handle of type VK_IMAGE_VIEW_TYPE_2D or VK_IMAGE_VIEW_TYPE_2D_ARRAY.");
}
if (ivci.format != VK_FORMAT_R8_UINT) {
const LogObjectList objlist(commandBuffer, imageView);
skip |= LogError("VUID-vkCmdBindShadingRateImageNV-imageView-02060", objlist, error_obj.location,
"If imageView is not VK_NULL_HANDLE, it must have a format of "
"VK_FORMAT_R8_UINT but is %s.",
string_VkFormat(ivci.format));
}
const auto *image_state = view_state->image_state.get();
auto usage = image_state->createInfo.usage;
if (!(usage & VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV)) {
const LogObjectList objlist(commandBuffer, imageView);
skip |= LogError("VUID-vkCmdBindShadingRateImageNV-imageView-02061", objlist, error_obj.location,
"If imageView is not VK_NULL_HANDLE, the image must have been "
"created with VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV set.");
}
// XXX TODO: While the VUID says "each subresource", only the base mip level is
// actually used. Since we don't have an existing convenience function to iterate
// over all mip levels, just don't bother with non-base levels.
const VkImageSubresourceRange &range = view_state->normalized_subresource_range;
VkImageSubresourceLayers subresource = {range.aspectMask, range.baseMipLevel, range.baseArrayLayer, range.layerCount};
if (image_state) {
skip |= VerifyImageLayoutSubresource(*cb_state, *image_state, subresource, imageLayout,
VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV, error_obj.location.dot(Field::imageView),
"VUID-vkCmdBindShadingRateImageNV-imageLayout-02063",
"VUID-vkCmdBindShadingRateImageNV-imageView-02062");
}
return skip;
}
void CoreChecks::PostCallRecordCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT *pLabelInfo,
const RecordObject &record_obj) {
auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
cb_state->BeginLabel();
}
bool CoreChecks::PreCallValidateCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const ErrorObject &error_obj) const {
auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
bool skip = false;
if (cb_state->IsPrimary() || enabled_features.nested_command_buffer_features.nestedCommandBuffer) {
return skip;
}
if (cb_state->LabelStackDepth() < 1) {
skip |= LogError("VUID-vkCmdEndDebugUtilsLabelEXT-commandBuffer-01913", commandBuffer, error_obj.location,
"called without a corresponding vkCmdBeginDebugUtilsLabelEXT first");
}
return skip;
}
void CoreChecks::PostCallRecordCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const RecordObject &record_obj) {
auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer);
assert(cb_state);
cb_state->EndLabel();
}