| /* Copyright (c) 2015-2022 The Khronos Group Inc. |
| * Copyright (c) 2015-2022 Valve Corporation |
| * Copyright (c) 2015-2022 LunarG, Inc. |
| * Copyright (C) 2015-2022 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. |
| * |
| * Author: Cody Northrop <cnorthrop@google.com> |
| * Author: Michael Lentine <mlentine@google.com> |
| * Author: Tobin Ehlis <tobine@google.com> |
| * Author: Chia-I Wu <olv@google.com> |
| * Author: Chris Forbes <chrisf@ijw.co.nz> |
| * Author: Mark Lobodzinski <mark@lunarg.com> |
| * Author: Ian Elliott <ianelliott@google.com> |
| * Author: Dave Houlton <daveh@lunarg.com> |
| * Author: Dustin Graves <dustin@lunarg.com> |
| * Author: Jeremy Hayes <jeremy@lunarg.com> |
| * Author: Jon Ashburn <jon@lunarg.com> |
| * Author: Karl Schultz <karl@lunarg.com> |
| * Author: Mark Young <marky@lunarg.com> |
| * Author: Mike Schuchardt <mikes@lunarg.com> |
| * Author: Mike Weiblen <mikew@lunarg.com> |
| * Author: Tony Barbour <tony@LunarG.com> |
| * Author: John Zulauf <jzulauf@lunarg.com> |
| * Author: Shannon McPherson <shannon@lunarg.com> |
| * Author: Jeremy Kniager <jeremyk@lunarg.com> |
| * Author: Tobias Hector <tobias.hector@amd.com> |
| * Author: Jeremy Gebben <jeremyg@lunarg.com> |
| */ |
| |
| #include <algorithm> |
| #include <array> |
| #include <assert.h> |
| #include <cmath> |
| #include <fstream> |
| #include <iostream> |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <mutex> |
| #include <set> |
| #include <sstream> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <string.h> |
| #include <string> |
| #include <valarray> |
| |
| #if defined(__linux__) || defined(__FreeBSD__) |
| |
| #include <unistd.h> |
| #include <sys/types.h> |
| #endif |
| |
| #include "vk_loader_platform.h" |
| #include "vk_enum_string_helper.h" |
| #include "chassis.h" |
| #include "convert_to_renderpass2.h" |
| #include "core_validation.h" |
| #include "buffer_validation.h" |
| #include "shader_validation.h" |
| #include "vk_layer_utils.h" |
| #include "sync_utils.h" |
| #include "sync_vuid_maps.h" |
| |
| // these templates are defined in buffer_validation.cpp so we need to pull in the explicit instantiations from there |
| extern template void CoreChecks::TransitionImageLayouts(CMD_BUFFER_STATE *cb_state, uint32_t barrier_count, |
| const VkImageMemoryBarrier *barrier); |
| extern template void CoreChecks::TransitionImageLayouts(CMD_BUFFER_STATE *cb_state, uint32_t barrier_count, |
| const VkImageMemoryBarrier2KHR *barrier); |
| extern template bool CoreChecks::ValidateImageBarrierAttachment(const Location &loc, CMD_BUFFER_STATE const *cb_state, |
| const FRAMEBUFFER_STATE *framebuffer, uint32_t active_subpass, |
| const safe_VkSubpassDescription2 &sub_desc, |
| const VkRenderPass rp_handle, |
| const VkImageMemoryBarrier &img_barrier, |
| const CMD_BUFFER_STATE *primary_cb_state) const; |
| extern template bool CoreChecks::ValidateImageBarrierAttachment(const Location &loc, CMD_BUFFER_STATE const *cb_state, |
| const FRAMEBUFFER_STATE *framebuffer, uint32_t active_subpass, |
| const safe_VkSubpassDescription2 &sub_desc, |
| const VkRenderPass rp_handle, |
| const VkImageMemoryBarrier2KHR &img_barrier, |
| const CMD_BUFFER_STATE *primary_cb_state) const; |
| |
| using std::max; |
| using std::string; |
| using std::stringstream; |
| using std::unique_ptr; |
| using std::vector; |
| |
| ReadLockGuard CoreChecks::ReadLock() { |
| if (fine_grained_locking) { |
| return ReadLockGuard(validation_object_mutex, std::defer_lock); |
| } else { |
| return ReadLockGuard(validation_object_mutex); |
| } |
| } |
| |
| WriteLockGuard CoreChecks::WriteLock() { |
| if (fine_grained_locking) { |
| return WriteLockGuard(validation_object_mutex, std::defer_lock); |
| } else { |
| return WriteLockGuard(validation_object_mutex); |
| } |
| } |
| |
| // For given mem object, verify that it is not null or UNBOUND, if it is, report error. Return skip value. |
| template <typename T1> |
| bool CoreChecks::VerifyBoundMemoryIsValid(const DEVICE_MEMORY_STATE *mem_state, const T1 object, |
| const VulkanTypedHandle &typed_handle, const char *api_name, |
| const char *error_code) const { |
| return VerifyBoundMemoryIsValid<T1, SimpleErrorLocation>(mem_state, object, typed_handle, {api_name, error_code}); |
| } |
| |
| template <typename T1, typename LocType> |
| bool CoreChecks::VerifyBoundMemoryIsValid(const DEVICE_MEMORY_STATE *mem_state, const T1 object, |
| const VulkanTypedHandle &typed_handle, const LocType &location) const { |
| bool result = false; |
| auto type_name = object_string[typed_handle.type]; |
| if (!mem_state) { |
| result |= LogError(object, location.Vuid(), |
| "%s: %s used with no memory bound. Memory should be bound by calling vkBind%sMemory().", |
| location.FuncName(), report_data->FormatHandle(typed_handle).c_str(), type_name + 2); |
| } else if (mem_state->Destroyed()) { |
| result |= LogError(object, location.Vuid(), |
| "%s: %s used with no memory bound and previously bound memory was freed. Memory must not be freed " |
| "prior to this operation.", |
| location.FuncName(), report_data->FormatHandle(typed_handle).c_str()); |
| } |
| return result; |
| } |
| |
| // Check to see if memory was ever bound to this image |
| |
| bool CoreChecks::ValidateMemoryIsBoundToImage(const IMAGE_STATE *image_state, const Location &loc) const { |
| using LocationAdapter = core_error::LocationVuidAdapter<sync_vuid_maps::GetImageBarrierVUIDFunctor>; |
| return ValidateMemoryIsBoundToImage<LocationAdapter>(image_state, LocationAdapter(loc, sync_vuid_maps::ImageError::kNoMemory)); |
| } |
| |
| bool CoreChecks::ValidateMemoryIsBoundToImage(const IMAGE_STATE *image_state, const char *api_name, const char *error_code) const { |
| return ValidateMemoryIsBoundToImage<SimpleErrorLocation>(image_state, SimpleErrorLocation(api_name, error_code)); |
| } |
| |
| template <typename LocType> |
| bool CoreChecks::ValidateMemoryIsBoundToImage(const IMAGE_STATE *image_state, const LocType &location) const { |
| bool result = false; |
| if (image_state->create_from_swapchain != VK_NULL_HANDLE) { |
| if (!image_state->bind_swapchain) { |
| LogObjectList objlist(image_state->image()); |
| objlist.add(image_state->create_from_swapchain); |
| result |= LogError( |
| objlist, location.Vuid(), |
| "%s: %s is created by %s, and the image should be bound by calling vkBindImageMemory2(), and the pNext chain " |
| "includes VkBindImageMemorySwapchainInfoKHR.", |
| location.FuncName(), report_data->FormatHandle(image_state->image()).c_str(), |
| report_data->FormatHandle(image_state->create_from_swapchain).c_str()); |
| } else if (image_state->create_from_swapchain != image_state->bind_swapchain->swapchain()) { |
| LogObjectList objlist(image_state->image()); |
| objlist.add(image_state->create_from_swapchain); |
| objlist.add(image_state->bind_swapchain->Handle()); |
| result |= |
| LogError(objlist, location.Vuid(), |
| "%s: %s is created by %s, but the image is bound by %s. The image should be created and bound by the same " |
| "swapchain", |
| location.FuncName(), report_data->FormatHandle(image_state->image()).c_str(), |
| report_data->FormatHandle(image_state->create_from_swapchain).c_str(), |
| report_data->FormatHandle(image_state->bind_swapchain->Handle()).c_str()); |
| } |
| } else if (image_state->IsExternalAHB()) { |
| // TODO look into how to properly check for a valid bound memory for an external AHB |
| } else if (0 == (static_cast<uint32_t>(image_state->createInfo.flags) & VK_IMAGE_CREATE_SPARSE_BINDING_BIT)) { |
| result |= VerifyBoundMemoryIsValid(image_state->MemState(), image_state->image(), image_state->Handle(), location); |
| } |
| return result; |
| } |
| |
| // Check to see if memory was bound to this buffer |
| bool CoreChecks::ValidateMemoryIsBoundToBuffer(const BUFFER_STATE *buffer_state, const char *api_name, |
| const char *error_code) const { |
| bool result = false; |
| if (0 == (static_cast<uint32_t>(buffer_state->createInfo.flags) & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)) { |
| result |= VerifyBoundMemoryIsValid(buffer_state->MemState(), buffer_state->buffer(), buffer_state->Handle(), api_name, |
| error_code); |
| } |
| return result; |
| } |
| |
| // Check to see if host-visible memory was bound to this buffer |
| bool CoreChecks::ValidateHostVisibleMemoryIsBoundToBuffer(const BUFFER_STATE *buffer_state, const char *api_name, |
| const char *error_code) const { |
| bool result = false; |
| result |= ValidateMemoryIsBoundToBuffer(buffer_state, api_name, error_code); |
| if (!result) { |
| const auto mem_state = buffer_state->MemState(); |
| if (mem_state) { |
| if ((phys_dev_mem_props.memoryTypes[mem_state->alloc_info.memoryTypeIndex].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) { |
| result |= LogError(buffer_state->buffer(), error_code, "%s: %s used with memory that is not host visible.", |
| api_name, report_data->FormatHandle(buffer_state->Handle()).c_str()); |
| } |
| } |
| } |
| return result; |
| } |
| |
| // Check to see if memory was bound to this acceleration structure |
| bool CoreChecks::ValidateMemoryIsBoundToAccelerationStructure(const ACCELERATION_STRUCTURE_STATE *as_state, const char *api_name, |
| const char *error_code) const { |
| return VerifyBoundMemoryIsValid(as_state->MemState(), as_state->acceleration_structure(), as_state->Handle(), api_name, |
| error_code); |
| } |
| |
| // Check to see if memory was bound to this acceleration structure |
| bool CoreChecks::ValidateMemoryIsBoundToAccelerationStructure(const ACCELERATION_STRUCTURE_STATE_KHR *as_state, |
| const char *api_name, const char *error_code) const { |
| return VerifyBoundMemoryIsValid(as_state->buffer_state->MemState(), as_state->acceleration_structure(), as_state->Handle(), api_name, |
| error_code); |
| } |
| |
| // Valid usage checks for a call to SetMemBinding(). |
| // For NULL mem case, output warning |
| // Make sure given object is in global object map |
| // IF a previous binding existed, output validation error |
| // Otherwise, add reference from objectInfo to memoryInfo |
| // Add reference off of objInfo |
| // TODO: We may need to refactor or pass in multiple valid usage statements to handle multiple valid usage conditions. |
| bool CoreChecks::ValidateSetMemBinding(VkDeviceMemory mem, const BINDABLE &mem_binding, const char *apiName) const { |
| bool skip = false; |
| // It's an error to bind an object to NULL memory |
| if (mem != VK_NULL_HANDLE) { |
| auto typed_handle = mem_binding.Handle(); |
| if (mem_binding.sparse) { |
| const char *error_code = nullptr; |
| const char *handle_type = nullptr; |
| if (typed_handle.type == kVulkanObjectTypeBuffer) { |
| handle_type = "BUFFER"; |
| if (strcmp(apiName, "vkBindBufferMemory()") == 0) { |
| error_code = "VUID-vkBindBufferMemory-buffer-01030"; |
| } else { |
| error_code = "VUID-VkBindBufferMemoryInfo-buffer-01030"; |
| } |
| } else if (typed_handle.type == kVulkanObjectTypeImage) { |
| handle_type = "IMAGE"; |
| if (strcmp(apiName, "vkBindImageMemory()") == 0) { |
| error_code = "VUID-vkBindImageMemory-image-01045"; |
| } else { |
| error_code = "VUID-VkBindImageMemoryInfo-image-01045"; |
| } |
| } else { |
| // Unsupported object type |
| assert(false); |
| } |
| |
| LogObjectList objlist(mem); |
| objlist.add(typed_handle); |
| skip |= LogError(objlist, error_code, |
| "In %s, attempting to bind %s to %s which was created with sparse memory flags " |
| "(VK_%s_CREATE_SPARSE_*_BIT).", |
| apiName, report_data->FormatHandle(mem).c_str(), report_data->FormatHandle(typed_handle).c_str(), |
| handle_type); |
| } |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem); |
| if (mem_info) { |
| const auto *prev_binding = mem_binding.MemState(); |
| if (prev_binding) { |
| if (!prev_binding->Destroyed()) { |
| const char *error_code = nullptr; |
| if (typed_handle.type == kVulkanObjectTypeBuffer) { |
| if (strcmp(apiName, "vkBindBufferMemory()") == 0) { |
| error_code = "VUID-vkBindBufferMemory-buffer-01029"; |
| } else { |
| error_code = "VUID-VkBindBufferMemoryInfo-buffer-01029"; |
| } |
| } else if (typed_handle.type == kVulkanObjectTypeImage) { |
| if (strcmp(apiName, "vkBindImageMemory()") == 0) { |
| error_code = "VUID-vkBindImageMemory-image-01044"; |
| } else { |
| error_code = "VUID-VkBindImageMemoryInfo-image-01044"; |
| } |
| } else { |
| // Unsupported object type |
| assert(false); |
| } |
| |
| LogObjectList objlist(mem); |
| objlist.add(typed_handle); |
| objlist.add(prev_binding->mem()); |
| skip |= |
| LogError(objlist, error_code, "In %s, attempting to bind %s to %s which has already been bound to %s.", |
| apiName, report_data->FormatHandle(mem).c_str(), report_data->FormatHandle(typed_handle).c_str(), |
| report_data->FormatHandle(prev_binding->mem()).c_str()); |
| } else { |
| LogObjectList objlist(mem); |
| objlist.add(typed_handle); |
| skip |= |
| LogError(objlist, kVUID_Core_MemTrack_RebindObject, |
| "In %s, attempting to bind %s to %s which was previous bound to memory that has " |
| "since been freed. Memory bindings are immutable in " |
| "Vulkan so this attempt to bind to new memory is not allowed.", |
| apiName, report_data->FormatHandle(mem).c_str(), report_data->FormatHandle(typed_handle).c_str()); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateDeviceQueueFamily(uint32_t queue_family, const char *cmd_name, const char *parameter_name, |
| const char *error_code, bool optional = false) const { |
| bool skip = false; |
| if (!optional && queue_family == VK_QUEUE_FAMILY_IGNORED) { |
| skip |= LogError(device, error_code, |
| "%s: %s is VK_QUEUE_FAMILY_IGNORED, but it is required to provide a valid queue family index value.", |
| cmd_name, parameter_name); |
| } else if (queue_family_index_set.find(queue_family) == queue_family_index_set.end()) { |
| skip |= |
| LogError(device, error_code, |
| "%s: %s (= %" PRIu32 |
| ") is not one of the queue families given via VkDeviceQueueCreateInfo structures when the device was created.", |
| cmd_name, parameter_name, queue_family); |
| } |
| |
| return skip; |
| } |
| |
| // Validate the specified queue families against the families supported by the physical device that owns this device |
| bool CoreChecks::ValidatePhysicalDeviceQueueFamilies(uint32_t queue_family_count, const uint32_t *queue_families, |
| const char *cmd_name, const char *array_parameter_name, |
| const char *vuid) const { |
| bool skip = false; |
| if (queue_families) { |
| layer_data::unordered_set<uint32_t> set; |
| for (uint32_t i = 0; i < queue_family_count; ++i) { |
| std::string parameter_name = std::string(array_parameter_name) + "[" + std::to_string(i) + "]"; |
| |
| if (set.count(queue_families[i])) { |
| skip |= LogError(device, vuid, "%s: %s (=%" PRIu32 ") is not unique within %s array.", cmd_name, |
| parameter_name.c_str(), queue_families[i], array_parameter_name); |
| } else { |
| set.insert(queue_families[i]); |
| if (queue_families[i] == VK_QUEUE_FAMILY_IGNORED) { |
| skip |= LogError( |
| device, vuid, |
| "%s: %s is VK_QUEUE_FAMILY_IGNORED, but it is required to provide a valid queue family index value.", |
| cmd_name, parameter_name.c_str()); |
| } else if (queue_families[i] >= physical_device_state->queue_family_known_count) { |
| LogObjectList obj_list(physical_device); |
| obj_list.add(device); |
| skip |= |
| LogError(obj_list, vuid, |
| "%s: %s (= %" PRIu32 |
| ") is not one of the queue families supported by the parent PhysicalDevice %s of this device %s.", |
| cmd_name, parameter_name.c_str(), queue_families[i], |
| report_data->FormatHandle(physical_device).c_str(), report_data->FormatHandle(device).c_str()); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| // Check object status for selected flag state |
| bool CoreChecks::ValidateStatus(const CMD_BUFFER_STATE *pNode, CBStatusFlags status_mask, const char *fail_msg, |
| const char *msg_code) const { |
| if (!(pNode->status & status_mask)) { |
| return LogError(pNode->commandBuffer(), msg_code, "%s: %s.", report_data->FormatHandle(pNode->commandBuffer()).c_str(), |
| fail_msg); |
| } |
| return false; |
| } |
| |
| // Return true if for a given PSO, the given state enum is dynamic, else return false |
| bool CoreChecks::IsDynamic(const PIPELINE_STATE *pPipeline, const VkDynamicState state) const { |
| const auto *dynamic_state = pPipeline->DynamicState(); |
| if (pPipeline && (pPipeline->GetPipelineType() == VK_PIPELINE_BIND_POINT_GRAPHICS) && dynamic_state) { |
| for (uint32_t i = 0; i < dynamic_state->dynamicStateCount; i++) { |
| if (state == dynamic_state->pDynamicStates[i]) return true; |
| } |
| } |
| return false; |
| } |
| |
| // Validate state stored as flags at time of draw call |
| bool CoreChecks::ValidateDrawStateFlags(const CMD_BUFFER_STATE *pCB, const PIPELINE_STATE *pPipe, bool indexed, |
| const char *msg_code) const { |
| bool result = false; |
| if (pPipe->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || |
| pPipe->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) { |
| result |= |
| ValidateStatus(pCB, CBSTATUS_LINE_WIDTH_SET, "Dynamic line width state not set for this command buffer", msg_code); |
| } |
| const auto rp_state = pPipe->RasterizationState(); |
| if (rp_state && (rp_state->depthBiasEnable == VK_TRUE)) { |
| result |= |
| ValidateStatus(pCB, CBSTATUS_DEPTH_BIAS_SET, "Dynamic depth bias state not set for this command buffer", msg_code); |
| } |
| if (pPipe->BlendConstantsEnabled()) { |
| result |= ValidateStatus(pCB, CBSTATUS_BLEND_CONSTANTS_SET, "Dynamic blend constants state not set for this command buffer", |
| msg_code); |
| } |
| |
| const auto ds_state = pPipe->DepthStencilState(); |
| if (ds_state && (ds_state->depthBoundsTestEnable == VK_TRUE)) { |
| result |= |
| ValidateStatus(pCB, CBSTATUS_DEPTH_BOUNDS_SET, "Dynamic depth bounds state not set for this command buffer", msg_code); |
| } |
| if (ds_state && (ds_state->stencilTestEnable == VK_TRUE)) { |
| result |= ValidateStatus(pCB, CBSTATUS_STENCIL_READ_MASK_SET, |
| "Dynamic stencil read mask state not set for this command buffer", msg_code); |
| result |= ValidateStatus(pCB, CBSTATUS_STENCIL_WRITE_MASK_SET, |
| "Dynamic stencil write mask state not set for this command buffer", msg_code); |
| result |= ValidateStatus(pCB, CBSTATUS_STENCIL_REFERENCE_SET, |
| "Dynamic stencil reference state not set for this command buffer", msg_code); |
| } |
| if (indexed) { |
| result |= ValidateStatus(pCB, CBSTATUS_INDEX_BUFFER_BOUND, |
| "Index buffer object not bound to this command buffer when Indexed Draw attempted", msg_code); |
| } |
| if (pPipe->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || |
| pPipe->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) { |
| const auto *line_state = LvlFindInChain<VkPipelineRasterizationLineStateCreateInfoEXT>(rp_state); |
| if (line_state && line_state->stippledLineEnable) { |
| result |= ValidateStatus(pCB, CBSTATUS_LINE_STIPPLE_SET, "Dynamic line stipple state not set for this command buffer", |
| msg_code); |
| } |
| } |
| |
| return result; |
| } |
| |
| bool CoreChecks::LogInvalidAttachmentMessage(const char *type1_string, const RENDER_PASS_STATE *rp1_state, const char *type2_string, |
| const RENDER_PASS_STATE *rp2_state, uint32_t primary_attach, uint32_t secondary_attach, |
| const char *msg, const char *caller, const char *error_code) const { |
| LogObjectList objlist(rp1_state->renderPass()); |
| objlist.add(rp2_state->renderPass()); |
| return LogError(objlist, error_code, |
| "%s: RenderPasses incompatible between %s w/ %s and %s w/ %s Attachment %u is not " |
| "compatible with %u: %s.", |
| caller, type1_string, report_data->FormatHandle(rp1_state->renderPass()).c_str(), type2_string, |
| report_data->FormatHandle(rp2_state->renderPass()).c_str(), primary_attach, secondary_attach, msg); |
| } |
| |
| bool CoreChecks::ValidateAttachmentCompatibility(const char *type1_string, const RENDER_PASS_STATE *rp1_state, |
| const char *type2_string, const RENDER_PASS_STATE *rp2_state, |
| uint32_t primary_attach, uint32_t secondary_attach, const char *caller, |
| const char *error_code) const { |
| bool skip = false; |
| const auto &primary_pass_ci = rp1_state->createInfo; |
| const auto &secondary_pass_ci = rp2_state->createInfo; |
| if (primary_pass_ci.attachmentCount <= primary_attach) { |
| primary_attach = VK_ATTACHMENT_UNUSED; |
| } |
| if (secondary_pass_ci.attachmentCount <= secondary_attach) { |
| secondary_attach = VK_ATTACHMENT_UNUSED; |
| } |
| if (primary_attach == VK_ATTACHMENT_UNUSED && secondary_attach == VK_ATTACHMENT_UNUSED) { |
| return skip; |
| } |
| if (primary_attach == VK_ATTACHMENT_UNUSED) { |
| skip |= LogInvalidAttachmentMessage(type1_string, rp1_state, type2_string, rp2_state, primary_attach, secondary_attach, |
| "The first is unused while the second is not.", caller, error_code); |
| return skip; |
| } |
| if (secondary_attach == VK_ATTACHMENT_UNUSED) { |
| skip |= LogInvalidAttachmentMessage(type1_string, rp1_state, type2_string, rp2_state, primary_attach, secondary_attach, |
| "The second is unused while the first is not.", caller, error_code); |
| return skip; |
| } |
| if (primary_pass_ci.pAttachments[primary_attach].format != secondary_pass_ci.pAttachments[secondary_attach].format) { |
| skip |= LogInvalidAttachmentMessage(type1_string, rp1_state, type2_string, rp2_state, primary_attach, secondary_attach, |
| "They have different formats.", caller, error_code); |
| } |
| if (primary_pass_ci.pAttachments[primary_attach].samples != secondary_pass_ci.pAttachments[secondary_attach].samples) { |
| skip |= LogInvalidAttachmentMessage(type1_string, rp1_state, type2_string, rp2_state, primary_attach, secondary_attach, |
| "They have different samples.", caller, error_code); |
| } |
| if (primary_pass_ci.pAttachments[primary_attach].flags != secondary_pass_ci.pAttachments[secondary_attach].flags) { |
| skip |= LogInvalidAttachmentMessage(type1_string, rp1_state, type2_string, rp2_state, primary_attach, secondary_attach, |
| "They have different flags.", caller, error_code); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateSubpassCompatibility(const char *type1_string, const RENDER_PASS_STATE *rp1_state, |
| const char *type2_string, const RENDER_PASS_STATE *rp2_state, const int subpass, |
| const char *caller, const char *error_code) const { |
| bool skip = false; |
| const auto &primary_desc = rp1_state->createInfo.pSubpasses[subpass]; |
| const auto &secondary_desc = rp2_state->createInfo.pSubpasses[subpass]; |
| uint32_t max_input_attachment_count = std::max(primary_desc.inputAttachmentCount, secondary_desc.inputAttachmentCount); |
| for (uint32_t i = 0; i < max_input_attachment_count; ++i) { |
| uint32_t primary_input_attach = VK_ATTACHMENT_UNUSED, secondary_input_attach = VK_ATTACHMENT_UNUSED; |
| if (i < primary_desc.inputAttachmentCount) { |
| primary_input_attach = primary_desc.pInputAttachments[i].attachment; |
| } |
| if (i < secondary_desc.inputAttachmentCount) { |
| secondary_input_attach = secondary_desc.pInputAttachments[i].attachment; |
| } |
| skip |= ValidateAttachmentCompatibility(type1_string, rp1_state, type2_string, rp2_state, primary_input_attach, |
| secondary_input_attach, caller, error_code); |
| } |
| uint32_t max_color_attachment_count = std::max(primary_desc.colorAttachmentCount, secondary_desc.colorAttachmentCount); |
| for (uint32_t i = 0; i < max_color_attachment_count; ++i) { |
| uint32_t primary_color_attach = VK_ATTACHMENT_UNUSED, secondary_color_attach = VK_ATTACHMENT_UNUSED; |
| if (i < primary_desc.colorAttachmentCount) { |
| primary_color_attach = primary_desc.pColorAttachments[i].attachment; |
| } |
| if (i < secondary_desc.colorAttachmentCount) { |
| secondary_color_attach = secondary_desc.pColorAttachments[i].attachment; |
| } |
| skip |= ValidateAttachmentCompatibility(type1_string, rp1_state, type2_string, rp2_state, primary_color_attach, |
| secondary_color_attach, caller, error_code); |
| if (rp1_state->createInfo.subpassCount > 1) { |
| uint32_t primary_resolve_attach = VK_ATTACHMENT_UNUSED, secondary_resolve_attach = VK_ATTACHMENT_UNUSED; |
| if (i < primary_desc.colorAttachmentCount && primary_desc.pResolveAttachments) { |
| primary_resolve_attach = primary_desc.pResolveAttachments[i].attachment; |
| } |
| if (i < secondary_desc.colorAttachmentCount && secondary_desc.pResolveAttachments) { |
| secondary_resolve_attach = secondary_desc.pResolveAttachments[i].attachment; |
| } |
| skip |= ValidateAttachmentCompatibility(type1_string, rp1_state, type2_string, rp2_state, primary_resolve_attach, |
| secondary_resolve_attach, caller, error_code); |
| } |
| } |
| uint32_t primary_depthstencil_attach = VK_ATTACHMENT_UNUSED, secondary_depthstencil_attach = VK_ATTACHMENT_UNUSED; |
| if (primary_desc.pDepthStencilAttachment) { |
| primary_depthstencil_attach = primary_desc.pDepthStencilAttachment[0].attachment; |
| } |
| if (secondary_desc.pDepthStencilAttachment) { |
| secondary_depthstencil_attach = secondary_desc.pDepthStencilAttachment[0].attachment; |
| } |
| skip |= ValidateAttachmentCompatibility(type1_string, rp1_state, type2_string, rp2_state, primary_depthstencil_attach, |
| secondary_depthstencil_attach, caller, error_code); |
| |
| // Both renderpasses must agree on Multiview usage |
| if (primary_desc.viewMask && secondary_desc.viewMask) { |
| if (primary_desc.viewMask != secondary_desc.viewMask) { |
| std::stringstream ss; |
| ss << "For subpass " << subpass << ", they have a different viewMask. The first has view mask " << primary_desc.viewMask |
| << " while the second has view mask " << secondary_desc.viewMask << "."; |
| skip |= LogInvalidPnextMessage(type1_string, rp1_state, type2_string, rp2_state, ss.str().c_str(), caller, error_code); |
| } |
| } else if (primary_desc.viewMask) { |
| skip |= LogInvalidPnextMessage(type1_string, rp1_state, type2_string, rp2_state, |
| "The first uses Multiview (has non-zero viewMasks) while the second one does not.", caller, |
| error_code); |
| } else if (secondary_desc.viewMask) { |
| skip |= LogInvalidPnextMessage(type1_string, rp1_state, type2_string, rp2_state, |
| "The second uses Multiview (has non-zero viewMasks) while the first one does not.", caller, |
| error_code); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::LogInvalidPnextMessage(const char *type1_string, const RENDER_PASS_STATE *rp1_state, const char *type2_string, |
| const RENDER_PASS_STATE *rp2_state, const char *msg, const char *caller, |
| const char *error_code) const { |
| LogObjectList objlist(rp1_state->renderPass()); |
| objlist.add(rp2_state->renderPass()); |
| return LogError(objlist, error_code, "%s: RenderPasses incompatible between %s w/ %s and %s w/ %s: %s", caller, type1_string, |
| report_data->FormatHandle(rp1_state->renderPass()).c_str(), type2_string, |
| report_data->FormatHandle(rp2_state->renderPass()).c_str(), msg); |
| } |
| |
| // Verify that given renderPass CreateInfo for primary and secondary command buffers are compatible. |
| // This function deals directly with the CreateInfo, there are overloaded versions below that can take the renderPass handle and |
| // will then feed into this function |
| bool CoreChecks::ValidateRenderPassCompatibility(const char *type1_string, const RENDER_PASS_STATE *rp1_state, |
| const char *type2_string, const RENDER_PASS_STATE *rp2_state, const char *caller, |
| const char *error_code) const { |
| bool skip = false; |
| |
| // createInfo flags must be identical for the renderpasses to be compatible. |
| if (rp1_state->createInfo.flags != rp2_state->createInfo.flags) { |
| LogObjectList objlist(rp1_state->renderPass()); |
| objlist.add(rp2_state->renderPass()); |
| skip |= |
| LogError(objlist, error_code, |
| "%s: RenderPasses incompatible between %s w/ %s with flags of %u and %s w/ " |
| "%s with a flags of %u.", |
| caller, type1_string, report_data->FormatHandle(rp1_state->renderPass()).c_str(), rp1_state->createInfo.flags, |
| type2_string, report_data->FormatHandle(rp2_state->renderPass()).c_str(), rp2_state->createInfo.flags); |
| } |
| |
| if (rp1_state->createInfo.subpassCount != rp2_state->createInfo.subpassCount) { |
| LogObjectList objlist(rp1_state->renderPass()); |
| objlist.add(rp2_state->renderPass()); |
| skip |= LogError(objlist, error_code, |
| "%s: RenderPasses incompatible between %s w/ %s with a subpassCount of %u and %s w/ " |
| "%s with a subpassCount of %u.", |
| caller, type1_string, report_data->FormatHandle(rp1_state->renderPass()).c_str(), |
| rp1_state->createInfo.subpassCount, type2_string, report_data->FormatHandle(rp2_state->renderPass()).c_str(), |
| rp2_state->createInfo.subpassCount); |
| } else { |
| for (uint32_t i = 0; i < rp1_state->createInfo.subpassCount; ++i) { |
| skip |= ValidateSubpassCompatibility(type1_string, rp1_state, type2_string, rp2_state, i, caller, error_code); |
| } |
| } |
| |
| // Find an entry of the Fragment Density Map type in the pNext chain, if it exists |
| const auto fdm1 = LvlFindInChain<VkRenderPassFragmentDensityMapCreateInfoEXT>(rp1_state->createInfo.pNext); |
| const auto fdm2 = LvlFindInChain<VkRenderPassFragmentDensityMapCreateInfoEXT>(rp2_state->createInfo.pNext); |
| |
| // Both renderpasses must agree on usage of a Fragment Density Map type |
| if (fdm1 && fdm2) { |
| uint32_t primary_input_attach = fdm1->fragmentDensityMapAttachment.attachment; |
| uint32_t secondary_input_attach = fdm2->fragmentDensityMapAttachment.attachment; |
| skip |= ValidateAttachmentCompatibility(type1_string, rp1_state, type2_string, rp2_state, primary_input_attach, |
| secondary_input_attach, caller, error_code); |
| } else if (fdm1) { |
| skip |= LogInvalidPnextMessage(type1_string, rp1_state, type2_string, rp2_state, |
| "The first uses a Fragment Density Map while the second one does not.", caller, error_code); |
| } else if (fdm2) { |
| skip |= LogInvalidPnextMessage(type1_string, rp1_state, type2_string, rp2_state, |
| "The second uses a Fragment Density Map while the first one does not.", caller, error_code); |
| } |
| |
| return skip; |
| } |
| |
| // For given pipeline, return number of MSAA samples, or one if MSAA disabled |
| static VkSampleCountFlagBits GetNumSamples(PIPELINE_STATE const *pipe) { |
| const auto ms_state = pipe->MultisampleState(); |
| if (ms_state) { |
| return ms_state->rasterizationSamples; |
| } |
| return VK_SAMPLE_COUNT_1_BIT; |
| } |
| |
| static void ListBits(std::ostream &s, uint32_t bits) { |
| for (int i = 0; i < 32 && bits; i++) { |
| if (bits & (1 << i)) { |
| s << i; |
| bits &= ~(1 << i); |
| if (bits) { |
| s << ","; |
| } |
| } |
| } |
| } |
| |
| std::string DynamicStateString(CBStatusFlags input_value) { |
| std::string ret; |
| int index = 0; |
| while (input_value) { |
| if (input_value & 1) { |
| if (!ret.empty()) ret.append("|"); |
| ret.append(string_VkDynamicState(ConvertToDynamicState(static_cast<CBStatusFlagBits>(1llu << index)))); |
| } |
| ++index; |
| input_value >>= 1; |
| } |
| if (ret.empty()) ret.append(string_VkDynamicState(ConvertToDynamicState(static_cast<CBStatusFlagBits>(0)))); |
| return ret; |
| } |
| |
| // Validate draw-time state related to the PSO |
| bool CoreChecks::ValidatePipelineDrawtimeState(const LAST_BOUND_STATE &state, const CMD_BUFFER_STATE *pCB, CMD_TYPE cmd_type, |
| const PIPELINE_STATE *pPipeline) const { |
| bool skip = false; |
| const auto ¤t_vtx_bfr_binding_info = pCB->current_vertex_buffer_binding_info.vertex_buffer_bindings; |
| const DrawDispatchVuid vuid = GetDrawDispatchVuid(cmd_type); |
| const char *caller = CommandTypeString(cmd_type); |
| |
| if (pCB->activeRenderPass && pCB->activeRenderPass->use_dynamic_rendering) { |
| const auto &rp_state = pPipeline->RenderPassState(); |
| if (rp_state) { |
| if (rp_state->renderPass() != VK_NULL_HANDLE) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_06198, |
| "%s: Currently bound pipeline %s must have been created with a " |
| "VkGraphicsPipelineCreateInfo::renderPass equal to VK_NULL_HANDLE", |
| caller, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str()); |
| } |
| |
| if (rp_state->dynamic_rendering_pipeline_create_info.viewMask != |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.viewMask) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_view_mask, |
| "%s: Currently bound pipeline %s viewMask ([%" PRIu32 |
| ") must be equal to pBeginRendering->viewMask ([%" PRIu32 ")", |
| caller, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str(), |
| rp_state->dynamic_rendering_pipeline_create_info.viewMask, |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.viewMask); |
| } |
| |
| const auto color_attachment_count = rp_state->dynamic_rendering_pipeline_create_info.colorAttachmentCount; |
| if (color_attachment_count && |
| (color_attachment_count != pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_color_count, |
| "%s: Currently bound pipeline %s colorAttachmentCount ([%" PRIu32 |
| ") must be equal to pBeginRendering->colorAttachmentCount ([%" PRIu32 ")", |
| caller, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str(), |
| rp_state->dynamic_rendering_pipeline_create_info.colorAttachmentCount, |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount); |
| } |
| |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount > 0) { |
| for (uint32_t i = 0; i < pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount; ++i) { |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView != |
| VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView); |
| if ((rp_state->dynamic_rendering_pipeline_create_info.colorAttachmentCount > i) && |
| view_state->create_info.format != |
| rp_state->dynamic_rendering_pipeline_create_info.pColorAttachmentFormats[i]) { |
| skip |= LogError( |
| pCB->commandBuffer(), vuid.dynamic_rendering_color_formats, |
| "%s: Color attachment ([%" PRIu32 |
| ") imageView format (%s) must match corresponding format in pipeline (%s)", |
| caller, i, string_VkFormat(view_state->create_info.format), |
| string_VkFormat(rp_state->dynamic_rendering_pipeline_create_info.pColorAttachmentFormats[i])); |
| } |
| } |
| } |
| } |
| |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment && |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView); |
| if (view_state->create_info.format != rp_state->dynamic_rendering_pipeline_create_info.depthAttachmentFormat) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_depth_format, |
| "%s: Depth attachment imageView format (%s) must match corresponding format in pipeline (%s)", |
| caller, string_VkFormat(view_state->create_info.format), |
| string_VkFormat(rp_state->dynamic_rendering_pipeline_create_info.depthAttachmentFormat)); |
| } |
| } |
| |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment && |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView); |
| if (view_state->create_info.format != rp_state->dynamic_rendering_pipeline_create_info.stencilAttachmentFormat) { |
| skip |= |
| LogError(pCB->commandBuffer(), vuid.dynamic_rendering_stencil_format, |
| "%s: Stencil attachment imageView format (%s) must match corresponding format in pipeline (%s)", |
| caller, string_VkFormat(view_state->create_info.format), |
| string_VkFormat(rp_state->dynamic_rendering_pipeline_create_info.stencilAttachmentFormat)); |
| } |
| } |
| |
| auto rendering_fragment_shading_rate_attachment_info = LvlFindInChain<VkRenderingFragmentShadingRateAttachmentInfoKHR>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pNext); |
| if (rendering_fragment_shading_rate_attachment_info && |
| (rendering_fragment_shading_rate_attachment_info->imageView != VK_NULL_HANDLE)) { |
| if (!(rp_state->createInfo.flags & |
| VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_fsr, |
| "%s: Currently bound graphics pipeline %s must have been created with " |
| "VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR", |
| caller, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str()); |
| } |
| } |
| |
| auto rendering_fragment_shading_rate_density_map = LvlFindInChain<VkRenderingFragmentDensityMapAttachmentInfoEXT>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pNext); |
| if (rendering_fragment_shading_rate_density_map && |
| (rendering_fragment_shading_rate_density_map->imageView != VK_NULL_HANDLE)) { |
| if (!(rp_state->createInfo.flags & |
| VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_fdm, |
| "%s: Currently bound graphics pipeline %s must have been created with " |
| "VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT", |
| caller, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str()); |
| } |
| } |
| } |
| |
| // VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV |
| const auto &graphics_create_info = pPipeline->GetUnifiedCreateInfo().graphics; |
| auto p_attachment_sample_count_info = LvlFindInChain<VkAttachmentSampleCountInfoAMD>(graphics_create_info.pNext); |
| |
| if (p_attachment_sample_count_info) { |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount > 0) { |
| for (uint32_t i = 0; i < pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount; ++i) { |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView != |
| VK_NULL_HANDLE) { |
| auto color_view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView); |
| auto color_image_samples = Get<IMAGE_STATE>(color_view_state->create_info.image)->createInfo.samples; |
| |
| if (p_attachment_sample_count_info) { |
| if (color_image_samples != p_attachment_sample_count_info->pColorAttachmentSamples[i]) { |
| skip |= LogError( |
| pCB->commandBuffer(), vuid.dynamic_rendering_color_sample, |
| "%s: Color attachment (%" PRIu32 |
| ") sample count (%s) must match corresponding VkAttachmentSampleCountInfoAMD " |
| "sample count (%s)", |
| caller, i, string_VkSampleCountFlagBits(color_image_samples), |
| string_VkSampleCountFlagBits(p_attachment_sample_count_info->pColorAttachmentSamples[i])); |
| } |
| } |
| } |
| } |
| } |
| |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment != nullptr) { |
| auto depth_view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView); |
| auto depth_image_samples = Get<IMAGE_STATE>(depth_view_state->create_info.image)->createInfo.samples; |
| |
| if (p_attachment_sample_count_info) { |
| if (depth_image_samples != p_attachment_sample_count_info->depthStencilAttachmentSamples) { |
| skip |= LogError( |
| pCB->commandBuffer(), vuid.dynamic_rendering_depth_sample, |
| "%s: Depth attachment sample count (%s) must match corresponding VkAttachmentSampleCountInfoAMD sample " |
| "count (%s)", |
| caller, string_VkSampleCountFlagBits(depth_image_samples), |
| string_VkSampleCountFlagBits(p_attachment_sample_count_info->depthStencilAttachmentSamples)); |
| } |
| } |
| } |
| |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment != nullptr) { |
| auto stencil_view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView); |
| auto stencil_image_samples = Get<IMAGE_STATE>(stencil_view_state->create_info.image)->createInfo.samples; |
| |
| if (p_attachment_sample_count_info) { |
| if (stencil_image_samples != p_attachment_sample_count_info->depthStencilAttachmentSamples) { |
| skip |= LogError( |
| pCB->commandBuffer(), vuid.dynamic_rendering_stencil_sample, |
| "%s: Stencil attachment sample count (%s) must match corresponding VkAttachmentSampleCountInfoAMD " |
| "sample count (%s)", |
| caller, string_VkSampleCountFlagBits(stencil_image_samples), |
| string_VkSampleCountFlagBits(p_attachment_sample_count_info->depthStencilAttachmentSamples)); |
| } |
| } |
| } |
| } else { |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount > 0) { |
| for (uint32_t i = 0; i < pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount; ++i) { |
| if (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView != |
| VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView); |
| auto samples = Get<IMAGE_STATE>(view_state->create_info.image)->createInfo.samples; |
| |
| if (samples != GetNumSamples(pPipeline)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_multi_sample, |
| "%s: Color attachment (%" PRIu32 |
| ") sample count (%s) must match corresponding VkPipelineMultisampleStateCreateInfo " |
| "sample count (%s)", |
| caller, i, string_VkSampleCountFlagBits(samples), |
| string_VkSampleCountFlagBits(GetNumSamples(pPipeline))); |
| } |
| } |
| } |
| } |
| |
| if ((pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment != nullptr) && |
| (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE)) { |
| const auto &depth_view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView); |
| const auto &depth_image_samples = Get<IMAGE_STATE>(depth_view_state->create_info.image)->createInfo.samples; |
| if (depth_image_samples != GetNumSamples(pPipeline)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_06189, |
| "%s: Depth attachment sample count (%s) must match corresponding " |
| "VkPipelineMultisampleStateCreateInfo::rasterizationSamples count (%s)", |
| caller, string_VkSampleCountFlagBits(depth_image_samples), |
| string_VkSampleCountFlagBits(GetNumSamples(pPipeline))); |
| } |
| } |
| |
| if ((pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment != nullptr) && |
| (pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE)) { |
| const auto &stencil_view_state = Get<IMAGE_VIEW_STATE>( |
| pCB->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView); |
| const auto &stencil_image_samples = Get<IMAGE_STATE>(stencil_view_state->create_info.image)->createInfo.samples; |
| if (stencil_image_samples != GetNumSamples(pPipeline)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.dynamic_rendering_06190, |
| "%s: Stencil attachment sample count (%s) must match corresponding " |
| "VkPipelineMultisampleStateCreateInfo::rasterizationSamples count (%s)", |
| caller, string_VkSampleCountFlagBits(stencil_image_samples), |
| string_VkSampleCountFlagBits(GetNumSamples(pPipeline))); |
| } |
| } |
| } |
| } |
| |
| if (enabled_features.primitives_generated_query_features.primitivesGeneratedQueryWithRasterizerDiscard == VK_FALSE) { |
| bool primitives_generated_query = false; |
| for (const auto &query : pCB->activeQueries) { |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query.pool); |
| if (query_pool_state && query_pool_state->createInfo.queryType == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT) { |
| primitives_generated_query = true; |
| break; |
| } |
| } |
| const auto rp_state = pPipeline->RasterizationState(); |
| if (primitives_generated_query && rp_state && rp_state->rasterizerDiscardEnable == VK_TRUE) { |
| skip |= LogError(pCB->commandBuffer(), vuid.primitives_generated, |
| "%s: a VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT query is active and pipeline was created with " |
| "VkPipelineRasterizationStateCreateInfo::rasterizerDiscardEnable set to VK_TRUE, but " |
| "primitivesGeneratedQueryWithRasterizerDiscard feature is not enabled.", |
| caller); |
| } |
| } |
| |
| // Verify vertex & index buffer for unprotected command buffer. |
| // Because vertex & index buffer is read only, it doesn't need to care protected command buffer case. |
| if (enabled_features.core11.protectedMemory == VK_TRUE) { |
| for (const auto &buffer_binding : current_vtx_bfr_binding_info) { |
| if (buffer_binding.buffer_state && !buffer_binding.buffer_state->Destroyed()) { |
| skip |= ValidateProtectedBuffer(pCB, buffer_binding.buffer_state.get(), caller, vuid.unprotected_command_buffer, |
| "Buffer is vertex buffer"); |
| } |
| } |
| if (pCB->index_buffer_binding.buffer_state && !pCB->index_buffer_binding.buffer_state->Destroyed()) { |
| skip |= ValidateProtectedBuffer(pCB, pCB->index_buffer_binding.buffer_state.get(), caller, |
| vuid.unprotected_command_buffer, "Buffer is index buffer"); |
| } |
| } |
| |
| // Verify if using dynamic state setting commands that it doesn't set up in pipeline |
| CBStatusFlags invalid_status = CBSTATUS_ALL_STATE_SET & ~(pCB->dynamic_status | pCB->static_status); |
| if (invalid_status) { |
| std::string dynamic_states = DynamicStateString(invalid_status); |
| LogObjectList objlist(pCB->commandBuffer()); |
| objlist.add(pPipeline->pipeline()); |
| skip |= LogError(objlist, vuid.dynamic_state_setting_commands, |
| "%s: %s doesn't set up %s, but it calls the related dynamic state setting commands", caller, |
| report_data->FormatHandle(state.pipeline_state->pipeline()).c_str(), dynamic_states.c_str()); |
| } |
| |
| // Verify vertex binding |
| if (pPipeline->vertex_input_state && pPipeline->vertex_input_state->binding_descriptions.size() > 0) { |
| for (size_t i = 0; i < pPipeline->vertex_input_state->binding_descriptions.size(); i++) { |
| const auto vertex_binding = pPipeline->vertex_input_state->binding_descriptions[i].binding; |
| if (current_vtx_bfr_binding_info.size() < (vertex_binding + 1)) { |
| skip |= LogError(pCB->commandBuffer(), vuid.vertex_binding, |
| "%s: %s expects that this Command Buffer's vertex binding Index %u should be set via " |
| "vkCmdBindVertexBuffers. This is because pVertexBindingDescriptions[" PRINTF_SIZE_T_SPECIFIER |
| "].binding value is %u.", |
| caller, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str(), vertex_binding, i, |
| vertex_binding); |
| } else if ((current_vtx_bfr_binding_info[vertex_binding].buffer_state == nullptr) && |
| !enabled_features.robustness2_features.nullDescriptor) { |
| skip |= LogError(pCB->commandBuffer(), vuid.vertex_binding_null, |
| "%s: Vertex binding %d must not be VK_NULL_HANDLE %s expects that this Command Buffer's vertex " |
| "binding Index %u should be set via " |
| "vkCmdBindVertexBuffers. This is because pVertexBindingDescriptions[" PRINTF_SIZE_T_SPECIFIER |
| "].binding value is %u.", |
| caller, vertex_binding, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str(), |
| vertex_binding, i, vertex_binding); |
| } |
| } |
| |
| // Verify vertex attribute address alignment |
| for (size_t i = 0; i < pPipeline->vertex_input_state->vertex_attribute_descriptions.size(); i++) { |
| const auto &attribute_description = pPipeline->vertex_input_state->vertex_attribute_descriptions[i]; |
| const auto vertex_binding = attribute_description.binding; |
| const auto attribute_offset = attribute_description.offset; |
| |
| const auto &vertex_binding_map_it = pPipeline->vertex_input_state->binding_to_index_map.find(vertex_binding); |
| if ((vertex_binding_map_it != pPipeline->vertex_input_state->binding_to_index_map.cend()) && |
| (vertex_binding < current_vtx_bfr_binding_info.size()) && |
| ((current_vtx_bfr_binding_info[vertex_binding].buffer_state) || |
| enabled_features.robustness2_features.nullDescriptor)) { |
| auto vertex_buffer_stride = |
| pPipeline->vertex_input_state->binding_descriptions[vertex_binding_map_it->second].stride; |
| if (IsDynamic(pPipeline, VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT)) { |
| vertex_buffer_stride = static_cast<uint32_t>(current_vtx_bfr_binding_info[vertex_binding].stride); |
| uint32_t attribute_binding_extent = |
| attribute_description.offset + FormatElementSize(attribute_description.format); |
| if (vertex_buffer_stride != 0 && vertex_buffer_stride < attribute_binding_extent) { |
| skip |= LogError(pCB->commandBuffer(), "VUID-vkCmdBindVertexBuffers2-pStrides-06209", |
| "The pStrides[%u] (%u) parameter in the last call to %s is not 0 " |
| "and less than the extent of the binding for attribute %zu (%u).", |
| vertex_binding, vertex_buffer_stride, CommandTypeString(cmd_type), i, |
| attribute_binding_extent); |
| } |
| } |
| const VkDeviceSize vertex_buffer_offset = current_vtx_bfr_binding_info[vertex_binding].offset; |
| |
| // Use 1 as vertex/instance index to use buffer stride as well |
| const VkDeviceSize attrib_address = vertex_buffer_offset + vertex_buffer_stride + attribute_offset; |
| |
| const VkDeviceSize vtx_attrib_req_alignment = pPipeline->vertex_input_state->vertex_attribute_alignments[i]; |
| |
| if (SafeModulo(attrib_address, vtx_attrib_req_alignment) != 0) { |
| LogObjectList objlist(current_vtx_bfr_binding_info[vertex_binding].buffer_state->buffer()); |
| objlist.add(state.pipeline_state->pipeline()); |
| skip |= LogError(objlist, vuid.vertex_binding_attribute, |
| "%s: Format %s has an alignment of %" PRIu64 " but the alignment of attribAddress (%" PRIu64 |
| ") is not aligned in pVertexAttributeDescriptions[" PRINTF_SIZE_T_SPECIFIER |
| "] (binding=%u location=%u) where attribAddress = vertex buffer offset (%" PRIu64 |
| ") + binding stride (%u) + attribute offset (%u).", |
| caller, string_VkFormat(attribute_description.format), vtx_attrib_req_alignment, |
| attrib_address, i, vertex_binding, attribute_description.location, vertex_buffer_offset, |
| vertex_buffer_stride, attribute_offset); |
| } |
| } else { |
| LogObjectList objlist(pCB->commandBuffer()); |
| objlist.add(state.pipeline_state->pipeline()); |
| skip |= LogError(objlist, vuid.vertex_binding_attribute, |
| "%s: binding #%" PRIu32 " in pVertexAttributeDescriptions[" PRINTF_SIZE_T_SPECIFIER |
| "] of %s is an invalid value for command buffer %s.", |
| caller, vertex_binding, i, report_data->FormatHandle(state.pipeline_state->pipeline()).c_str(), |
| report_data->FormatHandle(pCB->commandBuffer()).c_str()); |
| } |
| } |
| } |
| |
| // If Viewport or scissors are dynamic, verify that dynamic count matches PSO count. |
| // Skip check if rasterization is disabled, if there is no viewport, or if viewport/scissors are being inherited. |
| bool dyn_viewport = IsDynamic(pPipeline, VK_DYNAMIC_STATE_VIEWPORT); |
| const auto *raster_state = pPipeline->RasterizationState(); |
| const auto *viewport_state = pPipeline->ViewportState(); |
| if ((!raster_state || (raster_state->rasterizerDiscardEnable == VK_FALSE)) && viewport_state && |
| (pCB->inheritedViewportDepths.size() == 0)) { |
| bool dyn_scissor = IsDynamic(pPipeline, VK_DYNAMIC_STATE_SCISSOR); |
| |
| // NB (akeley98): Current validation layers do not detect the error where vkCmdSetViewport (or scissor) was called, but |
| // the dynamic state set is overwritten by binding a graphics pipeline with static viewport (scissor) state. |
| // This condition be detected by checking trashedViewportMask & viewportMask (trashedScissorMask & scissorMask) is |
| // nonzero in the range of bits needed by the pipeline. |
| if (dyn_viewport) { |
| const auto required_viewports_mask = (1 << viewport_state->viewportCount) - 1; |
| const auto missing_viewport_mask = ~pCB->viewportMask & required_viewports_mask; |
| if (missing_viewport_mask) { |
| std::stringstream ss; |
| ss << caller << ": Dynamic viewport(s) "; |
| ListBits(ss, missing_viewport_mask); |
| ss << " are used by pipeline state object, but were not provided via calls to vkCmdSetViewport()."; |
| skip |= LogError(device, vuid.dynamic_state, "%s", ss.str().c_str()); |
| } |
| } |
| |
| if (dyn_scissor) { |
| const auto required_scissor_mask = (1 << viewport_state->scissorCount) - 1; |
| const auto missing_scissor_mask = ~pCB->scissorMask & required_scissor_mask; |
| if (missing_scissor_mask) { |
| std::stringstream ss; |
| ss << caller << ": Dynamic scissor(s) "; |
| ListBits(ss, missing_scissor_mask); |
| ss << " are used by pipeline state object, but were not provided via calls to vkCmdSetScissor()."; |
| skip |= LogError(device, vuid.dynamic_state, "%s", ss.str().c_str()); |
| } |
| } |
| |
| bool dyn_viewport_count = IsDynamic(pPipeline, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT); |
| bool dyn_scissor_count = IsDynamic(pPipeline, VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT); |
| |
| // VUID {refpage}-viewportCount-03417 |
| if (dyn_viewport_count && !dyn_scissor_count) { |
| const auto required_viewport_mask = (1 << viewport_state->scissorCount) - 1; |
| const auto missing_viewport_mask = ~pCB->viewportWithCountMask & required_viewport_mask; |
| if (missing_viewport_mask) { |
| std::stringstream ss; |
| ss << caller << ": Dynamic viewport with count "; |
| ListBits(ss, missing_viewport_mask); |
| ss << " are used by pipeline state object, but were not provided via calls to vkCmdSetViewportWithCountEXT()."; |
| skip |= LogError(device, vuid.viewport_count, "%s", ss.str().c_str()); |
| } |
| } |
| |
| // VUID {refpage}-scissorCount-03418 |
| if (dyn_scissor_count && !dyn_viewport_count) { |
| const auto required_scissor_mask = (1 << viewport_state->viewportCount) - 1; |
| const auto missing_scissor_mask = ~pCB->scissorWithCountMask & required_scissor_mask; |
| if (missing_scissor_mask) { |
| std::stringstream ss; |
| ss << caller << ": Dynamic scissor with count "; |
| ListBits(ss, missing_scissor_mask); |
| ss << " are used by pipeline state object, but were not provided via calls to vkCmdSetScissorWithCountEXT()."; |
| skip |= LogError(device, vuid.scissor_count, "%s", ss.str().c_str()); |
| } |
| } |
| |
| // VUID {refpage}-viewportCount-03419 |
| if (dyn_scissor_count && dyn_viewport_count) { |
| if (pCB->viewportWithCountMask != pCB->scissorWithCountMask) { |
| std::stringstream ss; |
| ss << caller << ": Dynamic viewport and scissor with count "; |
| ListBits(ss, pCB->viewportWithCountMask ^ pCB->scissorWithCountMask); |
| ss << " are used by pipeline state object, but were not provided via matching calls to " |
| "vkCmdSetViewportWithCountEXT and vkCmdSetScissorWithCountEXT()."; |
| skip |= LogError(device, vuid.viewport_scissor_count, "%s", ss.str().c_str()); |
| } |
| } |
| } |
| |
| // If inheriting viewports, verify that not using more than inherited. |
| if (pCB->inheritedViewportDepths.size() != 0 && dyn_viewport) { |
| uint32_t viewport_count = viewport_state->viewportCount; |
| uint32_t max_inherited = uint32_t(pCB->inheritedViewportDepths.size()); |
| if (viewport_count > max_inherited) { |
| skip |= LogError(device, vuid.dynamic_state, |
| "Pipeline requires more viewports (%u) than inherited (viewportDepthCount=%u).", |
| unsigned(viewport_count), unsigned(max_inherited)); |
| } |
| } |
| |
| // Verify that any MSAA request in PSO matches sample# in bound FB |
| // Verify that blend is enabled only if supported by subpasses image views format features |
| // Skip the check if rasterization is disabled. |
| if (!raster_state || (raster_state->rasterizerDiscardEnable == VK_FALSE)) { |
| VkSampleCountFlagBits pso_num_samples = GetNumSamples(pPipeline); |
| if (pCB->activeRenderPass) { |
| if (pCB->activeRenderPass->use_dynamic_rendering || pCB->activeRenderPass->use_dynamic_rendering_inherited) { |
| // TODO: Mirror the below VUs but using dynamic rendering |
| const auto dynamic_rendering_info = pCB->activeRenderPass->dynamic_rendering_begin_rendering_info; |
| } else { |
| const auto render_pass_info = pCB->activeRenderPass->createInfo.ptr(); |
| const VkSubpassDescription2 *subpass_desc = &render_pass_info->pSubpasses[pCB->activeSubpass]; |
| uint32_t i; |
| unsigned subpass_num_samples = 0; |
| |
| for (i = 0; i < subpass_desc->colorAttachmentCount; i++) { |
| const auto attachment = subpass_desc->pColorAttachments[i].attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| subpass_num_samples |= static_cast<unsigned>(render_pass_info->pAttachments[attachment].samples); |
| |
| const auto *imageview_state = pCB->GetActiveAttachmentImageViewState(attachment); |
| const auto *color_blend_state = pPipeline->ColorBlendState(); |
| if (imageview_state && color_blend_state && (attachment < color_blend_state->attachmentCount)) { |
| if ((imageview_state->format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR) == 0 && |
| color_blend_state->pAttachments[i].blendEnable != VK_FALSE) { |
| skip |= |
| LogError(pPipeline->pipeline(), vuid.blend_enable, |
| "%s: Image view's format features of the color attachment (%" PRIu32 |
| ") of the active subpass do not contain VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT " |
| "bit, but active pipeline's pAttachments[%" PRIu32 "].blendEnable is not VK_FALSE.", |
| caller, attachment, attachment); |
| } |
| } |
| } |
| } |
| |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const auto attachment = subpass_desc->pDepthStencilAttachment->attachment; |
| subpass_num_samples |= static_cast<unsigned>(render_pass_info->pAttachments[attachment].samples); |
| } |
| |
| if (!(IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples) || |
| IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples)) && |
| ((subpass_num_samples & static_cast<unsigned>(pso_num_samples)) != subpass_num_samples)) { |
| LogObjectList objlist(pPipeline->pipeline()); |
| objlist.add(pCB->activeRenderPass->renderPass()); |
| skip |= |
| LogError(objlist, vuid.rasterization_samples, |
| "%s: In %s the sample count is %s while the current %s has %s and they need to be the same.", caller, |
| report_data->FormatHandle(pPipeline->pipeline()).c_str(), string_VkSampleCountFlagBits(pso_num_samples), |
| report_data->FormatHandle(pCB->activeRenderPass->renderPass()).c_str(), |
| string_VkSampleCountFlags(static_cast<VkSampleCountFlags>(subpass_num_samples)).c_str()); |
| } |
| } |
| } else { |
| skip |= LogError(pPipeline->pipeline(), kVUID_Core_DrawState_NoActiveRenderpass, |
| "%s: No active render pass found at draw-time in %s!", caller, |
| report_data->FormatHandle(pPipeline->pipeline()).c_str()); |
| } |
| } |
| // Verify that PSO creation renderPass is compatible with active renderPass |
| if (pCB->activeRenderPass && !pCB->activeRenderPass->use_dynamic_rendering) { |
| const auto &rp_state = pPipeline->RenderPassState(); |
| // TODO: AMD extension codes are included here, but actual function entrypoints are not yet intercepted |
| if (pCB->activeRenderPass->renderPass() != rp_state->renderPass()) { |
| // renderPass that PSO was created with must be compatible with active renderPass that PSO is being used with |
| skip |= ValidateRenderPassCompatibility("active render pass", pCB->activeRenderPass.get(), "pipeline state object", |
| rp_state.get(), caller, vuid.render_pass_compatible); |
| } |
| const auto subpass = pPipeline->Subpass(); |
| if (subpass != pCB->activeSubpass) { |
| skip |= LogError(pPipeline->pipeline(), vuid.subpass_index, |
| "%s: Pipeline was built for subpass %u but used in subpass %u.", caller, subpass, pCB->activeSubpass); |
| } |
| // Check if depth stencil attachment was created with sample location compatible bit |
| if (pPipeline->SampleLocationEnabled() == VK_TRUE) { |
| const safe_VkAttachmentReference2 *ds_attachment = |
| pCB->activeRenderPass->createInfo.pSubpasses[pCB->activeSubpass].pDepthStencilAttachment; |
| const FRAMEBUFFER_STATE *fb_state = pCB->activeFramebuffer.get(); |
| if ((ds_attachment != nullptr) && (fb_state != nullptr)) { |
| const uint32_t attachment = ds_attachment->attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| const auto *imageview_state = pCB->GetActiveAttachmentImageViewState(attachment); |
| if (imageview_state != nullptr) { |
| const auto *image_state = imageview_state->image_state.get(); |
| if (image_state != nullptr) { |
| if ((image_state->createInfo.flags & VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT) == 0) { |
| skip |= LogError(pPipeline->pipeline(), vuid.sample_location, |
| "%s: sampleLocationsEnable is true for the pipeline, but the subpass (%u) depth " |
| "stencil attachment's VkImage was not created with " |
| "VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT.", |
| caller, pCB->activeSubpass); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| skip |= ValidateStatus(pCB, CBSTATUS_PATCH_CONTROL_POINTS_SET, "Dynamic patch control points not set for this command buffer", |
| vuid.patch_control_points); |
| skip |= ValidateStatus(pCB, CBSTATUS_RASTERIZER_DISCARD_ENABLE_SET, |
| "Dynamic rasterizer discard enable not set for this command buffer", vuid.rasterizer_discard_enable); |
| skip |= ValidateStatus(pCB, CBSTATUS_DEPTH_BIAS_ENABLE_SET, "Dynamic depth bias enable not set for this command buffer", |
| vuid.depth_bias_enable); |
| skip |= ValidateStatus(pCB, CBSTATUS_LOGIC_OP_SET, "Dynamic state logicOp not set for this command buffer", vuid.logic_op); |
| skip |= ValidateStatus(pCB, CBSTATUS_PRIMITIVE_RESTART_ENABLE_SET, |
| "Dynamic primitive restart enable not set for this command buffer", vuid.primitive_restart_enable); |
| skip |= ValidateStatus(pCB, CBSTATUS_VERTEX_INPUT_BINDING_STRIDE_SET, |
| "Dynamic vertex input binding stride not set for this command buffer", vuid.vertex_input_binding_stride); |
| skip |= |
| ValidateStatus(pCB, CBSTATUS_VERTEX_INPUT_SET, "Dynamic vertex input not set for this command buffer", vuid.vertex_input); |
| skip |= ValidateStatus(pCB, CBSTATUS_COLOR_WRITE_ENABLE_SET, "Dynamic color write enable not set for this command buffer", |
| vuid.color_write_enable); |
| if (IsDynamic(pPipeline, VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT) && pCB->status & CBSTATUS_COLOR_WRITE_ENABLE_SET) { |
| uint32_t blend_attachment_count = |
| pCB->GetCurrentPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS)->ColorBlendState()->attachmentCount; |
| if (pCB->dynamicColorWriteEnableAttachmentCount != blend_attachment_count) { |
| skip |= LogError( |
| pCB->commandBuffer(), vuid.color_write_enable, |
| "%s(): Currently bound pipeline was created with VkPipelineColorBlendStateCreateInfo::attachmentCount %" PRIu32 |
| " and VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT, but the number of attachments written by " |
| "vkCmdSetColorWriteEnableEXT() is %" PRIu32 ".", |
| caller, blend_attachment_count, pCB->dynamicColorWriteEnableAttachmentCount); |
| } |
| } |
| skip |= ValidateStatus(pCB, CBSTATUS_SAMPLE_LOCATIONS_SET, "Dynamic sample locations not set for this command buffer", |
| vuid.dynamic_sample_locations); |
| |
| // VUID {refpage}-primitiveTopology-03420 |
| skip |= ValidateStatus(pCB, CBSTATUS_PRIMITIVE_TOPOLOGY_SET, "Dynamic primitive topology state not set for this command buffer", |
| vuid.primitive_topology); |
| if (IsDynamic(pPipeline, VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT)) { |
| bool compatible_topology = false; |
| const auto input_assembly_state = pPipeline->InputAssemblyState(); |
| switch (input_assembly_state->topology) { |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: |
| switch (pCB->primitiveTopology) { |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: |
| compatible_topology = true; |
| break; |
| default: |
| break; |
| } |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: |
| switch (pCB->primitiveTopology) { |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: |
| compatible_topology = true; |
| break; |
| default: |
| break; |
| } |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: |
| switch (pCB->primitiveTopology) { |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: |
| compatible_topology = true; |
| break; |
| default: |
| break; |
| } |
| break; |
| case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: |
| switch (pCB->primitiveTopology) { |
| case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: |
| compatible_topology = true; |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| if (!compatible_topology) { |
| skip |= LogError(pPipeline->pipeline(), vuid.primitive_topology, |
| "%s: the last primitive topology %s state set by vkCmdSetPrimitiveTopologyEXT is " |
| "not compatible with the pipeline topology %s.", |
| caller, string_VkPrimitiveTopology(pCB->primitiveTopology), |
| string_VkPrimitiveTopology(input_assembly_state->topology)); |
| } |
| } |
| |
| if (pPipeline->fragment_output_state->dual_source_blending) { |
| uint32_t count = pCB->activeRenderPass->createInfo.pSubpasses[pCB->activeSubpass].colorAttachmentCount; |
| if (count > phys_dev_props.limits.maxFragmentDualSrcAttachments) { |
| skip |= |
| LogError(pPipeline->pipeline(), "VUID-RuntimeSpirv-Fragment-06427", |
| "%s: Dual source blend mode is used, but the number of written fragment shader output attachment (%" PRIu32 |
| ") is greater than maxFragmentDualSrcAttachments (%" PRIu32 ")", |
| caller, count, phys_dev_props.limits.maxFragmentDualSrcAttachments); |
| } |
| } |
| |
| if (enabled_features.fragment_shading_rate_features.primitiveFragmentShadingRate) { |
| skip |= ValidateGraphicsPipelineShaderDynamicState(pPipeline, pCB, caller, vuid); |
| } |
| |
| return skip; |
| } |
| |
| // For given cvdescriptorset::DescriptorSet, verify that its Set is compatible w/ the setLayout corresponding to |
| // pipelineLayout[layoutIndex] |
| static bool VerifySetLayoutCompatibility(const debug_report_data *report_data, const cvdescriptorset::DescriptorSet *descriptor_set, |
| PIPELINE_LAYOUT_STATE const *pipeline_layout, const uint32_t layoutIndex, |
| string &errorMsg) { |
| auto num_sets = pipeline_layout->set_layouts.size(); |
| if (layoutIndex >= num_sets) { |
| stringstream error_str; |
| error_str << report_data->FormatHandle(pipeline_layout->layout()) << ") only contains " << num_sets |
| << " setLayouts corresponding to sets 0-" << num_sets - 1 << ", but you're attempting to bind set to index " |
| << layoutIndex; |
| errorMsg = error_str.str(); |
| return false; |
| } |
| if (descriptor_set->IsPushDescriptor()) return true; |
| const auto *layout_node = pipeline_layout->set_layouts[layoutIndex].get(); |
| if (layout_node && (descriptor_set->GetBindingCount() > 0) && (layout_node->GetBindingCount() > 0)) { |
| return cvdescriptorset::VerifySetLayoutCompatibility(report_data, layout_node, descriptor_set->GetLayout().get(), |
| &errorMsg); |
| } else { |
| // It's possible the DSL is null when creating a graphics pipeline library, in which case we can't verify compatibility |
| // here. |
| return true; |
| } |
| } |
| |
| // Validate overall state at the time of a draw call |
| bool CoreChecks::ValidateCmdBufDrawState(const CMD_BUFFER_STATE *cb_node, CMD_TYPE cmd_type, const bool indexed, |
| const VkPipelineBindPoint bind_point) const { |
| const DrawDispatchVuid vuid = GetDrawDispatchVuid(cmd_type); |
| const char *function = CommandTypeString(cmd_type); |
| const auto lv_bind_point = ConvertToLvlBindPoint(bind_point); |
| const auto &state = cb_node->lastBound[lv_bind_point]; |
| const auto *pipe = state.pipeline_state; |
| |
| if (nullptr == pipe) { |
| return LogError(cb_node->commandBuffer(), vuid.pipeline_bound, |
| "Must not call %s on this command buffer while there is no %s pipeline bound.", function, |
| bind_point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR |
| ? "RayTracing" |
| : bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS ? "Graphics" : "Compute"); |
| } |
| |
| bool result = false; |
| |
| if (VK_PIPELINE_BIND_POINT_GRAPHICS == bind_point) { |
| // First check flag states |
| result |= ValidateDrawStateFlags(cb_node, pipe, indexed, vuid.dynamic_state); |
| |
| if (cb_node->activeRenderPass && cb_node->activeFramebuffer) { |
| // Verify attachments for unprotected/protected command buffer. |
| if (enabled_features.core11.protectedMemory == VK_TRUE && cb_node->active_attachments) { |
| uint32_t i = 0; |
| for (const auto &view_state : *cb_node->active_attachments.get()) { |
| const auto &subpass = cb_node->active_subpasses->at(i); |
| if (subpass.used && view_state && !view_state->Destroyed()) { |
| std::string image_desc = "Image is "; |
| image_desc.append(string_VkImageUsageFlagBits(subpass.usage)); |
| // Because inputAttachment is read only, it doesn't need to care protected command buffer case. |
| // Some CMD_TYPE could not be protected. See VUID 02711. |
| if (subpass.usage != VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT && |
| vuid.protected_command_buffer != kVUIDUndefined) { |
| result |= ValidateUnprotectedImage(cb_node, view_state->image_state.get(), function, |
| vuid.protected_command_buffer, image_desc.c_str()); |
| } |
| result |= ValidateProtectedImage(cb_node, view_state->image_state.get(), function, |
| vuid.unprotected_command_buffer, image_desc.c_str()); |
| } |
| ++i; |
| } |
| } |
| } |
| } |
| // Now complete other state checks |
| string error_string; |
| auto const &pipeline_layout = pipe->PipelineLayoutState(); |
| |
| // Check if the current pipeline is compatible for the maximum used set with the bound sets. |
| if (pipe->active_slots.size() > 0 && !CompatForSet(pipe->max_active_slot, state, pipeline_layout->compat_for_set)) { |
| LogObjectList objlist(pipe->pipeline()); |
| objlist.add(pipeline_layout->layout()); |
| objlist.add(state.pipeline_layout); |
| result |= LogError(objlist, vuid.compatible_pipeline, |
| "%s(): %s defined with %s is not compatible for maximum set statically used %" PRIu32 |
| " with bound descriptor sets, last bound with %s", |
| CommandTypeString(cmd_type), report_data->FormatHandle(pipe->pipeline()).c_str(), |
| report_data->FormatHandle(pipeline_layout->layout()).c_str(), pipe->max_active_slot, |
| report_data->FormatHandle(state.pipeline_layout).c_str()); |
| } |
| |
| for (const auto &set_binding_pair : pipe->active_slots) { |
| uint32_t set_index = set_binding_pair.first; |
| // If valid set is not bound throw an error |
| if ((state.per_set.size() <= set_index) || (!state.per_set[set_index].bound_descriptor_set)) { |
| result |= LogError(cb_node->commandBuffer(), kVUID_Core_DrawState_DescriptorSetNotBound, |
| "%s(): %s uses set #%u but that set is not bound.", CommandTypeString(cmd_type), |
| report_data->FormatHandle(pipe->pipeline()).c_str(), set_index); |
| } else if (!VerifySetLayoutCompatibility(report_data, state.per_set[set_index].bound_descriptor_set.get(), |
| pipeline_layout.get(), set_index, error_string)) { |
| // Set is bound but not compatible w/ overlapping pipeline_layout from PSO |
| VkDescriptorSet set_handle = state.per_set[set_index].bound_descriptor_set->GetSet(); |
| LogObjectList objlist(set_handle); |
| objlist.add(pipeline_layout->layout()); |
| result |= LogError(objlist, kVUID_Core_DrawState_PipelineLayoutsIncompatible, |
| "%s(): %s bound as set #%u is not compatible with overlapping %s due to: %s", |
| CommandTypeString(cmd_type), report_data->FormatHandle(set_handle).c_str(), set_index, |
| report_data->FormatHandle(pipeline_layout->layout()).c_str(), error_string.c_str()); |
| } else { // Valid set is bound and layout compatible, validate that it's updated |
| // Pull the set node |
| const auto *descriptor_set = state.per_set[set_index].bound_descriptor_set.get(); |
| // Validate the draw-time state for this descriptor set |
| std::string err_str; |
| // For the "bindless" style resource usage with many descriptors, need to optimize command <-> descriptor |
| // binding validation. Take the requested binding set and prefilter it to eliminate redundant validation checks. |
| // Here, the currently bound pipeline determines whether an image validation check is redundant... |
| // for images are the "req" portion of the binding_req is indirectly (but tightly) coupled to the pipeline. |
| cvdescriptorset::PrefilterBindRequestMap reduced_map(*descriptor_set, set_binding_pair.second); |
| const auto &binding_req_map = reduced_map.FilteredMap(*cb_node, *pipe); |
| |
| // We can skip validating the descriptor set if "nothing" has changed since the last validation. |
| // Same set, no image layout changes, and same "pipeline state" (binding_req_map). If there are |
| // any dynamic descriptors, always revalidate rather than caching the values. We currently only |
| // apply this optimization if IsManyDescriptors is true, to avoid the overhead of copying the |
| // binding_req_map which could potentially be expensive. |
| bool descriptor_set_changed = |
| !reduced_map.IsManyDescriptors() || |
| // Revalidate each time if the set has dynamic offsets |
| state.per_set[set_index].dynamicOffsets.size() > 0 || |
| // Revalidate if descriptor set (or contents) has changed |
| state.per_set[set_index].validated_set != descriptor_set || |
| state.per_set[set_index].validated_set_change_count != descriptor_set->GetChangeCount() || |
| (!disabled[image_layout_validation] && |
| state.per_set[set_index].validated_set_image_layout_change_count != cb_node->image_layout_change_count); |
| bool need_validate = descriptor_set_changed || |
| // Revalidate if previous bindingReqMap doesn't include new bindingReqMap |
| !std::includes(state.per_set[set_index].validated_set_binding_req_map.begin(), |
| state.per_set[set_index].validated_set_binding_req_map.end(), |
| binding_req_map.begin(), binding_req_map.end()); |
| |
| if (need_validate) { |
| if (!descriptor_set_changed && reduced_map.IsManyDescriptors()) { |
| // Only validate the bindings that haven't already been validated |
| BindingReqMap delta_reqs; |
| std::set_difference(binding_req_map.begin(), binding_req_map.end(), |
| state.per_set[set_index].validated_set_binding_req_map.begin(), |
| state.per_set[set_index].validated_set_binding_req_map.end(), |
| layer_data::insert_iterator<BindingReqMap>(delta_reqs, delta_reqs.begin())); |
| result |= |
| ValidateDrawState(descriptor_set, delta_reqs, state.per_set[set_index].dynamicOffsets, cb_node, |
| cb_node->active_attachments.get(), cb_node->active_subpasses.get(), function, vuid); |
| } else { |
| result |= |
| ValidateDrawState(descriptor_set, binding_req_map, state.per_set[set_index].dynamicOffsets, cb_node, |
| cb_node->active_attachments.get(), cb_node->active_subpasses.get(), function, vuid); |
| } |
| } |
| } |
| } |
| |
| // Check general pipeline state that needs to be validated at drawtime |
| if (VK_PIPELINE_BIND_POINT_GRAPHICS == bind_point) { |
| result |= ValidatePipelineDrawtimeState(state, cb_node, cmd_type, pipe); |
| } |
| |
| // Verify if push constants have been set |
| // NOTE: Currently not checking whether active push constants are compatible with the active pipeline, nor whether the |
| // "life times" of push constants are correct. |
| // Discussion on validity of these checks can be found at https://gitlab.khronos.org/vulkan/vulkan/-/issues/2602. |
| if (!cb_node->push_constant_data_ranges || (pipeline_layout->push_constant_ranges == cb_node->push_constant_data_ranges)) { |
| for (const auto &stage : pipe->stage_state) { |
| const auto *entrypoint = stage.module_state->FindEntrypointStruct(stage.create_info->pName, stage.create_info->stage); |
| if (!entrypoint || !entrypoint->push_constant_used_in_shader.IsUsed()) { |
| continue; |
| } |
| |
| // Edge case where if the shader is using push constants statically and there never was a vkCmdPushConstants |
| if (!cb_node->push_constant_data_ranges && !enabled_features.core13.maintenance4) { |
| LogObjectList objlist(cb_node->commandBuffer()); |
| objlist.add(pipeline_layout->layout()); |
| objlist.add(pipe->pipeline()); |
| result |= LogError(objlist, vuid.push_constants_set, |
| "%s(): Shader in %s uses push-constant statically but vkCmdPushConstants was not called yet for " |
| "pipeline layout %s.", |
| CommandTypeString(cmd_type), string_VkShaderStageFlags(stage.stage_flag).c_str(), |
| report_data->FormatHandle(pipeline_layout->layout()).c_str()); |
| } |
| |
| const auto it = cb_node->push_constant_data_update.find(stage.stage_flag); |
| if (it == cb_node->push_constant_data_update.end()) { |
| // This error has been printed in ValidatePushConstantUsage. |
| break; |
| } |
| } |
| } |
| return result; |
| } |
| |
| bool CoreChecks::ValidateCmdRayQueryState(const CMD_BUFFER_STATE *cb_state, CMD_TYPE cmd_type, |
| const VkPipelineBindPoint bind_point) const { |
| bool skip = false; |
| const DrawDispatchVuid vuid = GetDrawDispatchVuid(cmd_type); |
| const auto lv_bind_point = ConvertToLvlBindPoint(bind_point); |
| const auto &state = cb_state->lastBound[lv_bind_point]; |
| const auto *pipe = state.pipeline_state; |
| |
| bool ray_query_shader = false; |
| if (nullptr != pipe) { |
| if (bind_point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) { |
| ray_query_shader = true; |
| } else { |
| // TODO - Loop through shader for RayQueryKHR for draw/dispatch commands |
| } |
| } |
| |
| if (cb_state->unprotected == false && ray_query_shader) { |
| skip |= LogError(cb_state->commandBuffer(), vuid.ray_query_protected_cb, |
| "%s(): can't use in protected command buffers for RayQuery operations.", CommandTypeString(cmd_type)); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineBlendEnable(const PIPELINE_STATE *pPipeline) const { |
| bool skip = false; |
| const auto *color_blend_state = pPipeline->ColorBlendState(); |
| const auto &rp_state = pPipeline->RenderPassState(); |
| if (rp_state && color_blend_state) { |
| const auto subpass = pPipeline->Subpass(); |
| const auto *subpass_desc = &rp_state->createInfo.pSubpasses[subpass]; |
| |
| uint32_t numberColorAttachments = (rp_state->use_dynamic_rendering) |
| ? rp_state->dynamic_rendering_pipeline_create_info.colorAttachmentCount |
| : subpass_desc->colorAttachmentCount; |
| |
| for (uint32_t i = 0; i < pPipeline->Attachments().size() && i < numberColorAttachments; ++i) { |
| VkFormatFeatureFlags2KHR format_features; |
| |
| if (rp_state->use_dynamic_rendering) { |
| if (color_blend_state->attachmentCount != numberColorAttachments) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06055", |
| "Pipeline %s: VkPipelineRenderingCreateInfoKHR::colorAttachmentCount (%" PRIu32 |
| ") must equal pColorBlendState->attachmentCount (%" PRIu32 ")", |
| report_data->FormatHandle(pPipeline->pipeline()).c_str(), numberColorAttachments, |
| color_blend_state->attachmentCount); |
| } |
| } else { |
| const auto attachment = subpass_desc->pColorAttachments[i].attachment; |
| if (attachment == VK_ATTACHMENT_UNUSED) continue; |
| |
| const auto attachment_desc = rp_state->createInfo.pAttachments[attachment]; |
| format_features = GetPotentialFormatFeatures(attachment_desc.format); |
| |
| const auto *raster_state = pPipeline->RasterizationState(); |
| if (raster_state && !raster_state->rasterizerDiscardEnable && pPipeline->Attachments()[i].blendEnable && |
| !(format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR)) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06041", |
| "vkCreateGraphicsPipelines(): pipeline.pColorBlendState.pAttachments[%" PRIu32 |
| "].blendEnable is VK_TRUE but format %s of the corresponding attachment description (subpass %" PRIu32 |
| ", attachment %" PRIu32 ") does not support VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT.", |
| i, string_VkFormat(attachment_desc.format), subpass, attachment); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineLocked(std::vector<std::shared_ptr<PIPELINE_STATE>> const &pPipelines, int pipelineIndex) const { |
| bool skip = false; |
| |
| const auto *pipeline = pPipelines[pipelineIndex].get(); |
| // If create derivative bit is set, check that we've specified a base |
| // pipeline correctly, and that the base pipeline was created to allow |
| // derivatives. |
| if (pipeline->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_DERIVATIVE_BIT) { |
| std::shared_ptr<const PIPELINE_STATE> base_pipeline; |
| const auto base_handle = pipeline->BasePipeline(); |
| const auto base_index = pipeline->BasePipelineIndex(); |
| if (!((base_handle != VK_NULL_HANDLE) ^ (base_index != -1))) { |
| // TODO: This check is a superset of VUID-VkGraphicsPipelineCreateInfo-flags-00724 and |
| // TODO: VUID-VkGraphicsPipelineCreateInfo-flags-00725 |
| skip |= LogError(device, kVUID_Core_DrawState_InvalidPipelineCreateState, |
| "Invalid Pipeline CreateInfo[%d]: exactly one of base pipeline index and handle must be specified", |
| pipelineIndex); |
| } else if (base_index != -1) { |
| if (base_index >= pipelineIndex) { |
| skip |= |
| LogError(device, "VUID-vkCreateGraphicsPipelines-flags-00720", |
| "Invalid Pipeline CreateInfo[%d]: base pipeline must occur earlier in array than derivative pipeline.", |
| pipelineIndex); |
| } else { |
| base_pipeline = pPipelines[base_index]; |
| } |
| } else if (base_handle != VK_NULL_HANDLE) { |
| base_pipeline = Get<PIPELINE_STATE>(base_handle); |
| } |
| |
| if (base_pipeline && !(base_pipeline->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT)) { |
| skip |= LogError(device, "VUID-vkCreateGraphicsPipelines-flags-00721", |
| "Invalid Pipeline CreateInfo[%d]: base pipeline does not allow derivatives.", pipelineIndex); |
| } |
| } |
| |
| // Check for portability errors |
| if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) { |
| if ((VK_FALSE == enabled_features.portability_subset_features.triangleFans) && |
| (VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN == pipeline->topology_at_rasterizer)) { |
| skip |= |
| LogError(device, "VUID-VkPipelineInputAssemblyStateCreateInfo-triangleFans-04452", |
| "Invalid Pipeline CreateInfo[%d] (portability error): VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN is not supported", |
| pipelineIndex); |
| } |
| |
| // Validate vertex inputs |
| for (const auto &desc : pipeline->vertex_input_state->binding_descriptions) { |
| const auto min_alignment = phys_dev_ext_props.portability_props.minVertexInputBindingStrideAlignment; |
| if ((desc.stride < min_alignment) || (min_alignment == 0) || ((desc.stride % min_alignment) != 0)) { |
| skip |= LogError( |
| device, "VUID-VkVertexInputBindingDescription-stride-04456", |
| "Invalid Pipeline CreateInfo[%d] (portability error): Vertex input stride must be at least as large as and a " |
| "multiple of VkPhysicalDevicePortabilitySubsetPropertiesKHR::minVertexInputBindingStrideAlignment.", |
| pipelineIndex); |
| } |
| } |
| |
| // Validate vertex attributes |
| if (VK_FALSE == enabled_features.portability_subset_features.vertexAttributeAccessBeyondStride) { |
| for (const auto &attrib : pipeline->vertex_input_state->vertex_attribute_descriptions) { |
| const auto vertex_binding_map_it = pipeline->vertex_input_state->binding_to_index_map.find(attrib.binding); |
| if (vertex_binding_map_it != pipeline->vertex_input_state->binding_to_index_map.cend()) { |
| const auto &desc = pipeline->vertex_input_state->binding_descriptions[vertex_binding_map_it->second]; |
| if ((attrib.offset + FormatElementSize(attrib.format)) > desc.stride) { |
| skip |= LogError(device, "VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457", |
| "Invalid Pipeline CreateInfo[%d] (portability error): (attribute.offset + " |
| "sizeof(vertex_description.format)) is larger than the vertex stride", |
| pipelineIndex); |
| } |
| } |
| } |
| } |
| |
| // Validate polygon mode |
| auto raster_state_ci = pipeline->RasterizationState(); |
| if ((VK_FALSE == enabled_features.portability_subset_features.pointPolygons) && raster_state_ci && |
| (VK_FALSE == raster_state_ci->rasterizerDiscardEnable) && (VK_POLYGON_MODE_POINT == raster_state_ci->polygonMode)) { |
| skip |= |
| LogError(device, "VUID-VkPipelineRasterizationStateCreateInfo-pointPolygons-04458", |
| "Invalid Pipeline CreateInfo[%d] (portability error): point polygons are not supported", pipelineIndex); |
| } |
| } |
| |
| return skip; |
| } |
| |
| // UNLOCKED pipeline validation. DO NOT lookup objects in the CoreChecks->* maps in this function. |
| bool CoreChecks::ValidatePipelineUnlocked(const PIPELINE_STATE *pPipeline, uint32_t pipelineIndex) const { |
| bool skip = false; |
| safe_VkSubpassDescription2 *subpass_desc = nullptr; |
| |
| const auto &rp_state = pPipeline->RenderPassState(); |
| |
| const bool rp_state_required = |
| pPipeline->pre_raster_state || pPipeline->fragment_shader_state || pPipeline->fragment_output_state; |
| if (rp_state_required) { |
| if (!rp_state) { |
| if (!IsExtEnabled(device_extensions.vk_khr_dynamic_rendering)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06574", |
| "vkCreateGraphicsPipelines(): pCreateInfos[%" PRIu32 |
| "] requires a valid renderPass, but one was not provided", |
| pipelineIndex); |
| } else if (!enabled_features.core13.dynamicRendering) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06575", |
| "vkCreateGraphicsPipelines(): pCreateInfos[%" PRIu32 |
| "] requires a valid renderPass, but one was not provided", |
| pipelineIndex); |
| } |
| } |
| } |
| |
| const auto subpass = pPipeline->Subpass(); |
| if (rp_state && !rp_state->use_dynamic_rendering) { |
| // Ensure the subpass index is valid. If not, then ValidateGraphicsPipelineShaderState |
| // produces nonsense errors that confuse users. Other layers should already |
| // emit errors for renderpass being invalid. |
| subpass_desc = &rp_state->createInfo.pSubpasses[subpass]; |
| if (subpass >= rp_state->createInfo.subpassCount) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06046", |
| "vkCreateGraphicsPipelines() Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: Subpass index %u is out of range for this renderpass (0..%u).", |
| pipelineIndex, subpass, rp_state->createInfo.subpassCount - 1); |
| subpass_desc = nullptr; |
| } |
| } |
| |
| const auto ds_state = pPipeline->DepthStencilState(); |
| if (ds_state) { |
| if ((((ds_state->flags & VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM) != |
| 0) || |
| ((ds_state->flags & VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM) != |
| 0)) && |
| rp_state) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-flags-06483", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pDepthStencilState[%s] contains " |
| "VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM or" |
| " VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM, " |
| "renderpass must" |
| "not be VK_NULL_HANDLE.", |
| pipelineIndex, string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str()); |
| } |
| } |
| |
| const auto color_blend_state = pPipeline->ColorBlendState(); |
| if (color_blend_state) { |
| if (((color_blend_state->flags & VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM) != |
| 0) && |
| rp_state) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-flags-06482", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: created with" |
| "VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM," |
| "renderpass must not be VK_NULL_HANDLE.", |
| pipelineIndex); |
| } |
| |
| // VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV |
| auto attachment_sample_count_info = LvlFindInChain<VkAttachmentSampleCountInfoAMD>(pPipeline->PNext()); |
| const auto rendering_struct = LvlFindInChain<VkPipelineRenderingCreateInfo>(pPipeline->PNext()); |
| if (rendering_struct && attachment_sample_count_info && |
| (attachment_sample_count_info->colorAttachmentCount != rendering_struct->colorAttachmentCount)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06063", |
| "vkCreateGraphicsPipelines() Pipeline %" PRIu32 |
| " interface VkAttachmentSampleCountInfo->attachmentCount %" PRIu32 |
| " and " |
| "VkPipelineRenderingCreateInfoKHR->colorAttachmentCount %" PRIu32 " must be equal", |
| pipelineIndex, |
| attachment_sample_count_info->colorAttachmentCount, |
| rendering_struct->colorAttachmentCount); |
| } |
| |
| if (subpass_desc && color_blend_state->attachmentCount != subpass_desc->colorAttachmentCount) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06042", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: %s subpass %u has colorAttachmentCount of %u which doesn't " |
| "match the pColorBlendState->attachmentCount of %u.", |
| pipelineIndex, report_data->FormatHandle(rp_state->renderPass()).c_str(), subpass, |
| subpass_desc->colorAttachmentCount, color_blend_state->attachmentCount); |
| } |
| const auto &pipe_attachments = pPipeline->Attachments(); |
| if (!enabled_features.core.independentBlend) { |
| if (pipe_attachments.size() > 1) { |
| const auto *const attachments = &pipe_attachments[0]; |
| for (size_t i = 1; i < pipe_attachments.size(); i++) { |
| // Quoting the spec: "If [the independent blend] feature is not enabled, the VkPipelineColorBlendAttachmentState |
| // settings for all color attachments must be identical." VkPipelineColorBlendAttachmentState contains |
| // only attachment state, so memcmp is best suited for the comparison |
| if (memcmp(static_cast<const void *>(attachments), static_cast<const void *>(&attachments[i]), |
| sizeof(attachments[0]))) { |
| skip |= LogError(device, "VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "]: If independent blend feature not enabled, all elements of " |
| "pAttachments must be identical.", |
| pipelineIndex); |
| break; |
| } |
| } |
| } |
| } |
| if (!enabled_features.core.logicOp && (color_blend_state->logicOpEnable != VK_FALSE)) { |
| skip |= LogError(device, "VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00606", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "]: If logic operations feature not enabled, logicOpEnable must be VK_FALSE.", |
| pipelineIndex); |
| } |
| for (size_t i = 0; i < pipe_attachments.size(); i++) { |
| if ((pipe_attachments[i].srcColorBlendFactor == VK_BLEND_FACTOR_SRC1_COLOR) || |
| (pipe_attachments[i].srcColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR) || |
| (pipe_attachments[i].srcColorBlendFactor == VK_BLEND_FACTOR_SRC1_ALPHA) || |
| (pipe_attachments[i].srcColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| skip |= LogError(device, "VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-00608", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState.pAttachments[" PRINTF_SIZE_T_SPECIFIER |
| "].srcColorBlendFactor uses a dual-source blend factor (%d), but this device feature is not " |
| "enabled.", |
| pipelineIndex, i, pipe_attachments[i].srcColorBlendFactor); |
| } |
| } |
| if ((pipe_attachments[i].dstColorBlendFactor == VK_BLEND_FACTOR_SRC1_COLOR) || |
| (pipe_attachments[i].dstColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR) || |
| (pipe_attachments[i].dstColorBlendFactor == VK_BLEND_FACTOR_SRC1_ALPHA) || |
| (pipe_attachments[i].dstColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| skip |= LogError(device, "VUID-VkPipelineColorBlendAttachmentState-dstColorBlendFactor-00609", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState.pAttachments[" PRINTF_SIZE_T_SPECIFIER |
| "].dstColorBlendFactor uses a dual-source blend factor (%d), but this device feature is not " |
| "enabled.", |
| pipelineIndex, i, pipe_attachments[i].dstColorBlendFactor); |
| } |
| } |
| if ((pipe_attachments[i].srcAlphaBlendFactor == VK_BLEND_FACTOR_SRC1_COLOR) || |
| (pipe_attachments[i].srcAlphaBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR) || |
| (pipe_attachments[i].srcAlphaBlendFactor == VK_BLEND_FACTOR_SRC1_ALPHA) || |
| (pipe_attachments[i].srcAlphaBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| skip |= LogError(device, "VUID-VkPipelineColorBlendAttachmentState-srcAlphaBlendFactor-00610", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState.pAttachments[" PRINTF_SIZE_T_SPECIFIER |
| "].srcAlphaBlendFactor uses a dual-source blend factor (%d), but this device feature is not " |
| "enabled.", |
| pipelineIndex, i, pipe_attachments[i].srcAlphaBlendFactor); |
| } |
| } |
| if ((pipe_attachments[i].dstAlphaBlendFactor == VK_BLEND_FACTOR_SRC1_COLOR) || |
| (pipe_attachments[i].dstAlphaBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR) || |
| (pipe_attachments[i].dstAlphaBlendFactor == VK_BLEND_FACTOR_SRC1_ALPHA) || |
| (pipe_attachments[i].dstAlphaBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| skip |= LogError(device, "VUID-VkPipelineColorBlendAttachmentState-dstAlphaBlendFactor-00611", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState.pAttachments[" PRINTF_SIZE_T_SPECIFIER |
| "].dstAlphaBlendFactor uses a dual-source blend factor (%d), but this device feature is not " |
| "enabled.", |
| pipelineIndex, i, pipe_attachments[i].dstAlphaBlendFactor); |
| } |
| } |
| } |
| auto color_write = lvl_find_in_chain<VkPipelineColorWriteCreateInfoEXT>(color_blend_state->pNext); |
| if (color_write) { |
| // if over limit don't give extra redundant error of mismatch of attachmentCount |
| if (color_write->attachmentCount > phys_dev_props.limits.maxColorAttachments) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorWriteCreateInfoEXT-attachmentCount-06655", |
| "vkCreateGraphicsPipelines(): VkPipelineColorWriteCreateInfoEXT in the pNext chain of pPipelines[%" PRIu32 |
| "].pColorBlendState has an attachmentCount of (%" PRIu32 |
| ") which is greater than the VkPhysicalDeviceLimits::maxColorAttachments limit (%" PRIu32 ").", |
| pipelineIndex, color_write->attachmentCount, phys_dev_props.limits.maxColorAttachments); |
| } else if (color_write->attachmentCount != color_blend_state->attachmentCount) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorWriteCreateInfoEXT-attachmentCount-04802", |
| "vkCreateGraphicsPipelines(): VkPipelineColorWriteCreateInfoEXT in the pNext chain of pPipelines[%" PRIu32 |
| "].pColorBlendState has different attachmentCount (%" PRIu32 ") than pColorBlendState.attachmentCount (%" PRIu32 |
| ").", |
| pipelineIndex, color_write->attachmentCount, color_blend_state->attachmentCount); |
| } |
| if (!enabled_features.color_write_features.colorWriteEnable) { |
| for (uint32_t i = 0; i < color_write->attachmentCount; ++i) { |
| if (color_write->pColorWriteEnables[i] != VK_TRUE) { |
| skip |= LogError(device, "VUID-VkPipelineColorWriteCreateInfoEXT-pAttachments-04801", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState pNext chain includes VkPipelineColorWriteCreateInfoEXT with " |
| "pColorWriteEnables[%" PRIu32 "] = VK_FALSE, but colorWriteEnable is not enabled.", |
| pipelineIndex, i); |
| } |
| } |
| } |
| } |
| const auto *color_blend_advanced = LvlFindInChain<VkPipelineColorBlendAdvancedStateCreateInfoEXT>(color_blend_state->pNext); |
| if (color_blend_advanced) { |
| if (!phys_dev_ext_props.blend_operation_advanced_props.advancedBlendCorrelatedOverlap && |
| color_blend_advanced->blendOverlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-blendOverlap-01426", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState pNext chain contains VkPipelineColorBlendAdvancedStateCreateInfoEXT structure with " |
| "blendOverlap equal to %s, but " |
| "VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT::advancedBlendCorrelatedOverlap is not supported.", |
| pipelineIndex, string_VkBlendOverlapEXT(color_blend_advanced->blendOverlap)); |
| } |
| if (!phys_dev_ext_props.blend_operation_advanced_props.advancedBlendNonPremultipliedDstColor && |
| color_blend_advanced->dstPremultiplied != VK_TRUE) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-dstPremultiplied-01425", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState pNext chain contains VkPipelineColorBlendAdvancedStateCreateInfoEXT structure with " |
| "dstPremultiplied equal to VK_FALSE, but " |
| "VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT::advancedBlendNonPremultipliedDstColor is not supported.", |
| pipelineIndex); |
| } |
| if (!phys_dev_ext_props.blend_operation_advanced_props.advancedBlendNonPremultipliedSrcColor && |
| color_blend_advanced->srcPremultiplied != VK_TRUE) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-srcPremultiplied-01424", |
| "vkCreateGraphicsPipelines(): pPipelines[%" PRIu32 |
| "].pColorBlendState pNext chain contains VkPipelineColorBlendAdvancedStateCreateInfoEXT structure with " |
| "srcPremultiplied equal to VK_FALSE, but " |
| "VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT::advancedBlendNonPremultipliedSrcColor is not supported.", |
| pipelineIndex); |
| } |
| } |
| } |
| |
| if (ValidateGraphicsPipelineShaderState(pPipeline)) { |
| skip = true; |
| } |
| skip |= ValidateGraphicsPipelineBlendEnable(pPipeline); |
| |
| if (pPipeline->pre_raster_state) { |
| // Each shader's stage must be unique |
| for (uint32_t stage = VK_SHADER_STAGE_VERTEX_BIT; stage & VK_SHADER_STAGE_ALL_GRAPHICS; stage <<= 1) { |
| if (pPipeline->active_shaders & stage) { |
| const auto &states = pPipeline->stage_state; |
| if (std::count_if(states.begin(), states.end(), |
| [stage](const PipelineStageState &pss) { return stage == pss.stage_flag; }) > 1) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-stage-00726", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Multiple shaders provided for stage %s", |
| pipelineIndex, string_VkShaderStageFlagBits(VkShaderStageFlagBits(stage))); |
| } |
| } |
| } |
| if (!enabled_features.core.geometryShader && (pPipeline->active_shaders & VK_SHADER_STAGE_GEOMETRY_BIT)) { |
| skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-stage-00704", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Geometry Shader not supported.", pipelineIndex); |
| } |
| if (!enabled_features.core.tessellationShader && |
| (pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || |
| pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) { |
| skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-stage-00705", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Tessellation Shader not supported.", pipelineIndex); |
| } |
| if (IsExtEnabled(device_extensions.vk_nv_mesh_shader)) { |
| // VS or mesh is required |
| if (!(pPipeline->active_shaders & (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_MESH_BIT_NV))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-stage-02096", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Vertex Shader or Mesh Shader required.", |
| pipelineIndex); |
| } |
| // Can't mix mesh and VTG |
| if ((pPipeline->active_shaders & (VK_SHADER_STAGE_MESH_BIT_NV | VK_SHADER_STAGE_TASK_BIT_NV)) && |
| (pPipeline->active_shaders & |
| (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | |
| VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pStages-02095", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: Geometric shader stages must either be all mesh (mesh | task) " |
| "or all VTG (vertex, tess control, tess eval, geom).", |
| pipelineIndex); |
| } |
| } else { |
| // VS is required if this is a "normal" pipeline or is a pre-raster graphics library |
| if (pPipeline->pre_raster_state && !(pPipeline->active_shaders & VK_SHADER_STAGE_VERTEX_BIT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-stage-00727", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Vertex Shader required.", pipelineIndex); |
| } |
| } |
| |
| if (!enabled_features.mesh_shader_features.meshShader && (pPipeline->active_shaders & VK_SHADER_STAGE_MESH_BIT_NV)) { |
| skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-stage-02091", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Mesh Shader not supported.", pipelineIndex); |
| } |
| |
| if (!enabled_features.mesh_shader_features.taskShader && (pPipeline->active_shaders & VK_SHADER_STAGE_TASK_BIT_NV)) { |
| skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-stage-02092", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Task Shader not supported.", pipelineIndex); |
| } |
| |
| // Either both or neither TC/TE shaders should be defined |
| bool has_control = (pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0; |
| bool has_eval = (pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0; |
| if (has_control && !has_eval) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pStages-00729", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: TE and TC shaders must be included or excluded as a pair.", |
| pipelineIndex); |
| } |
| if (!has_control && has_eval) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pStages-00730", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: TE and TC shaders must be included or excluded as a pair.", |
| pipelineIndex); |
| } |
| |
| const auto *ia_state = pPipeline->InputAssemblyState(); |
| if (!ia_state) { |
| if ((!pPipeline->IsGraphicsLibrary() && |
| (pPipeline->active_shaders & VK_SHADER_STAGE_VERTEX_BIT)) || // This is a legacy pipeline with a VS |
| (pPipeline->IsGraphicsLibrary() && |
| pPipeline->vertex_input_state)) { // This is a graphics library that defines vertex input state |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pStages-02098", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Missing pInputAssemblyState.", pipelineIndex); |
| } |
| } |
| |
| // VK_PRIMITIVE_TOPOLOGY_PATCH_LIST primitive topology is only valid for tessellation pipelines. |
| // Mismatching primitive topology and tessellation fails graphics pipeline creation. |
| if (has_control && has_eval && (!ia_state || ia_state->topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pStages-00736", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: VK_PRIMITIVE_TOPOLOGY_PATCH_LIST must be set as IA topology for " |
| "tessellation pipelines.", |
| pipelineIndex); |
| } |
| if (ia_state) { |
| if (ia_state->topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { |
| if (!has_control || !has_eval) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-topology-00737", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: VK_PRIMITIVE_TOPOLOGY_PATCH_LIST primitive topology is only valid " |
| "for tessellation pipelines.", |
| pipelineIndex); |
| } |
| } |
| |
| if ((ia_state->primitiveRestartEnable == VK_TRUE) && |
| (ia_state->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || ia_state->topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)) { |
| if (IsExtEnabled(device_extensions.vk_ext_primitive_topology_list_restart)) { |
| if (ia_state->topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { |
| if (!enabled_features.primitive_topology_list_restart_features.primitiveTopologyPatchListRestart) { |
| skip |= LogError(device, "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06253", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: topology is %s and primitiveRestartEnable is VK_TRUE and the " |
| "primitiveTopologyPatchListRestart feature is not enabled.", |
| pipelineIndex, string_VkPrimitiveTopology(ia_state->topology)); |
| } |
| } else if (!enabled_features.primitive_topology_list_restart_features.primitiveTopologyListRestart) { |
| skip |= LogError( |
| device, "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06252", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: topology is %s and primitiveRestartEnable is VK_TRUE and the primitiveTopologyListRestart feature " |
| "is not enabled.", |
| pipelineIndex, string_VkPrimitiveTopology(ia_state->topology)); |
| } |
| } else { |
| skip |= LogError(device, "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: topology is %s and primitiveRestartEnable is VK_TRUE. It is invalid.", |
| pipelineIndex, string_VkPrimitiveTopology(ia_state->topology)); |
| } |
| } |
| if ((enabled_features.core.geometryShader == VK_FALSE) && |
| (ia_state->topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY || |
| ia_state->topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY)) { |
| skip |= LogError(device, "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: topology is %s and geometry shaders feature is not enabled. " |
| "It is invalid.", |
| pipelineIndex, string_VkPrimitiveTopology(ia_state->topology)); |
| } |
| if ((enabled_features.core.tessellationShader == VK_FALSE) && |
| (ia_state->topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)) { |
| skip |= LogError(device, "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00430", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: topology is %s and tessellation shaders feature is not " |
| "enabled. It is invalid.", |
| pipelineIndex, string_VkPrimitiveTopology(ia_state->topology)); |
| } |
| } |
| } |
| |
| // Compute shaders should be specified independent of Gfx shaders |
| if (pPipeline->active_shaders & VK_SHADER_STAGE_COMPUTE_BIT) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-stage-00728", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Do not specify Compute Shader for Gfx Pipeline.", |
| pipelineIndex); |
| } |
| |
| // If a rasterization state is provided... |
| const auto raster_state = pPipeline->RasterizationState(); |
| if (raster_state) { |
| if ((raster_state->depthClampEnable == VK_TRUE) && (!enabled_features.core.depthClamp)) { |
| skip |= LogError(device, "VUID-VkPipelineRasterizationStateCreateInfo-depthClampEnable-00782", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: the depthClamp device feature is disabled: the " |
| "depthClampEnable member " |
| "of the VkPipelineRasterizationStateCreateInfo structure must be set to VK_FALSE.", |
| pipelineIndex); |
| } |
| |
| if (!IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_BIAS) && (raster_state->depthBiasClamp != 0.0) && |
| (!enabled_features.core.depthBiasClamp)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00754", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: the depthBiasClamp device feature is disabled: the " |
| "depthBiasClamp member " |
| "of the VkPipelineRasterizationStateCreateInfo structure must be set to 0.0 unless the " |
| "VK_DYNAMIC_STATE_DEPTH_BIAS dynamic state is enabled", |
| pipelineIndex); |
| } |
| |
| // If rasterization is enabled... |
| if (raster_state->rasterizerDiscardEnable == VK_FALSE) { |
| // pMultisampleState can be null for graphics library |
| const bool has_ms_state = |
| !pPipeline->IsGraphicsLibrary() || |
| ((pPipeline->graphics_lib_type & (VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT | |
| VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT)) != 0); |
| if (has_ms_state) { |
| const auto ms_state = pPipeline->MultisampleState(); |
| if (ms_state && (ms_state->alphaToOneEnable == VK_TRUE) && (!enabled_features.core.alphaToOne)) { |
| skip |= LogError(device, "VUID-VkPipelineMultisampleStateCreateInfo-alphaToOneEnable-00785", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: the alphaToOne device feature is disabled: the alphaToOneEnable " |
| "member of the VkPipelineMultisampleStateCreateInfo structure must be set to VK_FALSE.", |
| pipelineIndex); |
| } |
| } |
| |
| // If subpass uses a depth/stencil attachment, pDepthStencilState must be a pointer to a valid structure |
| const bool has_ds_state = |
| (!pPipeline->IsGraphicsLibrary() && pPipeline->HasFullState()) || |
| ((pPipeline->graphics_lib_type & (VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)) != 0); |
| if (has_ds_state) { |
| if (subpass_desc && subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| if (!ds_state) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06043", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: pDepthStencilState is NULL when rasterization is enabled " |
| "and subpass uses a depth/stencil attachment.", |
| pipelineIndex); |
| } else if (ds_state->depthBoundsTestEnable == VK_TRUE) { |
| if (!enabled_features.core.depthBounds) { |
| skip |= LogError( |
| device, "VUID-VkPipelineDepthStencilStateCreateInfo-depthBoundsTestEnable-00598", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: the depthBounds device feature is disabled: the " |
| "depthBoundsTestEnable member of the VkPipelineDepthStencilStateCreateInfo structure must be " |
| "set to VK_FALSE.", |
| pipelineIndex); |
| } |
| |
| // The extension was not created with a feature bit whichs prevents displaying the 2 variations of the VUIDs |
| if (!IsExtEnabled(device_extensions.vk_ext_depth_range_unrestricted) && |
| !IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_BOUNDS)) { |
| const float minDepthBounds = ds_state->minDepthBounds; |
| const float maxDepthBounds = ds_state->maxDepthBounds; |
| // Also VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00755 |
| if (!(minDepthBounds >= 0.0) || !(minDepthBounds <= 1.0)) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-02510", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: VK_EXT_depth_range_unrestricted extension " |
| "is not enabled, VK_DYNAMIC_STATE_DEPTH_BOUNDS is not used, depthBoundsTestEnable is " |
| "true, and pDepthStencilState::minDepthBounds (=%f) is not within the [0.0, 1.0] range.", |
| pipelineIndex, minDepthBounds); |
| } |
| if (!(maxDepthBounds >= 0.0) || !(maxDepthBounds <= 1.0)) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-02510", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: VK_EXT_depth_range_unrestricted extension " |
| "is not enabled, VK_DYNAMIC_STATE_DEPTH_BOUNDS is not used, depthBoundsTestEnable is " |
| "true, and pDepthStencilState::maxDepthBounds (=%f) is not within the [0.0, 1.0] range.", |
| pipelineIndex, maxDepthBounds); |
| } |
| } |
| } |
| } |
| } |
| |
| // If subpass uses color attachments, pColorBlendState must be valid pointer |
| const bool has_color_blend_state = |
| !pPipeline->IsGraphicsLibrary() || |
| ((pPipeline->graphics_lib_type & (VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT)) != 0); |
| if (has_color_blend_state && subpass_desc) { |
| uint32_t color_attachment_count = 0; |
| for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; ++i) { |
| if (subpass_desc->pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) { |
| ++color_attachment_count; |
| } |
| } |
| |
| if (pPipeline->fragment_output_state && (color_attachment_count > 0) && |
| !pPipeline->fragment_output_state->color_blend_state) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06044", |
| "Invalid Pipeline CreateInfo[%" PRIu32 |
| "] State: pColorBlendState is NULL when rasterization is enabled and " |
| "subpass uses color attachments.", |
| pipelineIndex); |
| } |
| |
| constexpr int num_bits = sizeof(subpass_desc->viewMask) * CHAR_BIT; |
| std::bitset<num_bits> view_bits(subpass_desc->viewMask); |
| uint32_t view_bits_count = static_cast<uint32_t>(view_bits.count()); |
| if (view_bits_count > 1) { |
| if (!enabled_features.multiview_features.multiviewTessellationShader && |
| (pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || |
| pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06047", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: subpass has %" PRIu32 |
| " bits set in viewMask and pStages includes tessellation shaders, but the " |
| "VkPhysicalDeviceMultiviewFeatures::multiviewTessellationShader features is not enabled.", |
| pipelineIndex, view_bits_count); |
| } |
| if (!enabled_features.multiview_features.multiviewGeometryShader && |
| pPipeline->active_shaders & VK_SHADER_STAGE_GEOMETRY_BIT) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06048", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: subpass has %" PRIu32 |
| " bits set in viewMask and pStages includes geometry shader, but the " |
| "VkPhysicalDeviceMultiviewFeatures::multiviewGeometryShader features is not enabled.", |
| pipelineIndex, view_bits_count); |
| } |
| } |
| } |
| } |
| |
| auto provoking_vertex_state_ci = |
| lvl_find_in_chain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>(raster_state->pNext); |
| if (provoking_vertex_state_ci && |
| provoking_vertex_state_ci->provokingVertexMode == VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT && |
| !enabled_features.provoking_vertex_features.provokingVertexLast) { |
| skip |= LogError( |
| device, "VUID-VkPipelineRasterizationProvokingVertexStateCreateInfoEXT-provokingVertexMode-04883", |
| "provokingVertexLast feature is not enabled."); |
| } |
| |
| const auto rasterization_state_stream_ci = |
| LvlFindInChain<VkPipelineRasterizationStateStreamCreateInfoEXT>(raster_state->pNext); |
| if (rasterization_state_stream_ci) { |
| if (!enabled_features.transform_feedback_features.geometryStreams) { |
| skip |= LogError(device, "VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-geometryStreams-02324", |
| "pCreateInfos[%" PRIu32 |
| "].pRasterizationState pNext chain includes VkPipelineRasterizationStateStreamCreateInfoEXT, but " |
| "geometryStreams feature is not enabled.", |
| pipelineIndex); |
| } else if (phys_dev_ext_props.transform_feedback_props.transformFeedbackRasterizationStreamSelect == VK_FALSE && |
| rasterization_state_stream_ci->rasterizationStream != 0) { |
| skip |= LogError(device, "VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-rasterizationStream-02326", |
| "VkPhysicalDeviceTransformFeedbackPropertiesEXT::transformFeedbackRasterizationStreamSelect is " |
| "VK_FALSE, but pCreateInfos[%" PRIu32 |
| "].pRasterizationState pNext chain includes VkPipelineRasterizationStateStreamCreateInfoEXT with " |
| "rasterizationStream (%" PRIu32 ") not equal to 0.", |
| pipelineIndex, rasterization_state_stream_ci->rasterizationStream); |
| } else if (rasterization_state_stream_ci->rasterizationStream >= |
| phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams) { |
| skip |= LogError( |
| device, "VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-rasterizationStream-02325", |
| "pCreateInfos[%" PRIu32 |
| "].pRasterizationState pNext chain includes VkPipelineRasterizationStateStreamCreateInfoEXT with " |
| "rasterizationStream (%" PRIu32 |
| ") not less than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackStreams (%" PRIu32 ").", |
| pipelineIndex, rasterization_state_stream_ci->rasterizationStream, |
| phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams); |
| } |
| } |
| |
| const auto rasterization_conservative_state_ci = |
| LvlFindInChain<VkPipelineRasterizationConservativeStateCreateInfoEXT>(raster_state->pNext); |
| if (rasterization_conservative_state_ci) { |
| if (rasterization_conservative_state_ci->extraPrimitiveOverestimationSize < 0.0f || |
| rasterization_conservative_state_ci->extraPrimitiveOverestimationSize > |
| phys_dev_ext_props.conservative_rasterization_props.maxExtraPrimitiveOverestimationSize) { |
| skip |= LogError( |
| device, "VUID-VkPipelineRasterizationConservativeStateCreateInfoEXT-extraPrimitiveOverestimationSize-01769", |
| "pCreateInfos[%" PRIu32 |
| "].pRasterizationState pNext chain includes VkPipelineRasterizationConservativeStateCreateInfoEXT with " |
| "extraPrimitiveOverestimationSize (%f), which is not between 0.0 and " |
| "VkPipelineRasterizationConservativeStateCreateInfoEXT::maxExtraPrimitiveOverestimationSize (%f).", |
| pipelineIndex, rasterization_conservative_state_ci->extraPrimitiveOverestimationSize, |
| phys_dev_ext_props.conservative_rasterization_props.maxExtraPrimitiveOverestimationSize); |
| } |
| } |
| } |
| |
| const auto vi_state = pPipeline->InputState(); |
| if (!vi_state) { |
| if (!pPipeline->IsGraphicsLibrary()) { |
| // This is a "regular" pipeline, so vertex input state is required if a vertex stage is present |
| if ((pPipeline->active_shaders & VK_SHADER_STAGE_VERTEX_BIT) && |
| !IsDynamic(pPipeline, VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pStages-02097", |
| "Invalid Pipeline CreateInfo[%" PRIu32 "] State: Missing pVertexInputState.", pipelineIndex); |
| } |
| } |
| } |
| |
| if (vi_state) { |
| for (uint32_t j = 0; j < vi_state->vertexAttributeDescriptionCount; j++) { |
| VkFormat format = vi_state->pVertexAttributeDescriptions[j].format; |
| // Internal call to get format info. Still goes through layers, could potentially go directly to ICD. |
| VkFormatProperties properties; |
| DispatchGetPhysicalDeviceFormatProperties(physical_device, format, &properties); |
| if ((properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) == 0) { |
| skip |= LogError(device, "VUID-VkVertexInputAttributeDescription-format-00623", |
| "vkCreateGraphicsPipelines: pCreateInfo[%" PRIu32 |
| "].pVertexInputState->vertexAttributeDescriptions[%d].format " |
| "(%s) is not a supported vertex buffer format.", |
| pipelineIndex, j, string_VkFormat(format)); |
| } |
| } |
| } |
| |
| const auto *multisample_state = pPipeline->MultisampleState(); |
| if (subpass_desc && multisample_state) { |
| auto accum_color_samples = [subpass_desc, &rp_state](uint32_t &samples) { |
| for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; i++) { |
| const auto attachment = subpass_desc->pColorAttachments[i].attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| samples |= static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| } |
| } |
| }; |
| |
| if (!(IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples) || |
| IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples))) { |
| uint32_t raster_samples = static_cast<uint32_t>(GetNumSamples(pPipeline)); |
| uint32_t subpass_num_samples = 0; |
| |
| accum_color_samples(subpass_num_samples); |
| |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const auto attachment = subpass_desc->pDepthStencilAttachment->attachment; |
| subpass_num_samples |= static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| } |
| |
| // subpass_num_samples is 0 when the subpass has no attachments or if all attachments are VK_ATTACHMENT_UNUSED. |
| // Only validate the value of subpass_num_samples if the subpass has attachments that are not VK_ATTACHMENT_UNUSED. |
| if (subpass_num_samples && (!IsPowerOfTwo(subpass_num_samples) || (subpass_num_samples != raster_samples))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-subpass-00757", |
| "vkCreateGraphicsPipelines: pCreateInfo[%" PRIu32 |
| "].pMultisampleState->rasterizationSamples (%u) " |
| "does not match the number of samples of the RenderPass color and/or depth attachment.", |
| pipelineIndex, raster_samples); |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples)) { |
| VkSampleCountFlagBits max_sample_count = static_cast<VkSampleCountFlagBits>(0); |
| for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; ++i) { |
| if (subpass_desc->pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) { |
| max_sample_count = std::max( |
| max_sample_count, rp_state->createInfo.pAttachments[subpass_desc->pColorAttachments[i].attachment].samples); |
| } |
| } |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| max_sample_count = std::max( |
| max_sample_count, rp_state->createInfo.pAttachments[subpass_desc->pDepthStencilAttachment->attachment].samples); |
| } |
| if ((raster_state->rasterizerDiscardEnable == VK_FALSE) && |
| (max_sample_count != static_cast<VkSampleCountFlagBits>(0)) && |
| (multisample_state->rasterizationSamples != max_sample_count)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-subpass-01505", |
| "vkCreateGraphicsPipelines: pCreateInfo[%" PRIu32 |
| "].pMultisampleState->rasterizationSamples (%s) != max " |
| "attachment samples (%s) used in subpass %u.", |
| pipelineIndex, string_VkSampleCountFlagBits(multisample_state->rasterizationSamples), |
| string_VkSampleCountFlagBits(max_sample_count), subpass); |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples)) { |
| uint32_t raster_samples = static_cast<uint32_t>(GetNumSamples(pPipeline)); |
| uint32_t subpass_color_samples = 0; |
| |
| accum_color_samples(subpass_color_samples); |
| |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const auto attachment = subpass_desc->pDepthStencilAttachment->attachment; |
| const uint32_t subpass_depth_samples = static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| |
| if (ds_state) { |
| const bool ds_test_enabled = (ds_state->depthTestEnable == VK_TRUE) || |
| (ds_state->depthBoundsTestEnable == VK_TRUE) || |
| (ds_state->stencilTestEnable == VK_TRUE); |
| |
| if (ds_test_enabled && (!IsPowerOfTwo(subpass_depth_samples) || (raster_samples != subpass_depth_samples))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-subpass-01411", |
| "vkCreateGraphicsPipelines: pCreateInfo[%" PRIu32 |
| "].pMultisampleState->rasterizationSamples (%u) " |
| "does not match the number of samples of the RenderPass depth attachment (%u).", |
| pipelineIndex, raster_samples, subpass_depth_samples); |
| } |
| } |
| } |
| |
| if (IsPowerOfTwo(subpass_color_samples)) { |
| if (raster_samples < subpass_color_samples) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-subpass-01412", |
| "vkCreateGraphicsPipelines: pCreateInfo[%" PRIu32 |
| "].pMultisampleState->rasterizationSamples (%u) " |
| "is not greater or equal to the number of samples of the RenderPass color attachment (%u).", |
| pipelineIndex, raster_samples, subpass_color_samples); |
| } |
| |
| if (multisample_state) { |
| if ((raster_samples > subpass_color_samples) && (multisample_state->sampleShadingEnable == VK_TRUE)) { |
| skip |= LogError(device, "VUID-VkPipelineMultisampleStateCreateInfo-rasterizationSamples-01415", |
| "vkCreateGraphicsPipelines: pCreateInfo[%" PRIu32 |
| "].pMultisampleState->sampleShadingEnable must be " |
| "VK_FALSE when " |
| "pCreateInfo[%" PRIu32 |
| "].pMultisampleState->rasterizationSamples (%u) is greater than the number of " |
| "samples of the " |
| "subpass color attachment (%u).", |
| pipelineIndex, pipelineIndex, raster_samples, subpass_color_samples); |
| } |
| |
| const auto *coverage_modulation_state = |
| LvlFindInChain<VkPipelineCoverageModulationStateCreateInfoNV>(multisample_state->pNext); |
| |
| if (coverage_modulation_state && (coverage_modulation_state->coverageModulationTableEnable == VK_TRUE)) { |
| if (coverage_modulation_state->coverageModulationTableCount != (raster_samples / subpass_color_samples)) { |
| skip |= LogError( |
| device, "VUID-VkPipelineCoverageModulationStateCreateInfoNV-coverageModulationTableEnable-01405", |
| "vkCreateGraphicsPipelines: pCreateInfos[%" PRIu32 |
| "] VkPipelineCoverageModulationStateCreateInfoNV " |
| "coverageModulationTableCount of %u is invalid.", |
| pipelineIndex, coverage_modulation_state->coverageModulationTableCount); |
| } |
| } |
| } |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_nv_coverage_reduction_mode)) { |
| uint32_t raster_samples = static_cast<uint32_t>(GetNumSamples(pPipeline)); |
| uint32_t subpass_color_samples = 0; |
| uint32_t subpass_depth_samples = 0; |
| |
| accum_color_samples(subpass_color_samples); |
| |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const auto attachment = subpass_desc->pDepthStencilAttachment->attachment; |
| subpass_depth_samples = static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| } |
| |
| if (multisample_state && IsPowerOfTwo(subpass_color_samples) && |
| (subpass_depth_samples == 0 || IsPowerOfTwo(subpass_depth_samples))) { |
| const auto *coverage_reduction_state = |
| LvlFindInChain<VkPipelineCoverageReductionStateCreateInfoNV>(multisample_state->pNext); |
| |
| if (coverage_reduction_state) { |
| const VkCoverageReductionModeNV coverage_reduction_mode = coverage_reduction_state->coverageReductionMode; |
| uint32_t combination_count = 0; |
| std::vector<VkFramebufferMixedSamplesCombinationNV> combinations; |
| DispatchGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV(physical_device, &combination_count, |
| nullptr); |
| combinations.resize(combination_count); |
| DispatchGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV(physical_device, &combination_count, |
| &combinations[0]); |
| |
| bool combination_found = false; |
| for (const auto &combination : combinations) { |
| if (coverage_reduction_mode == combination.coverageReductionMode && |
| raster_samples == combination.rasterizationSamples && |
| subpass_depth_samples == combination.depthStencilSamples && |
| subpass_color_samples == combination.colorSamples) { |
| combination_found = true; |
| break; |
| } |
| } |
| |
| if (!combination_found) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-coverageReductionMode-02722", |
| "vkCreateGraphicsPipelines: pCreateInfos[%" PRIu32 |
| "] the specified combination of coverage " |
| "reduction mode (%s), pMultisampleState->rasterizationSamples (%u), sample counts for " |
| "the subpass color and depth/stencil attachments is not a valid combination returned by " |
| "vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV.", |
| pipelineIndex, string_VkCoverageReductionModeNV(coverage_reduction_mode), raster_samples); |
| } |
| } |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_nv_fragment_coverage_to_color)) { |
| const auto coverage_to_color_state = LvlFindInChain<VkPipelineCoverageToColorStateCreateInfoNV>(multisample_state); |
| |
| if (coverage_to_color_state && coverage_to_color_state->coverageToColorEnable == VK_TRUE) { |
| bool attachment_is_valid = false; |
| std::string error_detail; |
| |
| if (coverage_to_color_state->coverageToColorLocation < subpass_desc->colorAttachmentCount) { |
| const auto& color_attachment_ref = |
| subpass_desc->pColorAttachments[coverage_to_color_state->coverageToColorLocation]; |
| if (color_attachment_ref.attachment != VK_ATTACHMENT_UNUSED) { |
| const auto &color_attachment = rp_state->createInfo.pAttachments[color_attachment_ref.attachment]; |
| |
| switch (color_attachment.format) { |
| case VK_FORMAT_R8_UINT: |
| case VK_FORMAT_R8_SINT: |
| case VK_FORMAT_R16_UINT: |
| case VK_FORMAT_R16_SINT: |
| case VK_FORMAT_R32_UINT: |
| case VK_FORMAT_R32_SINT: |
| attachment_is_valid = true; |
| break; |
| default: |
| std::ostringstream str; |
| str << "references an attachment with an invalid format (" |
| << string_VkFormat(color_attachment.format) << ")."; |
| error_detail = str.str(); |
| break; |
| } |
| } else { |
| std::ostringstream str; |
| str << "references an invalid attachment. The subpass pColorAttachments[" |
| << coverage_to_color_state->coverageToColorLocation |
| << "].attachment has the value VK_ATTACHMENT_UNUSED."; |
| error_detail = str.str(); |
| } |
| } else { |
| std::ostringstream str; |
| str << "references an non-existing attachment since the subpass colorAttachmentCount is " |
| << subpass_desc->colorAttachmentCount << "."; |
| error_detail = str.str(); |
| } |
| |
| if (!attachment_is_valid) { |
| skip |= LogError(device, "VUID-VkPipelineCoverageToColorStateCreateInfoNV-coverageToColorEnable-01404", |
| "vkCreateGraphicsPipelines: pCreateInfos[%" PRId32 |
| "].pMultisampleState VkPipelineCoverageToColorStateCreateInfoNV " |
| "coverageToColorLocation = %" PRIu32 " %s", |
| pipelineIndex, coverage_to_color_state->coverageToColorLocation, error_detail.c_str()); |
| } |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_ext_sample_locations)) { |
| const VkPipelineSampleLocationsStateCreateInfoEXT *sample_location_state = |
| LvlFindInChain<VkPipelineSampleLocationsStateCreateInfoEXT>(multisample_state->pNext); |
| |
| if (sample_location_state != nullptr) { |
| if ((sample_location_state->sampleLocationsEnable == VK_TRUE) && |
| (IsDynamic(pPipeline, VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT) == false)) { |
| const VkSampleLocationsInfoEXT sample_location_info = sample_location_state->sampleLocationsInfo; |
| skip |= ValidateSampleLocationsInfo(&sample_location_info, "vkCreateGraphicsPipelines"); |
| const VkExtent2D grid_size = sample_location_info.sampleLocationGridSize; |
| |
| auto multisample_prop = LvlInitStruct<VkMultisamplePropertiesEXT>(); |
| DispatchGetPhysicalDeviceMultisamplePropertiesEXT(physical_device, multisample_state->rasterizationSamples, |
| &multisample_prop); |
| const VkExtent2D max_grid_size = multisample_prop.maxSampleLocationGridSize; |
| |
| // Note order or "divide" in "sampleLocationsInfo must evenly divide VkMultisamplePropertiesEXT" |
| if (SafeModulo(max_grid_size.width, grid_size.width) != 0) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-01521", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: Because there is no dynamic state for Sample Location " |
| "and sampleLocationEnable is true, the " |
| "VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsInfo::sampleLocationGridSize.width (%u) " |
| "must be evenly divided by VkMultisamplePropertiesEXT::sampleLocationGridSize.width (%u).", |
| pipelineIndex, grid_size.width, max_grid_size.width); |
| } |
| if (SafeModulo(max_grid_size.height, grid_size.height) != 0) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-01522", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: Because there is no dynamic state for Sample Location " |
| "and sampleLocationEnable is true, the " |
| "VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsInfo::sampleLocationGridSize.height (%u) " |
| "must be evenly divided by VkMultisamplePropertiesEXT::sampleLocationGridSize.height (%u).", |
| pipelineIndex, grid_size.height, max_grid_size.height); |
| } |
| if (sample_location_info.sampleLocationsPerPixel != multisample_state->rasterizationSamples) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-01523", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: Because there is no dynamic state for Sample Location " |
| "and sampleLocationEnable is true, the " |
| "VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsInfo::sampleLocationsPerPixel (%s) must " |
| "be the same as the VkPipelineMultisampleStateCreateInfo::rasterizationSamples (%s).", |
| pipelineIndex, string_VkSampleCountFlagBits(sample_location_info.sampleLocationsPerPixel), |
| string_VkSampleCountFlagBits(multisample_state->rasterizationSamples)); |
| } |
| } |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_qcom_render_pass_shader_resolve)) { |
| uint32_t raster_samples = static_cast<uint32_t>(GetNumSamples(pPipeline)); |
| uint32_t subpass_input_attachment_samples = 0; |
| |
| for (uint32_t i = 0; i < subpass_desc->inputAttachmentCount; i++) { |
| const auto attachment = subpass_desc->pInputAttachments[i].attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| subpass_input_attachment_samples |= |
| static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| } |
| } |
| |
| if ((subpass_desc->flags & VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM) != 0) { |
| if (raster_samples != subpass_input_attachment_samples) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-rasterizationSamples-04899", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: The subpass includes " |
| "VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM " |
| "but the input attachment VkSampleCountFlagBits (%u) does not match the " |
| "VkPipelineMultisampleStateCreateInfo::rasterizationSamples (%u) VkSampleCountFlagBits.", |
| pipelineIndex, subpass_input_attachment_samples, multisample_state->rasterizationSamples); |
| } |
| if (multisample_state->sampleShadingEnable == VK_TRUE) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-sampleShadingEnable-04900", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 |
| "]: The subpass includes " |
| "VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM " |
| "which requires sample shading is disabled, but " |
| "VkPipelineMultisampleStateCreateInfo::sampleShadingEnable is true. ", |
| pipelineIndex); |
| } |
| } |
| } |
| } |
| |
| skip |= ValidatePipelineCacheControlFlags(pPipeline->GetPipelineCreateFlags(), pipelineIndex, "vkCreateGraphicsPipelines", |
| "VUID-VkGraphicsPipelineCreateInfo-pipelineCreationCacheControl-02878"); |
| |
| // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03378 |
| if (api_version < VK_API_VERSION_1_3 && !enabled_features.extended_dynamic_state_features.extendedDynamicState && |
| (IsDynamic(pPipeline, VK_DYNAMIC_STATE_CULL_MODE_EXT) || IsDynamic(pPipeline, VK_DYNAMIC_STATE_FRONT_FACE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT) || IsDynamic(pPipeline, VK_DYNAMIC_STATE_STENCIL_OP_EXT))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03378", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Extended dynamic state used by the extendedDynamicState " |
| "feature is not enabled", |
| pipelineIndex); |
| } |
| |
| // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04868 |
| if (api_version < VK_API_VERSION_1_3 && !enabled_features.extended_dynamic_state2_features.extendedDynamicState2 && |
| (IsDynamic(pPipeline, VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT) || |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04868", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Extended dynamic state used by the extendedDynamicState2 " |
| "feature is not enabled", |
| pipelineIndex); |
| } |
| |
| // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04869 |
| if (!enabled_features.extended_dynamic_state2_features.extendedDynamicState2LogicOp && |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_LOGIC_OP_EXT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04869", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Extended dynamic state used by the " |
| "extendedDynamicState2LogicOp feature is not enabled", |
| pipelineIndex); |
| } |
| |
| // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04870 |
| if (!enabled_features.extended_dynamic_state2_features.extendedDynamicState2PatchControlPoints && |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04870", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Extended dynamic state used by the " |
| "extendedDynamicState2PatchControlPoints " |
| "feature is not enabled", |
| pipelineIndex); |
| } |
| |
| const VkPipelineFragmentShadingRateStateCreateInfoKHR *fragment_shading_rate_state = |
| LvlFindInChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pPipeline->PNext()); |
| |
| if (fragment_shading_rate_state && !IsDynamic(pPipeline, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR)) { |
| const char *struct_name = "VkPipelineFragmentShadingRateStateCreateInfoKHR"; |
| |
| if (fragment_shading_rate_state->fragmentSize.width == 0) { |
| skip |= |
| LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 "]: Fragment width of %u has been specified in %s.", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.width, struct_name); |
| } |
| |
| if (fragment_shading_rate_state->fragmentSize.height == 0) { |
| skip |= |
| LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 "]: Fragment height of %u has been specified in %s.", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.height, struct_name); |
| } |
| |
| if (fragment_shading_rate_state->fragmentSize.width != 0 && |
| !IsPowerOfTwo(fragment_shading_rate_state->fragmentSize.width)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Non-power-of-two fragment width of %u has been specified in %s.", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.width, struct_name); |
| } |
| |
| if (fragment_shading_rate_state->fragmentSize.height != 0 && |
| !IsPowerOfTwo(fragment_shading_rate_state->fragmentSize.height)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Non-power-of-two fragment height of %u has been specified in %s.", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.height, struct_name); |
| } |
| |
| if (fragment_shading_rate_state->fragmentSize.width > 4) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Fragment width of %u specified in %s is too large.", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.width, struct_name); |
| } |
| |
| if (fragment_shading_rate_state->fragmentSize.height > 4) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Fragment height of %u specified in %s is too large", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.height, struct_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.pipelineFragmentShadingRate && |
| fragment_shading_rate_state->fragmentSize.width != 1) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Pipeline fragment width of %u has been specified in %s, but " |
| "pipelineFragmentShadingRate is not enabled", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.width, struct_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.pipelineFragmentShadingRate && |
| fragment_shading_rate_state->fragmentSize.height != 1) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Pipeline fragment height of %u has been specified in %s, but " |
| "pipelineFragmentShadingRate is not enabled", |
| pipelineIndex, fragment_shading_rate_state->fragmentSize.height, struct_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.primitiveFragmentShadingRate && |
| fragment_shading_rate_state->combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: First combiner operation of %s has been specified in %s, but " |
| "primitiveFragmentShadingRate is not enabled", |
| pipelineIndex, string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state->combinerOps[0]), |
| struct_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate && |
| fragment_shading_rate_state->combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Second combiner operation of %s has been specified in %s, but " |
| "attachmentFragmentShadingRate is not enabled", |
| pipelineIndex, string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state->combinerOps[1]), |
| struct_name); |
| } |
| |
| if (!phys_dev_ext_props.fragment_shading_rate_props.fragmentShadingRateNonTrivialCombinerOps && |
| (fragment_shading_rate_state->combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR && |
| fragment_shading_rate_state->combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: First combiner operation of %s has been specified in %s, but " |
| "fragmentShadingRateNonTrivialCombinerOps is not supported", |
| pipelineIndex, string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state->combinerOps[0]), |
| struct_name); |
| } |
| |
| if (!phys_dev_ext_props.fragment_shading_rate_props.fragmentShadingRateNonTrivialCombinerOps && |
| (fragment_shading_rate_state->combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR && |
| fragment_shading_rate_state->combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Second combiner operation of %s has been specified in %s, but " |
| "fragmentShadingRateNonTrivialCombinerOps is not supported", |
| pipelineIndex, string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state->combinerOps[1]), |
| struct_name); |
| } |
| |
| const auto combiner_ops = fragment_shading_rate_state->combinerOps; |
| if (pPipeline->pre_raster_state || pPipeline->fragment_shader_state) { |
| // TODO automate what a "valid combiner op" is |
| const auto combiner_op0_valid = (combiner_ops[0] >= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) && |
| (combiner_ops[0] <= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR); |
| const auto combiner_op1_valid = (combiner_ops[1] >= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) && |
| (combiner_ops[1] <= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR); |
| if (!combiner_op0_valid) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567", |
| "vkCreateGraphicsPipelines(): in pCreateInfos[%" PRIu32 |
| "], combinerOp[0] (%s) is not a valid VkFragmentShadingRateCombinerOpKHR value.", |
| pipelineIndex, string_VkFragmentShadingRateCombinerOpKHR(combiner_ops[0])); |
| } |
| if (!combiner_op1_valid) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568", |
| "vkCreateGraphicsPipelines(): in pCreateInfos[%" PRIu32 |
| "], combinerOp[1] (%s) is not a valid VkFragmentShadingRateCombinerOpKHR value.", |
| pipelineIndex, string_VkFragmentShadingRateCombinerOpKHR(combiner_ops[1])); |
| } |
| } |
| } |
| |
| const auto *discard_rectangle_state = LvlFindInChain<VkPipelineDiscardRectangleStateCreateInfoEXT>(pPipeline->PNext()); |
| if (discard_rectangle_state) { |
| if (discard_rectangle_state->discardRectangleCount > phys_dev_ext_props.discard_rectangle_props.maxDiscardRectangles) { |
| skip |= LogError( |
| device, "VUID-VkPipelineDiscardRectangleStateCreateInfoEXT-discardRectangleCount-00582", |
| "vkCreateGraphicsPipelines(): VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleCount (%" PRIu32 |
| ") in pNext chain of pCreateInfo[%" PRIu32 |
| "] is not less than VkPhysicalDeviceDiscardRectanglePropertiesEXT::maxDiscardRectangles (%" PRIu32 ".", |
| discard_rectangle_state->discardRectangleCount, pipelineIndex, |
| phys_dev_ext_props.discard_rectangle_props.maxDiscardRectangles); |
| } |
| } |
| |
| // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04807 |
| if (!enabled_features.vertex_input_dynamic_state_features.vertexInputDynamicState && |
| IsDynamic(pPipeline, VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04807", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: The vertexInputDynamicState feature must be enabled to use " |
| "the VK_DYNAMIC_STATE_VERTEX_INPUT_EXT dynamic state", |
| pipelineIndex); |
| } |
| |
| if (!enabled_features.color_write_features.colorWriteEnable && IsDynamic(pPipeline, VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04800", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: The colorWriteEnable feature must be enabled to use the " |
| "VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT dynamic state", |
| pipelineIndex); |
| } |
| |
| const auto rendering_struct = LvlFindInChain<VkPipelineRenderingCreateInfo>(pPipeline->PNext()); |
| if (rendering_struct) { |
| const bool has_rasterization = |
| pPipeline->RasterizationState() && (pPipeline->RasterizationState()->rasterizerDiscardEnable == VK_FALSE); |
| if (has_rasterization && |
| ((rendering_struct->depthAttachmentFormat != VK_FORMAT_UNDEFINED) || |
| (rendering_struct->stencilAttachmentFormat != VK_FORMAT_UNDEFINED)) && |
| !pPipeline->DepthStencilState()) { |
| skip |= LogError( |
| device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06053", |
| "vkCreateGraphicsPipelines(): Pipeline %" PRIu32 |
| " has fragment state and a depth format (%s) or stencil format (%s) and an invalid pDepthStencilState structure", |
| pipelineIndex, string_VkFormat(rendering_struct->depthAttachmentFormat), |
| string_VkFormat(rendering_struct->stencilAttachmentFormat)); |
| } |
| |
| if (has_rasterization && (rendering_struct->colorAttachmentCount != 0) && !pPipeline->ColorBlendState()) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06054", |
| "vkCreateGraphicsPipelines(): Pipeline %" PRIu32 |
| " has VkPipelineRenderingCreateInfoKHR::colorAttachmentCount (%" PRIu32 |
| ") and an invalid pColorBlendState structure", |
| pipelineIndex, rendering_struct->colorAttachmentCount); |
| } |
| |
| if ((rendering_struct->viewMask != 0) && !enabled_features.multiview_features.multiviewTessellationShader && |
| (pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || |
| pPipeline->active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06057", |
| "vkCreateGraphicsPipelines(): Pipeline %" PRIu32 " has VkPipelineRenderingCreateInfoKHR->viewMask(%" PRIu32 |
| ") and " |
| "multiviewTessellationShader is not enabled, contains tesselation shaders", |
| pipelineIndex, rendering_struct->viewMask); |
| } |
| |
| if ((rendering_struct->viewMask != 0) && !enabled_features.multiview_features.multiviewGeometryShader && |
| (pPipeline->active_shaders & VK_SHADER_STAGE_GEOMETRY_BIT)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06058", |
| "vkCreateGraphicsPipelines(): Pipeline %" PRIu32 " has VkPipelineRenderingCreateInfoKHR->viewMask(%" PRIu32 |
| ") and " |
| "multiviewGeometryShader is not enabled, contains geometry shader", |
| pipelineIndex, rendering_struct->viewMask); |
| } |
| |
| if (color_blend_state && (rendering_struct->colorAttachmentCount != color_blend_state->attachmentCount)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06060", |
| "vkCreateGraphicsPipelines() Pipeline %" PRIu32 " interface pColorBlendState->attachmentCount %" PRIu32 |
| " and " |
| "VkPipelineRenderingCreateInfoKHR->colorAttachmentCount %" PRIu32 " must be equal", |
| pipelineIndex, rendering_struct->colorAttachmentCount, color_blend_state->attachmentCount); |
| } |
| |
| for (uint32_t color_index = 0; color_index < rendering_struct->colorAttachmentCount; color_index++) { |
| const VkFormat color_format = rendering_struct->pColorAttachmentFormats[color_index]; |
| if (color_format != VK_FORMAT_UNDEFINED) { |
| VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(color_format); |
| if (((format_features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT) == 0) && |
| (color_blend_state && (color_index < color_blend_state->attachmentCount) && |
| (color_blend_state->pAttachments[color_index].blendEnable != VK_FALSE))) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06062", |
| "vkCreateGraphicsPipelines(): vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: pColorBlendState->blendEnable must be false ", |
| pipelineIndex); |
| } |
| |
| if (!IsExtEnabled(device_extensions.vk_nv_linear_color_attachment)) { |
| if ((format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT) == 0) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06581", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: color_format (%s) must be a format with potential format features that include " |
| "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", |
| pipelineIndex, string_VkFormat(color_format)); |
| } |
| } else { |
| if ((format_features & |
| (VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV)) == 0) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06582", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: color_format (%s) must be a format with potential format features that include " |
| "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", |
| pipelineIndex, string_VkFormat(color_format)); |
| } |
| } |
| } |
| } |
| |
| if (rendering_struct->depthAttachmentFormat != VK_FORMAT_UNDEFINED) { |
| VkFormatFeatureFlags2 format_features = GetPotentialFormatFeatures(rendering_struct->depthAttachmentFormat); |
| if ((format_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06585", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: depthAttachmentFormat (%s) must be a format with potential format features that include " |
| "VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", |
| pipelineIndex, string_VkFormat(rendering_struct->depthAttachmentFormat)); |
| } |
| } |
| |
| if (rendering_struct->stencilAttachmentFormat != VK_FORMAT_UNDEFINED) { |
| VkFormatFeatureFlags2 format_features = GetPotentialFormatFeatures(rendering_struct->stencilAttachmentFormat); |
| if ((format_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06586", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: stencilAttachmentFormat (%s) must be a format with potential format features that include " |
| "VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", |
| pipelineIndex, string_VkFormat(rendering_struct->stencilAttachmentFormat)); |
| } |
| } |
| |
| if ((rendering_struct->depthAttachmentFormat != VK_FORMAT_UNDEFINED) && |
| (rendering_struct->stencilAttachmentFormat != VK_FORMAT_UNDEFINED) && |
| (rendering_struct->depthAttachmentFormat != rendering_struct->stencilAttachmentFormat)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06589", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: depthAttachmentFormat is not VK_FORMAT_UNDEFINED and stencilAttachmentFormat is not " |
| "VK_FORMAT_UNDEFINED, but depthAttachmentFormat (%s) does not equal stencilAttachmentFormat (%s)", |
| pipelineIndex, string_VkFormat(rendering_struct->depthAttachmentFormat), |
| string_VkFormat(rendering_struct->stencilAttachmentFormat)); |
| } |
| |
| if (rp_state_required) { |
| if ((enabled_features.core11.multiview == VK_FALSE) && (rendering_struct->viewMask != 0)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-multiview-06577", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: multiview is not enabled but viewMask is (%u).", |
| pipelineIndex, rendering_struct->viewMask); |
| } |
| |
| if (MostSignificantBit(rendering_struct->viewMask) >= |
| static_cast<int32_t>(phys_dev_props_core11.maxMultiviewViewCount)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06578", |
| "vkCreateGraphicsPipelines() pCreateInfos[%" PRIu32 |
| "]: Most significant bit in " |
| "VkPipelineRenderingCreateInfo->viewMask(%u) must be less maxMultiviewViewCount(%u)", |
| pipelineIndex, rendering_struct->viewMask, phys_dev_props_core11.maxMultiviewViewCount); |
| } |
| } |
| } |
| |
| // If VK_EXT_graphics_pipeline_library is not enabled, a complete set of state must be defined at this point |
| if (!IsExtEnabled(device_extensions.vk_ext_graphics_pipeline_library)) { |
| std::string msg; |
| if (!pPipeline->vertex_input_state) { |
| msg += "<vertex input> "; |
| } |
| if (!pPipeline->pre_raster_state) { |
| msg += "<pre-raster> "; |
| } |
| if (!pPipeline->fragment_shader_state) { |
| msg += "<fragment shader> "; |
| } |
| if (!pPipeline->fragment_output_state) { |
| msg += "<fragment output> "; |
| } |
| |
| if (!msg.empty()) { |
| skip |= |
| LogError(device, "VUID-VkGraphicsPipelineCreateInfo-None-06573", |
| "pCreateInfos[%" PRIu32 |
| "] does not contain a complete set of state and %s is not enabled. The following state is missing: [ %s].", |
| pipelineIndex, VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME, msg.c_str()); |
| } |
| } else { |
| enum GPLInitInfo : uint8_t { |
| uninitialized = 0, |
| from_gpl_info, |
| from_link_info, |
| }; |
| std::pair<VkPipelineLayoutCreateFlags, GPLInitInfo> pre_raster_flags = |
| std::make_pair(VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM, |
| GPLInitInfo::uninitialized), |
| fs_flags = std::make_pair(VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM, |
| GPLInitInfo::uninitialized); |
| const auto gpl_info = LvlFindInChain<VkGraphicsPipelineLibraryCreateInfoEXT>(pPipeline->PNext()); |
| if (gpl_info) { |
| if ((gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) && |
| !(gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)) { |
| pre_raster_flags.first = pPipeline->pre_raster_state->pipeline_layout->CreateFlags(); |
| pre_raster_flags.second = GPLInitInfo::from_gpl_info; |
| } else if ((gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) && |
| !(gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT)) { |
| fs_flags.first = pPipeline->fragment_shader_state->pipeline_layout->CreateFlags(); |
| fs_flags.second = GPLInitInfo::from_gpl_info; |
| } |
| } |
| |
| const auto link_info = LvlFindInChain<VkPipelineLibraryCreateInfoKHR>(pPipeline->PNext()); |
| if (link_info) { |
| for (decltype(link_info->libraryCount) i = 0; i < link_info->libraryCount; ++i) { |
| const auto lib = Get<PIPELINE_STATE>(link_info->pLibraries[i]); |
| if (lib) { |
| if (lib->graphics_lib_type == VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) { |
| pre_raster_flags.first = lib->PipelineLayoutState()->CreateFlags(); |
| pre_raster_flags.second = GPLInitInfo::from_link_info; |
| } else if (lib->graphics_lib_type == VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) { |
| fs_flags.first = lib->PipelineLayoutState()->CreateFlags(); |
| fs_flags.second = GPLInitInfo::from_link_info; |
| } |
| } |
| } |
| } |
| |
| if ((pre_raster_flags.second != GPLInitInfo::uninitialized) && (fs_flags.second != GPLInitInfo::uninitialized)) { |
| const char *vuid = nullptr; |
| if (pre_raster_flags.second != fs_flags.second) { |
| vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06614"; |
| } else if ((pre_raster_flags.second == GPLInitInfo::from_link_info) && |
| (fs_flags.second == GPLInitInfo::from_link_info)) { |
| vuid = "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06615"; |
| } |
| |
| if (vuid) { |
| const auto pre_raster_indset = (pre_raster_flags.first & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| const auto fs_indset = (fs_flags.first & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| if (pre_raster_indset ^ fs_indset) { |
| const char *pre_raster_str = (pre_raster_indset != 0) ? "defined with" : "not defined with"; |
| const char *fs_str = (fs_indset != 0) ? "defined with" : "not defined with"; |
| skip |= LogError( |
| device, vuid, |
| "vkCreateGraphicsPipelines(): pCreateInfos[%" PRIu32 |
| "] is attempting to create a graphics pipeline library with pre-raster and fragment shader state. However " |
| "the " |
| "pre-raster layout create flags (%s) are %s VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT, and the " |
| "fragment shader layout create flags (%s) are %s VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT", |
| pipelineIndex, string_VkPipelineLayoutCreateFlags(pre_raster_flags.first).c_str(), pre_raster_str, |
| string_VkPipelineLayoutCreateFlags(fs_flags.first).c_str(), fs_str); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| // Block of code at start here specifically for managing/tracking DSs |
| |
| // Validate that given set is valid and that it's not being used by an in-flight CmdBuffer |
| // func_str is the name of the calling function |
| // Return false if no errors occur |
| // Return true if validation error occurs and callback returns true (to skip upcoming API call down the chain) |
| bool CoreChecks::ValidateIdleDescriptorSet(VkDescriptorSet set, const char *func_str) const { |
| if (disabled[object_in_use]) return false; |
| bool skip = false; |
| auto set_node = Get<cvdescriptorset::DescriptorSet>(set); |
| if (set_node) { |
| // TODO : This covers various error cases so should pass error enum into this function and use passed in enum here |
| if (set_node->InUse()) { |
| skip |= LogError(set, "VUID-vkFreeDescriptorSets-pDescriptorSets-00309", |
| "Cannot call %s() on %s that is in use by a command buffer.", func_str, |
| report_data->FormatHandle(set).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| // If a renderpass is active, verify that the given command type is appropriate for current subpass state |
| bool CoreChecks::ValidateCmdSubpassState(const CMD_BUFFER_STATE *pCB, const CMD_TYPE cmd_type) const { |
| if (!pCB->activeRenderPass) return false; |
| bool skip = false; |
| if (pCB->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY && |
| pCB->activeSubpassContents == VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS && |
| (cmd_type != CMD_EXECUTECOMMANDS && cmd_type != CMD_NEXTSUBPASS && cmd_type != CMD_ENDRENDERPASS && |
| cmd_type != CMD_NEXTSUBPASS2 && cmd_type != CMD_NEXTSUBPASS2KHR && cmd_type != CMD_ENDRENDERPASS2 && |
| cmd_type != CMD_ENDRENDERPASS2KHR)) { |
| skip |= LogError(pCB->commandBuffer(), kVUID_Core_DrawState_InvalidCommandBuffer, |
| "%s() cannot be called in a subpass using secondary command buffers.", |
| kGeneratedCommandNameList[cmd_type]); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdQueueFlags(const CMD_BUFFER_STATE *cb_node, const char *caller_name, VkQueueFlags required_flags, |
| const char *error_code) const { |
| auto pool = cb_node->command_pool; |
| if (pool) { |
| const uint32_t queue_family_index = pool->queueFamilyIndex; |
| const VkQueueFlags queue_flags = physical_device_state->queue_family_properties[queue_family_index].queueFlags; |
| if (!(required_flags & queue_flags)) { |
| string required_flags_string; |
| for (auto flag : {VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT, VK_QUEUE_COMPUTE_BIT, VK_QUEUE_SPARSE_BINDING_BIT, |
| VK_QUEUE_PROTECTED_BIT}) { |
| if (flag & required_flags) { |
| if (required_flags_string.size()) { |
| required_flags_string += " or "; |
| } |
| required_flags_string += string_VkQueueFlagBits(flag); |
| } |
| } |
| return LogError(cb_node->commandBuffer(), error_code, |
| "%s(): Called in command buffer %s which was allocated from the command pool %s which was created with " |
| "queueFamilyIndex %u which doesn't contain the required %s capability flags.", |
| caller_name, report_data->FormatHandle(cb_node->commandBuffer()).c_str(), |
| report_data->FormatHandle(pool->commandPool()).c_str(), queue_family_index, |
| required_flags_string.c_str()); |
| } |
| } |
| return false; |
| } |
| |
| bool CoreChecks::ValidateSampleLocationsInfo(const VkSampleLocationsInfoEXT *pSampleLocationsInfo, const char *apiName) const { |
| bool skip = false; |
| const VkSampleCountFlagBits sample_count = pSampleLocationsInfo->sampleLocationsPerPixel; |
| const uint32_t sample_total_size = pSampleLocationsInfo->sampleLocationGridSize.width * |
| pSampleLocationsInfo->sampleLocationGridSize.height * SampleCountSize(sample_count); |
| if (pSampleLocationsInfo->sampleLocationsCount != sample_total_size) { |
| skip |= LogError(device, "VUID-VkSampleLocationsInfoEXT-sampleLocationsCount-01527", |
| "%s: VkSampleLocationsInfoEXT::sampleLocationsCount (%u) must equal grid width * grid height * pixel " |
| "sample rate which currently is (%u * %u * %u).", |
| apiName, pSampleLocationsInfo->sampleLocationsCount, pSampleLocationsInfo->sampleLocationGridSize.width, |
| pSampleLocationsInfo->sampleLocationGridSize.height, SampleCountSize(sample_count)); |
| } |
| if ((phys_dev_ext_props.sample_locations_props.sampleLocationSampleCounts & sample_count) == 0) { |
| skip |= LogError(device, "VUID-VkSampleLocationsInfoEXT-sampleLocationsPerPixel-01526", |
| "%s: VkSampleLocationsInfoEXT::sampleLocationsPerPixel of %s is not supported by the device, please check " |
| "VkPhysicalDeviceSampleLocationsPropertiesEXT::sampleLocationSampleCounts for valid sample counts.", |
| apiName, string_VkSampleCountFlagBits(sample_count)); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::MatchSampleLocationsInfo(const VkSampleLocationsInfoEXT *pSampleLocationsInfo1, |
| const VkSampleLocationsInfoEXT *pSampleLocationsInfo2) const { |
| if (pSampleLocationsInfo1->sampleLocationsPerPixel != pSampleLocationsInfo2->sampleLocationsPerPixel || |
| pSampleLocationsInfo1->sampleLocationGridSize.width != pSampleLocationsInfo2->sampleLocationGridSize.width || |
| pSampleLocationsInfo1->sampleLocationGridSize.height != pSampleLocationsInfo2->sampleLocationGridSize.height || |
| pSampleLocationsInfo1->sampleLocationsCount != pSampleLocationsInfo2->sampleLocationsCount) { |
| return false; |
| } |
| for (uint32_t i = 0; i < pSampleLocationsInfo1->sampleLocationsCount; ++i) { |
| if (pSampleLocationsInfo1->pSampleLocations[i].x != pSampleLocationsInfo2->pSampleLocations[i].x || |
| pSampleLocationsInfo1->pSampleLocations[i].y != pSampleLocationsInfo2->pSampleLocations[i].y) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static char const *GetCauseStr(VulkanTypedHandle obj) { |
| if (obj.type == kVulkanObjectTypeDescriptorSet) return "destroyed or updated"; |
| if (obj.type == kVulkanObjectTypeCommandBuffer) return "destroyed or rerecorded"; |
| return "destroyed"; |
| } |
| |
| bool CoreChecks::ReportInvalidCommandBuffer(const CMD_BUFFER_STATE *cb_state, const char *call_source) const { |
| bool skip = false; |
| for (const auto& entry: cb_state->broken_bindings) { |
| const auto& obj = entry.first; |
| const char *cause_str = GetCauseStr(obj); |
| 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(objlist, vuid, "You are adding %s to %s that is invalid because bound %s was %s.", call_source, |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), report_data->FormatHandle(obj).c_str(), cause_str); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateIndirectCmd(const CMD_BUFFER_STATE &cb_state, const BUFFER_STATE &buffer_state, CMD_TYPE cmd_type) const { |
| bool skip = false; |
| const DrawDispatchVuid vuid = GetDrawDispatchVuid(cmd_type); |
| const char *caller_name = CommandTypeString(cmd_type); |
| |
| skip |= ValidateMemoryIsBoundToBuffer(&buffer_state, caller_name, vuid.indirect_contiguous_memory); |
| skip |= ValidateBufferUsageFlags(&buffer_state, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, true, vuid.indirect_buffer_bit, |
| caller_name, "VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT"); |
| if (cb_state.unprotected == false) { |
| skip |= LogError(cb_state.Handle(), vuid.indirect_protected_cb, |
| "%s: Indirect commands can't be used in protected command buffers.", caller_name); |
| } |
| return skip; |
| } |
| |
| template <typename T1> |
| bool CoreChecks::ValidateDeviceMaskToPhysicalDeviceCount(uint32_t deviceMask, const T1 object, const char *VUID) const { |
| bool skip = false; |
| uint32_t count = 1 << physical_device_count; |
| if (count <= deviceMask) { |
| skip |= LogError(object, VUID, "deviceMask(0x%" PRIx32 ") is invalid. Physical device count is %" PRIu32 ".", deviceMask, |
| physical_device_count); |
| } |
| return skip; |
| } |
| |
| template <typename T1> |
| bool CoreChecks::ValidateDeviceMaskToZero(uint32_t deviceMask, const T1 object, const char *VUID) const { |
| bool skip = false; |
| if (deviceMask == 0) { |
| skip |= LogError(object, VUID, "deviceMask(0x%" PRIx32 ") must be non-zero.", deviceMask); |
| } |
| return skip; |
| } |
| |
| template <typename T1> |
| bool CoreChecks::ValidateDeviceMaskToCommandBuffer(const CMD_BUFFER_STATE *pCB, uint32_t deviceMask, const T1 object, |
| const char *VUID) const { |
| bool skip = false; |
| if ((deviceMask & pCB->initial_device_mask) != deviceMask) { |
| skip |= LogError(object, VUID, "deviceMask(0x%" PRIx32 ") is not a subset of %s initial device mask(0x%" PRIx32 ").", |
| deviceMask, report_data->FormatHandle(pCB->commandBuffer()).c_str(), pCB->initial_device_mask); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateDeviceMaskToRenderPass(const CMD_BUFFER_STATE *pCB, uint32_t deviceMask, const char *VUID) const { |
| bool skip = false; |
| if ((deviceMask & pCB->active_render_pass_device_mask) != deviceMask) { |
| skip |= LogError(pCB->commandBuffer(), VUID, "deviceMask(0x%" PRIx32 ") is not a subset of %s device mask(0x%" PRIx32 ").", |
| deviceMask, report_data->FormatHandle(pCB->activeRenderPass->renderPass()).c_str(), |
| pCB->active_render_pass_device_mask); |
| } |
| return skip; |
| } |
| |
| // Flags validation error if the associated call is made inside a render pass. The apiName routine should ONLY be called outside a |
| // render pass. |
| bool CoreChecks::InsideRenderPass(const CMD_BUFFER_STATE *pCB, const char *apiName, const char *msgCode) const { |
| bool inside = false; |
| if (pCB->activeRenderPass) { |
| inside = LogError(pCB->commandBuffer(), msgCode, "%s: It is invalid to issue this call inside an active %s.", apiName, |
| report_data->FormatHandle(pCB->activeRenderPass->renderPass()).c_str()); |
| } |
| return inside; |
| } |
| |
| // Flags validation error if the associated call is made outside a render pass. The apiName |
| // routine should ONLY be called inside a render pass. |
| bool CoreChecks::OutsideRenderPass(const CMD_BUFFER_STATE *pCB, const char *apiName, const char *msgCode) const { |
| bool outside = false; |
| if (((pCB->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) && (!pCB->activeRenderPass)) || |
| ((pCB->createInfo.level == VK_COMMAND_BUFFER_LEVEL_SECONDARY) && (!pCB->activeRenderPass) && |
| !(pCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT))) { |
| outside = LogError(pCB->commandBuffer(), msgCode, "%s: This call must be issued inside an active render pass.", apiName); |
| } |
| return outside; |
| } |
| |
| bool CoreChecks::ValidateQueueFamilyIndex(const PHYSICAL_DEVICE_STATE *pd_state, uint32_t requested_queue_family, |
| const char *err_code, const char *cmd_name, const char *queue_family_var_name) const { |
| bool skip = false; |
| |
| if (requested_queue_family >= pd_state->queue_family_known_count) { |
| const char *conditional_ext_cmd = |
| instance_extensions.vk_khr_get_physical_device_properties2 ? " or vkGetPhysicalDeviceQueueFamilyProperties2[KHR]" : ""; |
| |
| skip |= LogError(pd_state->Handle(), err_code, |
| "%s: %s (= %" PRIu32 |
| ") is not less than any previously obtained pQueueFamilyPropertyCount from " |
| "vkGetPhysicalDeviceQueueFamilyProperties%s (i.e. is not less than %s).", |
| cmd_name, queue_family_var_name, requested_queue_family, conditional_ext_cmd, |
| std::to_string(pd_state->queue_family_known_count).c_str()); |
| } |
| return skip; |
| } |
| |
| // Verify VkDeviceQueueCreateInfos |
| bool CoreChecks::ValidateDeviceQueueCreateInfos(const PHYSICAL_DEVICE_STATE *pd_state, uint32_t info_count, |
| const VkDeviceQueueCreateInfo *infos) const { |
| bool skip = false; |
| |
| const uint32_t not_used = std::numeric_limits<uint32_t>::max(); |
| struct create_flags { |
| // uint32_t is to represent the queue family index to allow for better error messages |
| uint32_t unprocted_index; |
| uint32_t protected_index; |
| create_flags(uint32_t a, uint32_t b) : unprocted_index(a), protected_index(b) {} |
| }; |
| layer_data::unordered_map<uint32_t, create_flags> queue_family_map; |
| layer_data::unordered_map<uint32_t, VkQueueGlobalPriorityKHR> global_priorities; |
| |
| for (uint32_t i = 0; i < info_count; ++i) { |
| const auto requested_queue_family = infos[i].queueFamilyIndex; |
| const bool protected_create_bit = (infos[i].flags & VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT) != 0; |
| |
| std::string queue_family_var_name = "pCreateInfo->pQueueCreateInfos[" + std::to_string(i) + "].queueFamilyIndex"; |
| skip |= ValidateQueueFamilyIndex(pd_state, requested_queue_family, "VUID-VkDeviceQueueCreateInfo-queueFamilyIndex-00381", |
| "vkCreateDevice", queue_family_var_name.c_str()); |
| if (skip) { // Skip if queue family index is invalid, as it will be used as index in arrays |
| continue; |
| } |
| |
| if (api_version == VK_API_VERSION_1_0) { |
| // Vulkan 1.0 didn't have protected memory so always needed unique info |
| create_flags flags = {requested_queue_family, not_used}; |
| if (queue_family_map.emplace(requested_queue_family, flags).second == false) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-queueFamilyIndex-00372", |
| "CreateDevice(): %s (=%" PRIu32 |
| ") is not unique and was also used in pCreateInfo->pQueueCreateInfos[%d].", |
| queue_family_var_name.c_str(), requested_queue_family, |
| queue_family_map.at(requested_queue_family).unprocted_index); |
| } |
| } else { |
| // Vulkan 1.1 and up can have 2 queues be same family index if one is protected and one isn't |
| auto it = queue_family_map.find(requested_queue_family); |
| if (it == queue_family_map.end()) { |
| // Add first time seeing queue family index and what the create flags were |
| create_flags new_flags = {not_used, not_used}; |
| if (protected_create_bit) { |
| new_flags.protected_index = requested_queue_family; |
| } else { |
| new_flags.unprocted_index = requested_queue_family; |
| } |
| queue_family_map.emplace(requested_queue_family, new_flags); |
| } else { |
| // The queue family was seen, so now need to make sure the flags were different |
| if (protected_create_bit) { |
| if (it->second.protected_index != not_used) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-queueFamilyIndex-02802", |
| "CreateDevice(): %s (=%" PRIu32 |
| ") is not unique and was also used in pCreateInfo->pQueueCreateInfos[%d] which both have " |
| "VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT.", |
| queue_family_var_name.c_str(), requested_queue_family, |
| queue_family_map.at(requested_queue_family).protected_index); |
| } else { |
| it->second.protected_index = requested_queue_family; |
| } |
| } else { |
| if (it->second.unprocted_index != not_used) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-queueFamilyIndex-02802", |
| "CreateDevice(): %s (=%" PRIu32 |
| ") is not unique and was also used in pCreateInfo->pQueueCreateInfos[%d].", |
| queue_family_var_name.c_str(), requested_queue_family, |
| queue_family_map.at(requested_queue_family).unprocted_index); |
| } else { |
| it->second.unprocted_index = requested_queue_family; |
| } |
| } |
| } |
| } |
| |
| VkQueueGlobalPriorityKHR global_priority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR; // Implicit default value |
| const auto *global_priority_ci = LvlFindInChain<VkDeviceQueueGlobalPriorityCreateInfoKHR>(infos[i].pNext); |
| if (global_priority_ci) { |
| global_priority = global_priority_ci->globalPriority; |
| } |
| const auto prev_global_priority = global_priorities.find(infos[i].queueFamilyIndex); |
| if (prev_global_priority != global_priorities.end()) { |
| if (prev_global_priority->second != global_priority) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-pQueueCreateInfos-06654", |
| "vkCreateDevice(): Multiple queues are created with queueFamilyIndex %" PRIu32 |
| ", but one has global priority %s and another %s.", |
| infos[i].queueFamilyIndex, string_VkQueueGlobalPriorityKHR(prev_global_priority->second), |
| string_VkQueueGlobalPriorityKHR(global_priority)); |
| } |
| } else { |
| global_priorities.insert({infos[i].queueFamilyIndex, global_priority}); |
| } |
| |
| const VkQueueFamilyProperties requested_queue_family_props = pd_state->queue_family_properties[requested_queue_family]; |
| |
| // if using protected flag, make sure queue supports it |
| if (protected_create_bit && ((requested_queue_family_props.queueFlags & VK_QUEUE_PROTECTED_BIT) == 0)) { |
| skip |= LogError( |
| pd_state->Handle(), "VUID-VkDeviceQueueCreateInfo-flags-06449", |
| "CreateDevice(): %s (=%" PRIu32 |
| ") does not have VK_QUEUE_PROTECTED_BIT supported, but VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT is being used.", |
| queue_family_var_name.c_str(), requested_queue_family); |
| } |
| |
| // Verify that requested queue count of queue family is known to be valid at this point in time |
| if (requested_queue_family < pd_state->queue_family_known_count) { |
| const auto requested_queue_count = infos[i].queueCount; |
| const bool queue_family_has_props = requested_queue_family < pd_state->queue_family_properties.size(); |
| // spec guarantees at least one queue for each queue family |
| const uint32_t available_queue_count = queue_family_has_props ? requested_queue_family_props.queueCount : 1; |
| const char *conditional_ext_cmd = instance_extensions.vk_khr_get_physical_device_properties2 |
| ? " or vkGetPhysicalDeviceQueueFamilyProperties2[KHR]" |
| : ""; |
| |
| if (requested_queue_count > available_queue_count) { |
| const std::string count_note = |
| queue_family_has_props |
| ? "i.e. is not less than or equal to " + std::to_string(requested_queue_family_props.queueCount) |
| : "the pQueueFamilyProperties[" + std::to_string(requested_queue_family) + "] was never obtained"; |
| |
| skip |= LogError( |
| pd_state->Handle(), "VUID-VkDeviceQueueCreateInfo-queueCount-00382", |
| "vkCreateDevice: pCreateInfo->pQueueCreateInfos[%" PRIu32 "].queueCount (=%" PRIu32 |
| ") is not less than or equal to available queue count for this pCreateInfo->pQueueCreateInfos[%" PRIu32 |
| "].queueFamilyIndex} (=%" PRIu32 ") obtained previously from vkGetPhysicalDeviceQueueFamilyProperties%s (%s).", |
| i, requested_queue_count, i, requested_queue_family, conditional_ext_cmd, count_note.c_str()); |
| } |
| } |
| |
| const VkQueueFlags queue_flags = pd_state->queue_family_properties[requested_queue_family].queueFlags; |
| if ((infos[i].flags == VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT) && ((queue_flags & VK_QUEUE_PROTECTED_BIT) == VK_FALSE)) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceQueueCreateInfo-flags-06449", |
| "vkCreateDevice: pCreateInfo->flags set to VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT on a queue that doesn't include VK_QUEUE_PROTECTED_BIT capability"); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) const { |
| bool skip = false; |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(gpu); |
| |
| // TODO: object_tracker should perhaps do this instead |
| // and it does not seem to currently work anyway -- the loader just crashes before this point |
| if (!pd_state) { |
| skip |= LogError(device, kVUID_Core_DevLimit_MustQueryCount, |
| "Invalid call to vkCreateDevice() w/o first calling vkEnumeratePhysicalDevices()."); |
| } else { |
| skip |= ValidateDeviceQueueCreateInfos(pd_state.get(), pCreateInfo->queueCreateInfoCount, pCreateInfo->pQueueCreateInfos); |
| |
| const VkPhysicalDeviceFragmentShadingRateFeaturesKHR *fragment_shading_rate_features = |
| LvlFindInChain<VkPhysicalDeviceFragmentShadingRateFeaturesKHR>(pCreateInfo->pNext); |
| |
| if (fragment_shading_rate_features) { |
| const VkPhysicalDeviceShadingRateImageFeaturesNV *shading_rate_image_features = |
| LvlFindInChain<VkPhysicalDeviceShadingRateImageFeaturesNV>(pCreateInfo->pNext); |
| |
| if (shading_rate_image_features && shading_rate_image_features->shadingRateImage) { |
| if (fragment_shading_rate_features->pipelineFragmentShadingRate) { |
| skip |= LogError( |
| pd_state->Handle(), "VUID-VkDeviceCreateInfo-shadingRateImage-04478", |
| "vkCreateDevice: Cannot enable shadingRateImage and pipelineFragmentShadingRate features simultaneously."); |
| } |
| if (fragment_shading_rate_features->primitiveFragmentShadingRate) { |
| skip |= LogError( |
| pd_state->Handle(), "VUID-VkDeviceCreateInfo-shadingRateImage-04479", |
| "vkCreateDevice: Cannot enable shadingRateImage and primitiveFragmentShadingRate features simultaneously."); |
| } |
| if (fragment_shading_rate_features->attachmentFragmentShadingRate) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-shadingRateImage-04480", |
| "vkCreateDevice: Cannot enable shadingRateImage and attachmentFragmentShadingRate features " |
| "simultaneously."); |
| } |
| } |
| |
| const VkPhysicalDeviceFragmentDensityMapFeaturesEXT *fragment_density_map_features = |
| LvlFindInChain<VkPhysicalDeviceFragmentDensityMapFeaturesEXT>(pCreateInfo->pNext); |
| |
| if (fragment_density_map_features && fragment_density_map_features->fragmentDensityMap) { |
| if (fragment_shading_rate_features->pipelineFragmentShadingRate) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-fragmentDensityMap-04481", |
| "vkCreateDevice: Cannot enable fragmentDensityMap and pipelineFragmentShadingRate features " |
| "simultaneously."); |
| } |
| if (fragment_shading_rate_features->primitiveFragmentShadingRate) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-fragmentDensityMap-04482", |
| "vkCreateDevice: Cannot enable fragmentDensityMap and primitiveFragmentShadingRate features " |
| "simultaneously."); |
| } |
| if (fragment_shading_rate_features->attachmentFragmentShadingRate) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-fragmentDensityMap-04483", |
| "vkCreateDevice: Cannot enable fragmentDensityMap and attachmentFragmentShadingRate features " |
| "simultaneously."); |
| } |
| } |
| } |
| |
| const auto *shader_image_atomic_int64_features = |
| LvlFindInChain<VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT>(pCreateInfo->pNext); |
| if (shader_image_atomic_int64_features) { |
| if (shader_image_atomic_int64_features->sparseImageInt64Atomics && |
| !shader_image_atomic_int64_features->shaderImageInt64Atomics) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-None-04896", |
| "vkCreateDevice: if shaderImageInt64Atomics feature is enabled then sparseImageInt64Atomics " |
| "feature must also be enabled."); |
| } |
| } |
| const auto *shader_atomic_float_features = LvlFindInChain<VkPhysicalDeviceShaderAtomicFloatFeaturesEXT>(pCreateInfo->pNext); |
| if (shader_atomic_float_features) { |
| if (shader_atomic_float_features->sparseImageFloat32Atomics && |
| !shader_atomic_float_features->shaderImageFloat32Atomics) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-None-04897", |
| "vkCreateDevice: if sparseImageFloat32Atomics feature is enabled then shaderImageFloat32Atomics " |
| "feature must also be enabled."); |
| } |
| if (shader_atomic_float_features->sparseImageFloat32AtomicAdd && |
| !shader_atomic_float_features->shaderImageFloat32AtomicAdd) { |
| skip |= |
| LogError(pd_state->Handle(), "VUID-VkDeviceCreateInfo-None-04898", |
| "vkCreateDevice: if sparseImageFloat32AtomicAdd feature is enabled then shaderImageFloat32AtomicAdd " |
| "feature must also be enabled."); |
| } |
| } |
| const auto *shader_atomic_float2_features = |
| LvlFindInChain<VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT>(pCreateInfo->pNext); |
| if (shader_atomic_float2_features) { |
| if (shader_atomic_float2_features->sparseImageFloat32AtomicMinMax && |
| !shader_atomic_float2_features->shaderImageFloat32AtomicMinMax) { |
| skip |= LogError( |
| pd_state->Handle(), "VUID-VkDeviceCreateInfo-sparseImageFloat32AtomicMinMax-04975", |
| "vkCreateDevice: if sparseImageFloat32AtomicMinMax feature is enabled then shaderImageFloat32AtomicMinMax " |
| "feature must also be enabled."); |
| } |
| } |
| const auto *device_group_ci = LvlFindInChain<VkDeviceGroupDeviceCreateInfo>(pCreateInfo->pNext); |
| if (device_group_ci) { |
| for (uint32_t i = 0; i < device_group_ci->physicalDeviceCount - 1; ++i) { |
| for (uint32_t j = i + 1; j < device_group_ci->physicalDeviceCount; ++j) { |
| if (device_group_ci->pPhysicalDevices[i] == device_group_ci->pPhysicalDevices[j]) { |
| skip |= LogError(pd_state->Handle(), "VUID-VkDeviceGroupDeviceCreateInfo-pPhysicalDevices-00375", |
| "vkCreateDevice: VkDeviceGroupDeviceCreateInfo has a duplicated physical device " |
| "in pPhysicalDevices [%" PRIu32 "] and [%" PRIu32 "].", |
| i, j); |
| } |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| void CoreChecks::CreateDevice(const VkDeviceCreateInfo *pCreateInfo) { |
| // The state tracker sets up the device state |
| StateTracker::CreateDevice(pCreateInfo); |
| |
| // Add the callback hooks for the functions that are either broadly or deeply used and that the ValidationStateTracker refactor |
| // would be messier without. |
| // TODO: Find a good way to do this hooklessly. |
| SetSetImageViewInitialLayoutCallback( |
| [](CMD_BUFFER_STATE *cb_node, const IMAGE_VIEW_STATE &iv_state, VkImageLayout layout) -> void { |
| cb_node->SetImageViewInitialLayout(iv_state, layout); |
| }); |
| |
| // Allocate shader validation cache |
| if (!disabled[shader_validation_caching] && !disabled[shader_validation] && !core_validation_cache) { |
| auto tmp_path = GetEnvironment("XDG_CACHE_HOME"); |
| if (!tmp_path.size()) { |
| auto cachepath = GetEnvironment("HOME") + "/.cache"; |
| struct stat info; |
| if (stat(cachepath.c_str(), &info) == 0) { |
| if ((info.st_mode & S_IFMT) == S_IFDIR) { |
| tmp_path = cachepath; |
| } |
| } |
| } |
| if (!tmp_path.size()) tmp_path = GetEnvironment("TMPDIR"); |
| if (!tmp_path.size()) tmp_path = GetEnvironment("TMP"); |
| if (!tmp_path.size()) tmp_path = GetEnvironment("TEMP"); |
| if (!tmp_path.size()) tmp_path = "/tmp"; |
| validation_cache_path = tmp_path + "/shader_validation_cache"; |
| #if defined(__linux__) || defined(__FreeBSD__) |
| validation_cache_path += "-" + std::to_string(getuid()); |
| #endif |
| validation_cache_path += ".bin"; |
| |
| std::vector<char> validation_cache_data; |
| std::ifstream read_file(validation_cache_path.c_str(), std::ios::in | std::ios::binary); |
| |
| if (read_file) { |
| std::copy(std::istreambuf_iterator<char>(read_file), {}, std::back_inserter(validation_cache_data)); |
| read_file.close(); |
| } else { |
| LogInfo(device, "UNASSIGNED-cache-file-error", |
| "Cannot open shader validation cache at %s for reading (it may not exist yet)", validation_cache_path.c_str()); |
| } |
| |
| VkValidationCacheCreateInfoEXT cacheCreateInfo = {}; |
| cacheCreateInfo.sType = VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT; |
| cacheCreateInfo.pNext = NULL; |
| cacheCreateInfo.initialDataSize = validation_cache_data.size(); |
| cacheCreateInfo.pInitialData = validation_cache_data.data(); |
| cacheCreateInfo.flags = 0; |
| CoreLayerCreateValidationCacheEXT(device, &cacheCreateInfo, nullptr, &core_validation_cache); |
| } |
| } |
| |
| void CoreChecks::PreCallRecordDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) { |
| if (!device) return; |
| |
| StateTracker::PreCallRecordDestroyDevice(device, pAllocator); |
| |
| if (core_validation_cache) { |
| size_t validation_cache_size = 0; |
| void *validation_cache_data = nullptr; |
| |
| CoreLayerGetValidationCacheDataEXT(device, core_validation_cache, &validation_cache_size, nullptr); |
| |
| validation_cache_data = (char *)malloc(sizeof(char) * validation_cache_size); |
| if (!validation_cache_data) { |
| LogInfo(device, "UNASSIGNED-cache-memory-error", "Validation Cache Memory Error"); |
| return; |
| } |
| |
| VkResult result = |
| CoreLayerGetValidationCacheDataEXT(device, core_validation_cache, &validation_cache_size, validation_cache_data); |
| |
| if (result != VK_SUCCESS) { |
| LogInfo(device, "UNASSIGNED-cache-retrieval-error", "Validation Cache Retrieval Error"); |
| return; |
| } |
| |
| if (validation_cache_path.size() > 0) { |
| FILE *write_file = fopen(validation_cache_path.c_str(), "wb"); |
| if (write_file) { |
| fwrite(validation_cache_data, sizeof(char), validation_cache_size, write_file); |
| fclose(write_file); |
| } else { |
| LogInfo(device, "UNASSIGNED-cache-write-error", "Cannot open shader validation cache at %s for writing", |
| validation_cache_path.c_str()); |
| } |
| } |
| free(validation_cache_data); |
| CoreLayerDestroyValidationCacheEXT(device, core_validation_cache, NULL); |
| } |
| } |
| |
| bool CoreChecks::ValidateStageMaskHost(const Location &loc, VkPipelineStageFlags2KHR stageMask) const { |
| bool skip = false; |
| if ((stageMask & VK_PIPELINE_STAGE_HOST_BIT) != 0) { |
| const auto &vuid = sync_vuid_maps::GetQueueSubmitVUID(loc, sync_vuid_maps::SubmitError::kHostStageMask); |
| skip |= LogError( |
| device, vuid, |
| "%s stage mask must not include VK_PIPELINE_STAGE_HOST_BIT as the stage can't be invoked inside a command buffer.", |
| loc.Message().c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCommandBufferSimultaneousUse(const Location &loc, const CMD_BUFFER_STATE *pCB, |
| int current_submit_count) const { |
| using sync_vuid_maps::GetQueueSubmitVUID; |
| using sync_vuid_maps::SubmitError; |
| |
| bool skip = false; |
| if ((pCB->InUse() || current_submit_count > 1) && |
| !(pCB->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { |
| const auto &vuid = sync_vuid_maps::GetQueueSubmitVUID(loc, SubmitError::kCmdNotSimultaneous); |
| |
| skip |= LogError(device, vuid, "%s %s is already in use and is not marked for simultaneous use.", loc.Message().c_str(), |
| report_data->FormatHandle(pCB->commandBuffer()).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCommandBufferState(const CMD_BUFFER_STATE *cb_state, const char *call_source, int current_submit_count, |
| const char *vu_id) const { |
| bool skip = false; |
| if (disabled[command_buffer_state]) return skip; |
| // Validate ONE_TIME_SUBMIT_BIT CB is not being submitted more than once |
| if ((cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) && |
| (cb_state->submitCount + current_submit_count > 1)) { |
| skip |= LogError(cb_state->commandBuffer(), kVUID_Core_DrawState_CommandBufferSingleSubmitViolation, |
| "%s was begun w/ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT set, but has been submitted 0x%" PRIxLEAST64 |
| "times.", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), cb_state->submitCount + current_submit_count); |
| } |
| |
| // Validate that cmd buffers have been updated |
| switch (cb_state->state) { |
| case CB_INVALID_INCOMPLETE: |
| case CB_INVALID_COMPLETE: |
| skip |= ReportInvalidCommandBuffer(cb_state, call_source); |
| break; |
| |
| case CB_NEW: |
| skip |= LogError(cb_state->commandBuffer(), vu_id, "%s used in the call to %s is unrecorded and contains no commands.", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), call_source); |
| break; |
| |
| case CB_RECORDING: |
| skip |= LogError(cb_state->commandBuffer(), kVUID_Core_DrawState_NoEndCommandBuffer, |
| "You must call vkEndCommandBuffer() on %s before this call to %s!", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), call_source); |
| break; |
| |
| default: /* recorded */ |
| break; |
| } |
| return skip; |
| } |
| |
| // Check that the queue family index of 'queue' matches one of the entries in pQueueFamilyIndices |
| bool CoreChecks::ValidImageBufferQueue(const CMD_BUFFER_STATE *cb_node, const VulkanTypedHandle &object, uint32_t queueFamilyIndex, |
| uint32_t count, const uint32_t *indices) const { |
| bool found = false; |
| bool skip = false; |
| for (uint32_t i = 0; i < count; i++) { |
| if (indices[i] == queueFamilyIndex) { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| LogObjectList objlist(cb_node->commandBuffer()); |
| objlist.add(object); |
| skip = LogError(objlist, "VUID-vkQueueSubmit-pSubmits-04626", |
| "vkQueueSubmit: %s contains %s which was not created allowing concurrent access to " |
| "this queue family %d.", |
| report_data->FormatHandle(cb_node->commandBuffer()).c_str(), report_data->FormatHandle(object).c_str(), |
| queueFamilyIndex); |
| } |
| return skip; |
| } |
| |
| // Validate that queueFamilyIndices of primary command buffers match this queue |
| // Secondary command buffers were previously validated in vkCmdExecuteCommands(). |
| bool CoreChecks::ValidateQueueFamilyIndices(const Location &loc, const CMD_BUFFER_STATE *pCB, VkQueue queue) const { |
| using sync_vuid_maps::GetQueueSubmitVUID; |
| using sync_vuid_maps::SubmitError; |
| bool skip = false; |
| auto pool = pCB->command_pool; |
| auto queue_state = Get<QUEUE_STATE>(queue); |
| |
| if (pool && queue_state) { |
| if (pool->queueFamilyIndex != queue_state->queueFamilyIndex) { |
| LogObjectList objlist(pCB->commandBuffer()); |
| objlist.add(queue); |
| const auto &vuid = GetQueueSubmitVUID(loc, SubmitError::kCmdWrongQueueFamily); |
| skip |= LogError(objlist, vuid, |
| "%s Primary %s created in queue family %d is being submitted on %s " |
| "from queue family %d.", |
| loc.Message().c_str(), report_data->FormatHandle(pCB->commandBuffer()).c_str(), pool->queueFamilyIndex, |
| report_data->FormatHandle(queue).c_str(), queue_state->queueFamilyIndex); |
| } |
| |
| // Ensure that any bound images or buffers created with SHARING_MODE_CONCURRENT have access to the current queue family |
| for (const auto &base_node : pCB->object_bindings) { |
| switch (base_node->Type()) { |
| case kVulkanObjectTypeImage: { |
| auto image_state = static_cast<const IMAGE_STATE *>(base_node.get()); |
| if (image_state && image_state->createInfo.sharingMode == VK_SHARING_MODE_CONCURRENT) { |
| skip |= ValidImageBufferQueue(pCB, image_state->Handle(), queue_state->queueFamilyIndex, |
| image_state->createInfo.queueFamilyIndexCount, |
| image_state->createInfo.pQueueFamilyIndices); |
| } |
| break; |
| } |
| case kVulkanObjectTypeBuffer: { |
| auto buffer_state = static_cast<const BUFFER_STATE *>(base_node.get()); |
| if (buffer_state && buffer_state->createInfo.sharingMode == VK_SHARING_MODE_CONCURRENT) { |
| skip |= ValidImageBufferQueue(pCB, buffer_state->Handle(), queue_state->queueFamilyIndex, |
| buffer_state->createInfo.queueFamilyIndexCount, |
| buffer_state->createInfo.pQueueFamilyIndices); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePrimaryCommandBufferState( |
| const Location &loc, const CMD_BUFFER_STATE *pCB, int current_submit_count, |
| QFOTransferCBScoreboards<QFOImageTransferBarrier> *qfo_image_scoreboards, |
| QFOTransferCBScoreboards<QFOBufferTransferBarrier> *qfo_buffer_scoreboards) const { |
| using sync_vuid_maps::GetQueueSubmitVUID; |
| using sync_vuid_maps::SubmitError; |
| |
| // Track in-use for resources off of primary and any secondary CBs |
| bool skip = false; |
| |
| if (pCB->createInfo.level == VK_COMMAND_BUFFER_LEVEL_SECONDARY) { |
| const auto &vuid = GetQueueSubmitVUID(loc, SubmitError::kSecondaryCmdInSubmit); |
| skip |= LogError(pCB->commandBuffer(), vuid, "%s Command buffer %s must be allocated with VK_COMMAND_BUFFER_LEVEL_PRIMARY.", |
| loc.Message().c_str(), report_data->FormatHandle(pCB->commandBuffer()).c_str()); |
| } else { |
| for (const auto *sub_cb : pCB->linkedCommandBuffers) { |
| skip |= ValidateQueuedQFOTransfers(sub_cb, qfo_image_scoreboards, qfo_buffer_scoreboards); |
| // TODO: replace with InvalidateCommandBuffers() at recording. |
| if ((sub_cb->primaryCommandBuffer != pCB->commandBuffer()) && |
| !(sub_cb->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { |
| LogObjectList objlist(device); |
| objlist.add(pCB->commandBuffer()); |
| objlist.add(sub_cb->commandBuffer()); |
| objlist.add(sub_cb->primaryCommandBuffer); |
| const auto &vuid = GetQueueSubmitVUID(loc, SubmitError::kSecondaryCmdNotSimultaneous); |
| skip |= LogError(objlist, vuid, |
| "%s %s was submitted with secondary %s but that buffer has subsequently been bound to " |
| "primary %s and it does not have VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set.", |
| loc.Message().c_str(), report_data->FormatHandle(pCB->commandBuffer()).c_str(), |
| report_data->FormatHandle(sub_cb->commandBuffer()).c_str(), |
| report_data->FormatHandle(sub_cb->primaryCommandBuffer).c_str()); |
| } |
| } |
| } |
| |
| // If USAGE_SIMULTANEOUS_USE_BIT not set then CB cannot already be executing on device |
| skip |= ValidateCommandBufferSimultaneousUse(loc, pCB, current_submit_count); |
| |
| skip |= ValidateQueuedQFOTransfers(pCB, qfo_image_scoreboards, qfo_buffer_scoreboards); |
| |
| const char *vuid = loc.function == Func::vkQueueSubmit ? "VUID-vkQueueSubmit-pCommandBuffers-00072" |
| : "VUID-vkQueueSubmit2-commandBuffer-03876"; |
| skip |= ValidateCommandBufferState(pCB, loc.StringFunc().c_str(), current_submit_count, vuid); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateFenceForSubmit(const FENCE_STATE *fence_state, const char *inflight_vuid, const char *retired_vuid, |
| const char *func_name) const { |
| bool skip = false; |
| |
| if (fence_state && fence_state->Scope() == kSyncScopeInternal) { |
| switch (fence_state->State()) { |
| case FENCE_INFLIGHT: |
| skip |= LogError(fence_state->fence(), inflight_vuid, "%s: %s is already in use by another submission.", func_name, |
| report_data->FormatHandle(fence_state->fence()).c_str()); |
| break; |
| case FENCE_RETIRED: |
| skip |= LogError(fence_state->fence(), retired_vuid, |
| "%s: %s submitted in SIGNALED state. Fences must be reset before being submitted", func_name, |
| report_data->FormatHandle(fence_state->fence()).c_str()); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return skip; |
| } |
| |
| void CoreChecks::PostCallRecordQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence, |
| VkResult result) { |
| StateTracker::PostCallRecordQueueSubmit(queue, submitCount, pSubmits, fence, result); |
| |
| if (result != VK_SUCCESS) return; |
| // The triply nested for duplicates that in the StateTracker, but avoids the need for two additional callbacks. |
| for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { |
| const VkSubmitInfo *submit = &pSubmits[submit_idx]; |
| for (uint32_t i = 0; i < submit->commandBufferCount; i++) { |
| auto cb_node = GetWrite<CMD_BUFFER_STATE>(submit->pCommandBuffers[i]); |
| if (cb_node) { |
| for (auto *secondary_cmd_buffer : cb_node->linkedCommandBuffers) { |
| UpdateCmdBufImageLayouts(secondary_cmd_buffer); |
| RecordQueuedQFOTransfers(secondary_cmd_buffer); |
| } |
| UpdateCmdBufImageLayouts(cb_node.get()); |
| RecordQueuedQFOTransfers(cb_node.get()); |
| } |
| } |
| } |
| } |
| |
| void CoreChecks::RecordQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence, |
| VkResult result) { |
| if (result != VK_SUCCESS) return; |
| // The triply nested for duplicates that in the StateTracker, but avoids the need for two additional callbacks. |
| for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { |
| const VkSubmitInfo2KHR *submit = &pSubmits[submit_idx]; |
| for (uint32_t i = 0; i < submit->commandBufferInfoCount; i++) { |
| auto cb_node = GetWrite<CMD_BUFFER_STATE>(submit->pCommandBufferInfos[i].commandBuffer); |
| if (cb_node) { |
| for (auto *secondaryCmdBuffer : cb_node->linkedCommandBuffers) { |
| UpdateCmdBufImageLayouts(secondaryCmdBuffer); |
| RecordQueuedQFOTransfers(secondaryCmdBuffer); |
| } |
| UpdateCmdBufImageLayouts(cb_node.get()); |
| RecordQueuedQFOTransfers(cb_node.get()); |
| } |
| } |
| } |
| } |
| |
| void CoreChecks::PostCallRecordQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, VkFence fence, |
| VkResult result) { |
| StateTracker::PostCallRecordQueueSubmit2KHR(queue, submitCount, pSubmits, fence, result); |
| RecordQueueSubmit2(queue, submitCount, pSubmits, fence, result); |
| } |
| |
| void CoreChecks::PostCallRecordQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 *pSubmits, VkFence fence, |
| VkResult result) { |
| StateTracker::PostCallRecordQueueSubmit2(queue, submitCount, pSubmits, fence, result); |
| RecordQueueSubmit2(queue, submitCount, pSubmits, fence, result); |
| } |
| |
| struct SemaphoreSubmitState { |
| const CoreChecks *core; |
| VkQueueFlags queue_flags; |
| layer_data::unordered_set<VkSemaphore> signaled_semaphores; |
| layer_data::unordered_set<VkSemaphore> unsignaled_semaphores; |
| layer_data::unordered_set<VkSemaphore> internal_semaphores; |
| |
| SemaphoreSubmitState(const CoreChecks *core_, VkQueueFlags queue_flags_) : core(core_), queue_flags(queue_flags_) {} |
| |
| bool CannotWait(const SEMAPHORE_STATE &semaphore_state) const { |
| auto semaphore = semaphore_state.semaphore(); |
| return unsignaled_semaphores.count(semaphore) || (!signaled_semaphores.count(semaphore) && !semaphore_state.CanBeWaited()); |
| } |
| |
| bool ValidateBinaryWait(const core_error::Location &loc, VkQueue queue, const SEMAPHORE_STATE &semaphore_state) { |
| bool skip = false; |
| using sync_vuid_maps::GetQueueSubmitVUID; |
| using sync_vuid_maps::SubmitError; |
| auto semaphore = semaphore_state.semaphore(); |
| if ((semaphore_state.Scope() == kSyncScopeInternal || internal_semaphores.count(semaphore))) { |
| if (CannotWait(semaphore_state)) { |
| auto last_op = semaphore_state.LastOp(); |
| if (last_op) { |
| if (last_op->IsWait()) { |
| auto other_queue = last_op->queue->Handle(); |
| const char *vuid = loc.function == core_error::Func::vkQueueSubmit |
| ? "VUID-vkQueueSubmit-pWaitSemaphores-00068" |
| : "VUID-vkQueueSubmit2-semaphore-03871"; |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| objlist.add(other_queue); |
| skip |= core->LogError(objlist, vuid, "%s Queue %s is already waiting on semaphore (%s).", |
| loc.Message().c_str(), core->report_data->FormatHandle(other_queue).c_str(), |
| core->report_data->FormatHandle(semaphore).c_str()); |
| } |
| } else { |
| auto error = IsExtEnabled(core->device_extensions.vk_khr_timeline_semaphore) |
| ? SubmitError::kTimelineCannotBeSignalled |
| : SubmitError::kBinaryCannotBeSignalled; |
| const auto &vuid = GetQueueSubmitVUID(loc, error); |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| skip |= core->LogError( |
| objlist, semaphore_state.Scope() == kSyncScopeInternal ? vuid : kVUID_Core_DrawState_QueueForwardProgress, |
| "%s Queue %s is waiting on semaphore (%s) that has no way to be signaled.", loc.Message().c_str(), |
| core->report_data->FormatHandle(queue).c_str(), core->report_data->FormatHandle(semaphore).c_str()); |
| } |
| } else { |
| signaled_semaphores.erase(semaphore); |
| unsignaled_semaphores.insert(semaphore); |
| } |
| } else if (semaphore_state.Scope() == kSyncScopeExternalTemporary) { |
| internal_semaphores.insert(semaphore); |
| } |
| return skip; |
| } |
| |
| bool ValidateWaitSemaphore(const core_error::Location &loc, VkQueue queue, VkSemaphore semaphore, uint64_t value, |
| uint32_t device_Index) { |
| bool skip = false; |
| |
| auto semaphore_state = core->Get<SEMAPHORE_STATE>(semaphore); |
| if (!semaphore_state) { |
| return skip; |
| } |
| switch (semaphore_state->type) { |
| case VK_SEMAPHORE_TYPE_BINARY: |
| skip = ValidateBinaryWait(loc, queue, *semaphore_state); |
| break; |
| case VK_SEMAPHORE_TYPE_TIMELINE: |
| skip |= core->ValidateMaxTimelineSemaphoreValueDifference(loc, *semaphore_state, value); |
| break; |
| default: |
| break; |
| } |
| return skip; |
| } |
| |
| bool CannotSignal(const SEMAPHORE_STATE &semaphore_state) const { |
| const auto semaphore = semaphore_state.semaphore(); |
| return signaled_semaphores.count(semaphore) || |
| (!unsignaled_semaphores.count(semaphore) && !semaphore_state.CanBeSignaled()); |
| } |
| |
| bool ValidateSignalSemaphore(const core_error::Location &loc, VkQueue queue, VkSemaphore semaphore, uint64_t value, |
| uint32_t deviceIndex) { |
| using sync_vuid_maps::GetQueueSubmitVUID; |
| using sync_vuid_maps::SubmitError; |
| bool skip = false; |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| |
| auto semaphore_state = core->Get<SEMAPHORE_STATE>(semaphore); |
| if (!semaphore_state) { |
| return skip; |
| } |
| switch (semaphore_state->type) { |
| case VK_SEMAPHORE_TYPE_BINARY: { |
| if ((semaphore_state->Scope() == kSyncScopeInternal || internal_semaphores.count(semaphore))) { |
| if (CannotSignal(*semaphore_state)) { |
| VkQueue other_queue; |
| const auto last_op = semaphore_state->LastOp(); |
| if (last_op) { |
| other_queue = last_op->queue ? last_op->queue->Queue() : VK_NULL_HANDLE; |
| } else { |
| const auto completed = semaphore_state->Completed(); |
| other_queue = completed.queue ? completed.queue->Queue() : VK_NULL_HANDLE; |
| } |
| objlist.add(other_queue); |
| skip |= core->LogError(objlist, kVUID_Core_DrawState_QueueForwardProgress, |
| "%s is signaling %s (%s) that was previously " |
| "signaled by %s but has not since been waited on by any queue.", |
| loc.Message().c_str(), core->report_data->FormatHandle(queue).c_str(), |
| core->report_data->FormatHandle(semaphore).c_str(), |
| core->report_data->FormatHandle(other_queue).c_str()); |
| } else { |
| unsignaled_semaphores.erase(semaphore); |
| signaled_semaphores.insert(semaphore); |
| } |
| } |
| break; |
| } |
| case VK_SEMAPHORE_TYPE_TIMELINE: { |
| const auto completed = semaphore_state->Completed(); |
| if (value <= completed.payload) { |
| const auto &vuid = GetQueueSubmitVUID(loc, SubmitError::kTimelineSemSmallValue); |
| skip |= core->LogError(objlist, vuid, |
| "%s signal value (0x%" PRIx64 |
| ") in %s must be greater than current timeline semaphore %s value (0x%" PRIx64 ")", |
| loc.Message().c_str(), completed.payload, core->report_data->FormatHandle(queue).c_str(), |
| core->report_data->FormatHandle(semaphore).c_str(), value); |
| } else { |
| skip |= core->ValidateMaxTimelineSemaphoreValueDifference(loc, *semaphore_state, value); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| return skip; |
| } |
| }; |
| |
| bool CoreChecks::ValidateSemaphoresForSubmit(SemaphoreSubmitState &state, VkQueue queue, const VkSubmitInfo *submit, |
| const Location &outer_loc) const { |
| bool skip = false; |
| auto *timeline_semaphore_submit_info = LvlFindInChain<VkTimelineSemaphoreSubmitInfo>(submit->pNext); |
| for (uint32_t i = 0; i < submit->waitSemaphoreCount; ++i) { |
| uint64_t value = 0; |
| uint32_t device_index = 0; // TODO: |
| VkSemaphore semaphore = submit->pWaitSemaphores[i]; |
| |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| if (submit->pWaitDstStageMask) { |
| auto loc = outer_loc.dot(Field::pWaitDstStageMask, i); |
| skip |= ValidatePipelineStage(objlist, loc, state.queue_flags, submit->pWaitDstStageMask[i]); |
| skip |= ValidateStageMaskHost(loc, submit->pWaitDstStageMask[i]); |
| } |
| auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore); |
| if (!semaphore_state) { |
| continue; |
| } |
| auto loc = outer_loc.dot(Field::pWaitSemaphores, i); |
| if (semaphore_state->type == VK_SEMAPHORE_TYPE_TIMELINE) { |
| if (timeline_semaphore_submit_info == nullptr) { |
| skip |= LogError(semaphore, "VUID-VkSubmitInfo-pWaitSemaphores-03239", |
| "%s (%s) is a timeline semaphore, but VkSubmitInfo does " |
| "not include an instance of VkTimelineSemaphoreSubmitInfo", |
| loc.Message().c_str(), report_data->FormatHandle(semaphore).c_str()); |
| continue; |
| } else if (submit->waitSemaphoreCount != timeline_semaphore_submit_info->waitSemaphoreValueCount) { |
| skip |= LogError(semaphore, "VUID-VkSubmitInfo-pNext-03240", |
| "%s (%s) is a timeline semaphore, it contains an " |
| "instance of VkTimelineSemaphoreSubmitInfo, but waitSemaphoreValueCount (%u) is different than " |
| "waitSemaphoreCount (%u)", |
| loc.Message().c_str(), report_data->FormatHandle(semaphore).c_str(), |
| timeline_semaphore_submit_info->waitSemaphoreValueCount, submit->waitSemaphoreCount); |
| continue; |
| } |
| value = timeline_semaphore_submit_info->pWaitSemaphoreValues[i]; |
| } |
| skip |= state.ValidateWaitSemaphore(outer_loc.dot(Field::pWaitSemaphores, i), queue, semaphore, value, device_index); |
| } |
| for (uint32_t i = 0; i < submit->signalSemaphoreCount; ++i) { |
| VkSemaphore semaphore = submit->pSignalSemaphores[i]; |
| uint64_t value = 0; |
| uint32_t device_index = 0; |
| auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore); |
| if (!semaphore_state) { |
| continue; |
| } |
| auto loc = outer_loc.dot(Field::pSignalSemaphores, i); |
| if (semaphore_state->type == VK_SEMAPHORE_TYPE_TIMELINE) { |
| if (timeline_semaphore_submit_info == nullptr) { |
| skip |= LogError(semaphore, "VUID-VkSubmitInfo-pWaitSemaphores-03239", |
| "%s (%s) is a timeline semaphore, but VkSubmitInfo" |
| "does not include an instance of VkTimelineSemaphoreSubmitInfo", |
| loc.Message().c_str(), report_data->FormatHandle(semaphore).c_str()); |
| continue; |
| } else if (submit->signalSemaphoreCount != timeline_semaphore_submit_info->signalSemaphoreValueCount) { |
| skip |= LogError(semaphore, "VUID-VkSubmitInfo-pNext-03241", |
| "%s (%s) is a timeline semaphore, it contains an " |
| "instance of VkTimelineSemaphoreSubmitInfo, but signalSemaphoreValueCount (%u) is different than " |
| "signalSemaphoreCount (%u)", |
| loc.Message().c_str(), report_data->FormatHandle(semaphore).c_str(), |
| timeline_semaphore_submit_info->signalSemaphoreValueCount, submit->signalSemaphoreCount); |
| continue; |
| } |
| value = timeline_semaphore_submit_info->pSignalSemaphoreValues[i]; |
| } |
| skip |= state.ValidateSignalSemaphore(loc, queue, semaphore, value, device_index); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateSemaphoresForSubmit(SemaphoreSubmitState &state, VkQueue queue, const VkSubmitInfo2KHR *submit, |
| const Location &outer_loc) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < submit->waitSemaphoreInfoCount; ++i) { |
| const auto &sem_info = submit->pWaitSemaphoreInfos[i]; |
| Location loc = outer_loc.dot(Field::pWaitSemaphoreInfos, i); |
| skip |= ValidatePipelineStage(LogObjectList(sem_info.semaphore), loc.dot(Field::stageMask), state.queue_flags, |
| sem_info.stageMask); |
| skip |= ValidateStageMaskHost(loc.dot(Field::stageMask), sem_info.stageMask); |
| skip |= state.ValidateWaitSemaphore(loc, queue, sem_info.semaphore, sem_info.value, sem_info.deviceIndex); |
| } |
| for (uint32_t i = 0; i < submit->signalSemaphoreInfoCount; ++i) { |
| const auto &sem_info = submit->pSignalSemaphoreInfos[i]; |
| auto loc = outer_loc.dot(Field::pSignalSemaphoreInfos, i); |
| skip |= ValidatePipelineStage(LogObjectList(sem_info.semaphore), loc.dot(Field::stageMask), state.queue_flags, |
| sem_info.stageMask); |
| skip |= ValidateStageMaskHost(loc.dot(Field::stageMask), sem_info.stageMask); |
| skip |= state.ValidateSignalSemaphore(loc, queue, sem_info.semaphore, sem_info.value, sem_info.deviceIndex); |
| } |
| return skip; |
| } |
| |
| static uint64_t TimelineDiff(uint64_t a, uint64_t b) { return a > b ? a - b : b - a; } |
| |
| bool CoreChecks::ValidateMaxTimelineSemaphoreValueDifference(const Location &loc, const SEMAPHORE_STATE &semaphore_state, |
| uint64_t value) const { |
| using sync_vuid_maps::GetQueueSubmitVUID; |
| using sync_vuid_maps::SubmitError; |
| bool skip = false; |
| |
| if (semaphore_state.type != VK_SEMAPHORE_TYPE_TIMELINE) return false; |
| |
| const auto semaphore = semaphore_state.Handle(); |
| const auto completed = semaphore_state.Completed(); |
| uint64_t diff = TimelineDiff(value, completed.payload); |
| if (diff > phys_dev_props_core12.maxTimelineSemaphoreValueDifference) { |
| const auto &vuid = GetQueueSubmitVUID(loc, SubmitError::kTimelineSemMaxDiff); |
| skip |= |
| LogError(semaphore, vuid, "%s value (%" PRIu64 ") exceeds limit regarding current semaphore %s payload (%" PRIu64 ").", |
| loc.Message().c_str(), value, report_data->FormatHandle(semaphore).c_str(), completed.payload); |
| } else if (semaphore_state.HasPendingOps()) { |
| const auto last_op = semaphore_state.LastOp(); |
| if (last_op && last_op->op_type != SEMAPHORE_STATE::kNone) { |
| diff = TimelineDiff(value, last_op->payload); |
| if (diff > phys_dev_props_core12.maxTimelineSemaphoreValueDifference) { |
| const auto &vuid = GetQueueSubmitVUID(loc, SubmitError::kTimelineSemMaxDiff); |
| skip |= LogError(semaphore, vuid, |
| "%s value (%" PRIu64 ") exceeds limit regarding pending semaphore %s %s value (%" PRIu64 ").", |
| loc.Message().c_str(), value, report_data->FormatHandle(semaphore).c_str(), |
| SEMAPHORE_STATE::OpTypeName(last_op->op_type), last_op->payload); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| struct CommandBufferSubmitState { |
| const CoreChecks *core; |
| const QUEUE_STATE *queue_state; |
| QFOTransferCBScoreboards<QFOImageTransferBarrier> qfo_image_scoreboards; |
| QFOTransferCBScoreboards<QFOBufferTransferBarrier> qfo_buffer_scoreboards; |
| vector<VkCommandBuffer> current_cmds; |
| GlobalImageLayoutMap overlay_image_layout_map; |
| QueryMap local_query_to_state_map; |
| EventToStageMap local_event_to_stage_map; |
| |
| CommandBufferSubmitState(const CoreChecks *c, const char *func, const QUEUE_STATE *q) : core(c), queue_state(q) {} |
| |
| bool Validate(const core_error::Location &loc, const CMD_BUFFER_STATE &cb_node, uint32_t perf_pass) { |
| bool skip = false; |
| skip |= core->ValidateCmdBufImageLayouts(loc, &cb_node, overlay_image_layout_map); |
| auto cmd = cb_node.commandBuffer(); |
| current_cmds.push_back(cmd); |
| skip |= core->ValidatePrimaryCommandBufferState(loc, &cb_node, |
| static_cast<int>(std::count(current_cmds.begin(), current_cmds.end(), cmd)), |
| &qfo_image_scoreboards, &qfo_buffer_scoreboards); |
| skip |= core->ValidateQueueFamilyIndices(loc, &cb_node, queue_state->Queue()); |
| |
| for (const auto &descriptor_set : cb_node.validate_descriptorsets_in_queuesubmit) { |
| auto set_node = core->Get<cvdescriptorset::DescriptorSet>(descriptor_set.first); |
| if (!set_node) { |
| continue; |
| } |
| for (const auto &cmd_info : descriptor_set.second) { |
| std::string function = loc.StringFunc(); |
| function += ", "; |
| function += CommandTypeString(cmd_info.cmd_type); |
| for (const auto &binding_info : cmd_info.binding_infos) { |
| std::string error; |
| std::vector<uint32_t> dynamic_offsets; |
| // dynamic data isn't allowed in UPDATE_AFTER_BIND, so dynamicOffsets is always empty. |
| // This submit time not record time... |
| const bool record_time_validate = false; |
| layer_data::optional<layer_data::unordered_map<VkImageView, VkImageLayout>> checked_layouts; |
| if (set_node->GetTotalDescriptorCount() > cvdescriptorset::PrefilterBindRequestMap::kManyDescriptors_) { |
| checked_layouts.emplace(); |
| } |
| skip |= core->ValidateDescriptorSetBindingData(&cb_node, set_node.get(), dynamic_offsets, binding_info, |
| cmd_info.framebuffer, cmd_info.attachments.get(), |
| cmd_info.subpasses.get(), record_time_validate, function.c_str(), |
| core->GetDrawDispatchVuid(cmd_info.cmd_type), checked_layouts); |
| } |
| } |
| } |
| |
| // Potential early exit here as bad object state may crash in delayed function calls |
| if (skip) { |
| return true; |
| } |
| |
| // Call submit-time functions to validate or update local mirrors of state (to preserve const-ness at validate time) |
| for (auto &function : cb_node.queue_submit_functions) { |
| skip |= function(*core, *queue_state, cb_node); |
| } |
| for (auto &function : cb_node.eventUpdates) { |
| skip |= function(const_cast<CMD_BUFFER_STATE &>(cb_node), /*do_validate*/ true, &local_event_to_stage_map); |
| } |
| VkQueryPool first_perf_query_pool = VK_NULL_HANDLE; |
| for (auto &function : cb_node.queryUpdates) { |
| skip |= function(core, /*do_validate*/ true, first_perf_query_pool, perf_pass, &local_query_to_state_map); |
| } |
| return skip; |
| } |
| }; |
| |
| bool CoreChecks::PreCallValidateQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, |
| VkFence fence) const { |
| auto fence_state = Get<FENCE_STATE>(fence); |
| bool skip = ValidateFenceForSubmit(fence_state.get(), "VUID-vkQueueSubmit-fence-00064", "VUID-vkQueueSubmit-fence-00063", |
| "vkQueueSubmit()"); |
| if (skip) { |
| return true; |
| } |
| auto queue_state = Get<QUEUE_STATE>(queue); |
| CommandBufferSubmitState cb_submit_state(this, "vkQueueSubmit()", queue_state.get()); |
| SemaphoreSubmitState sem_submit_state(this, |
| physical_device_state->queue_family_properties[queue_state->queueFamilyIndex].queueFlags); |
| |
| // Now verify each individual submit |
| for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { |
| const VkSubmitInfo *submit = &pSubmits[submit_idx]; |
| const auto perf_submit = LvlFindInChain<VkPerformanceQuerySubmitInfoKHR>(submit->pNext); |
| uint32_t perf_pass = perf_submit ? perf_submit->counterPassIndex : 0; |
| |
| Location loc(Func::vkQueueSubmit, Struct::VkSubmitInfo, Field::pSubmits, submit_idx); |
| for (uint32_t i = 0; i < submit->commandBufferCount; i++) { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(submit->pCommandBuffers[i]); |
| if (cb_state) { |
| skip |= cb_submit_state.Validate(loc.dot(Field::pCommandBuffers, i), *cb_state, perf_pass); |
| } |
| } |
| skip |= ValidateSemaphoresForSubmit(sem_submit_state, queue, submit, loc); |
| |
| auto chained_device_group_struct = LvlFindInChain<VkDeviceGroupSubmitInfo>(submit->pNext); |
| if (chained_device_group_struct && chained_device_group_struct->commandBufferCount > 0) { |
| for (uint32_t i = 0; i < chained_device_group_struct->commandBufferCount; ++i) { |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(chained_device_group_struct->pCommandBufferDeviceMasks[i], queue, |
| "VUID-VkDeviceGroupSubmitInfo-pCommandBufferDeviceMasks-00086"); |
| } |
| if (chained_device_group_struct->signalSemaphoreCount != submit->signalSemaphoreCount) { |
| skip |= LogError(queue, "VUID-VkDeviceGroupSubmitInfo-signalSemaphoreCount-00084", |
| "pSubmits[%" PRIu32 "] signalSemaphoreCount (%" PRIu32 |
| ") is different than signalSemaphoreCount (%" PRIu32 |
| ") of the VkDeviceGroupSubmitInfo in its pNext chain", |
| submit_idx, submit->signalSemaphoreCount, chained_device_group_struct->signalSemaphoreCount); |
| } |
| if (chained_device_group_struct->waitSemaphoreCount != submit->waitSemaphoreCount) { |
| skip |= |
| LogError(queue, "VUID-VkDeviceGroupSubmitInfo-waitSemaphoreCount-00082", |
| "pSubmits[%" PRIu32 "] waitSemaphoreCount (%" PRIu32 ") is different than waitSemaphoreCount (%" PRIu32 |
| ") of the VkDeviceGroupSubmitInfo in its pNext chain", |
| submit_idx, submit->waitSemaphoreCount, chained_device_group_struct->waitSemaphoreCount); |
| } |
| if (chained_device_group_struct->commandBufferCount != submit->commandBufferCount) { |
| skip |= |
| LogError(queue, "VUID-VkDeviceGroupSubmitInfo-commandBufferCount-00083", |
| "pSubmits[%" PRIu32 "] commandBufferCount (%" PRIu32 ") is different than commandBufferCount (%" PRIu32 |
| ") of the VkDeviceGroupSubmitInfo in its pNext chain", |
| submit_idx, submit->commandBufferCount, chained_device_group_struct->commandBufferCount); |
| } |
| } |
| |
| auto protected_submit_info = LvlFindInChain<VkProtectedSubmitInfo>(submit->pNext); |
| if (protected_submit_info) { |
| const bool protected_submit = protected_submit_info->protectedSubmit == VK_TRUE; |
| if ((protected_submit == true) && ((queue_state->flags & VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT) == 0)) { |
| skip |= LogError(queue, "VUID-vkQueueSubmit-queue-06448", |
| "vkQueueSubmit(): pSubmits[%u] contains a protected submission to %s which was not created with " |
| "VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT", |
| submit_idx, report_data->FormatHandle(queue).c_str()); |
| } |
| |
| // Make sure command buffers are all protected or unprotected |
| for (uint32_t i = 0; i < submit->commandBufferCount; i++) { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(submit->pCommandBuffers[i]); |
| if (cb_state) { |
| if ((cb_state->unprotected == true) && (protected_submit == true)) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(queue); |
| skip |= LogError(objlist, "VUID-VkSubmitInfo-pNext-04148", |
| "vkQueueSubmit(): command buffer %s is unprotected while queue %s pSubmits[%u] has " |
| "VkProtectedSubmitInfo:protectedSubmit set to VK_TRUE", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(queue).c_str(), submit_idx); |
| } |
| if ((cb_state->unprotected == false) && (protected_submit == false)) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(queue); |
| skip |= LogError(objlist, "VUID-VkSubmitInfo-pNext-04120", |
| "vkQueueSubmit(): command buffer %s is protected while queue %s pSubmits[%u] has " |
| "VkProtectedSubmitInfo:protectedSubmit set to VK_FALSE", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(queue).c_str(), submit_idx); |
| } |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits, |
| VkFence fence, bool is_2khr) const { |
| auto pFence = Get<FENCE_STATE>(fence); |
| const char* func_name = is_2khr ? "vkQueueSubmit2KHR()" : "vkQueueSubmit2()"; |
| bool skip = ValidateFenceForSubmit(pFence.get(), "VUID-vkQueueSubmit2-fence-04895", "VUID-vkQueueSubmit2-fence-04894", |
| func_name); |
| if (skip) { |
| return true; |
| } |
| |
| if (!enabled_features.core13.synchronization2) { |
| skip |= LogError(queue, "VUID-vkQueueSubmit2-synchronization2-03866", |
| "%s: Synchronization2 feature is not enabled", func_name); |
| } |
| |
| auto queue_state = Get<QUEUE_STATE>(queue); |
| CommandBufferSubmitState cb_submit_state(this, func_name, queue_state.get()); |
| SemaphoreSubmitState sem_submit_state(this, |
| physical_device_state->queue_family_properties[queue_state->queueFamilyIndex].queueFlags); |
| |
| // Now verify each individual submit |
| for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { |
| const VkSubmitInfo2KHR *submit = &pSubmits[submit_idx]; |
| const auto perf_submit = LvlFindInChain<VkPerformanceQuerySubmitInfoKHR>(submit->pNext); |
| uint32_t perf_pass = perf_submit ? perf_submit->counterPassIndex : 0; |
| Location loc(Func::vkQueueSubmit2, Struct::VkSubmitInfo2, Field::pSubmits, submit_idx); |
| |
| skip |= ValidateSemaphoresForSubmit(sem_submit_state, queue, submit, loc); |
| |
| bool protected_submit = (submit->flags & VK_SUBMIT_PROTECTED_BIT_KHR) != 0; |
| if ((protected_submit == true) && ((queue_state->flags & VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT)) == 0) { |
| skip |= LogError(queue, "VUID-vkQueueSubmit2-queue-06447", |
| "%s: pSubmits[%u] contains a protected submission to %s which was not created with " |
| "VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT", |
| func_name, submit_idx, report_data->FormatHandle(queue).c_str()); |
| } |
| |
| for (uint32_t i = 0; i < submit->commandBufferInfoCount; i++) { |
| auto info_loc = loc.dot(Field::pCommandBufferInfos, i); |
| info_loc.structure = Struct::VkCommandBufferSubmitInfo; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(submit->pCommandBufferInfos[i].commandBuffer); |
| skip |= cb_submit_state.Validate(info_loc.dot(Field::commandBuffer), *cb_state, perf_pass); |
| |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(submit->pCommandBufferInfos[i].deviceMask, queue, |
| "VUID-VkCommandBufferSubmitInfo-deviceMask-03891"); |
| |
| // Make sure command buffers are all protected or unprotected |
| if (cb_state != nullptr) { |
| if ((cb_state->unprotected == true) && (protected_submit == true)) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(queue); |
| skip |= LogError(objlist, "VUID-VkSubmitInfo2-flags-03886", |
| "%s: command buffer %s is unprotected while queue %s pSubmits[%u] has " |
| "VK_SUBMIT_PROTECTED_BIT_KHR set", |
| func_name, report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(queue).c_str(), submit_idx); |
| } |
| if ((cb_state->unprotected == false) && (protected_submit == false)) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(queue); |
| skip |= LogError(objlist, "VUID-VkSubmitInfo2-flags-03887", |
| "%s: command buffer %s is protected while queue %s pSubmitInfos[%u] has " |
| "VK_SUBMIT_PROTECTED_BIT_KHR not set", func_name, |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(queue).c_str(), submit_idx); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR* pSubmits, |
| VkFence fence) const { |
| return ValidateQueueSubmit2(queue, submitCount, pSubmits, fence, true); |
| } |
| |
| bool CoreChecks::PreCallValidateQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, |
| VkFence fence) const { |
| return ValidateQueueSubmit2(queue, submitCount, pSubmits, fence, false); |
| } |
| |
| #ifdef AHB_VALIDATION_SUPPORT |
| // Android-specific validation that uses types defined only on Android and only for NDK versions |
| // that support the VK_ANDROID_external_memory_android_hardware_buffer extension. |
| // This chunk could move into a seperate core_validation_android.cpp file... ? |
| |
| // clang-format off |
| |
| // Map external format and usage flags to/from equivalent Vulkan flags |
| // (Tables as of v1.1.92) |
| |
| // AHardwareBuffer Format Vulkan Format |
| // ====================== ============= |
| // AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM VK_FORMAT_R8G8B8A8_UNORM |
| // AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM VK_FORMAT_R8G8B8A8_UNORM |
| // AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM VK_FORMAT_R8G8B8_UNORM |
| // AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM VK_FORMAT_R5G6B5_UNORM_PACK16 |
| // AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT VK_FORMAT_R16G16B16A16_SFLOAT |
| // AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM VK_FORMAT_A2B10G10R10_UNORM_PACK32 |
| // AHARDWAREBUFFER_FORMAT_D16_UNORM VK_FORMAT_D16_UNORM |
| // AHARDWAREBUFFER_FORMAT_D24_UNORM VK_FORMAT_X8_D24_UNORM_PACK32 |
| // AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT VK_FORMAT_D24_UNORM_S8_UINT |
| // AHARDWAREBUFFER_FORMAT_D32_FLOAT VK_FORMAT_D32_SFLOAT |
| // AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT VK_FORMAT_D32_SFLOAT_S8_UINT |
| // AHARDWAREBUFFER_FORMAT_S8_UINT VK_FORMAT_S8_UINT |
| |
| // The AHARDWAREBUFFER_FORMAT_* are an enum in the NDK headers, but get passed in to Vulkan |
| // as uint32_t. Casting the enums here avoids scattering casts around in the code. |
| std::map<uint32_t, VkFormat> ahb_format_map_a2v = { |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, VK_FORMAT_R8G8B8A8_UNORM }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8_UNORM }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM, VK_FORMAT_R5G6B5_UNORM_PACK16 }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT, VK_FORMAT_R16G16B16A16_SFLOAT }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM, VK_FORMAT_A2B10G10R10_UNORM_PACK32 }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_D16_UNORM, VK_FORMAT_D16_UNORM }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_D24_UNORM, VK_FORMAT_X8_D24_UNORM_PACK32 }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_D32_FLOAT, VK_FORMAT_D32_SFLOAT }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT }, |
| { (uint32_t)AHARDWAREBUFFER_FORMAT_S8_UINT, VK_FORMAT_S8_UINT } |
| }; |
| |
| // AHardwareBuffer Usage Vulkan Usage or Creation Flag (Intermixed - Aargh!) |
| // ===================== =================================================== |
| // None VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
| // None VK_IMAGE_USAGE_TRANSFER_DST_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE VK_IMAGE_USAGE_SAMPLED_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE None |
| // AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT VK_IMAGE_CREATE_PROTECTED_BIT |
| // None VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT |
| // None VK_IMAGE_CREATE_EXTENDED_USAGE_BIT |
| // AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER VK_IMAGE_USAGE_STORAGE_BIT |
| |
| // Same casting rationale. De-mixing the table to prevent type confusion and aliasing |
| std::map<uint64_t, VkImageUsageFlags> ahb_usage_map_a2v = { |
| { (uint64_t)AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) }, |
| { (uint64_t)AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) }, |
| { (uint64_t)AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE, 0 }, // No equivalent |
| { (uint64_t)AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER, VK_IMAGE_USAGE_STORAGE_BIT }, |
| }; |
| |
| std::map<uint64_t, VkImageCreateFlags> ahb_create_map_a2v = { |
| { (uint64_t)AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT }, |
| { (uint64_t)AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT, VK_IMAGE_CREATE_PROTECTED_BIT }, |
| { (uint64_t)AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE, 0 }, // No equivalent |
| }; |
| |
| std::map<VkImageUsageFlags, uint64_t> ahb_usage_map_v2a = { |
| { VK_IMAGE_USAGE_SAMPLED_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE }, |
| { VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE }, |
| { VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER }, |
| { VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER }, |
| { VK_IMAGE_USAGE_STORAGE_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER }, |
| }; |
| |
| std::map<VkImageCreateFlags, uint64_t> ahb_create_map_v2a = { |
| { VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP }, |
| { VK_IMAGE_CREATE_PROTECTED_BIT, (uint64_t)AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT }, |
| }; |
| |
| // clang-format on |
| |
| // |
| // AHB-extension new APIs |
| // |
| bool CoreChecks::PreCallValidateGetAndroidHardwareBufferPropertiesANDROID( |
| VkDevice device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties) const { |
| bool skip = false; |
| // buffer must be a valid Android hardware buffer object with at least one of the AHARDWAREBUFFER_USAGE_GPU_* usage flags. |
| AHardwareBuffer_Desc ahb_desc; |
| AHardwareBuffer_describe(buffer, &ahb_desc); |
| uint32_t required_flags = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | |
| AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP | AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | |
| AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; |
| if (0 == (ahb_desc.usage & required_flags)) { |
| skip |= LogError(device, "VUID-vkGetAndroidHardwareBufferPropertiesANDROID-buffer-01884", |
| "vkGetAndroidHardwareBufferPropertiesANDROID: The AHardwareBuffer's AHardwareBuffer_Desc.usage (0x%" PRIx64 |
| ") does not have any AHARDWAREBUFFER_USAGE_GPU_* flags set.", |
| ahb_desc.usage); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetMemoryAndroidHardwareBufferANDROID(VkDevice device, |
| const VkMemoryGetAndroidHardwareBufferInfoANDROID *pInfo, |
| struct AHardwareBuffer **pBuffer) const { |
| bool skip = false; |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(pInfo->memory); |
| |
| // VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID must have been included in |
| // VkExportMemoryAllocateInfo::handleTypes when memory was created. |
| if (!mem_info->IsExport() || |
| (0 == (mem_info->export_handle_type_flags & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID))) { |
| skip |= LogError(device, "VUID-VkMemoryGetAndroidHardwareBufferInfoANDROID-handleTypes-01882", |
| "vkGetMemoryAndroidHardwareBufferANDROID: %s was not allocated for export, or the " |
| "export handleTypes (0x%" PRIx32 |
| ") did not contain VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID.", |
| report_data->FormatHandle(pInfo->memory).c_str(), mem_info->export_handle_type_flags); |
| } |
| |
| // If the pNext chain of the VkMemoryAllocateInfo used to allocate memory included a VkMemoryDedicatedAllocateInfo |
| // with non-NULL image member, then that image must already be bound to memory. |
| if (mem_info->IsDedicatedImage()) { |
| auto image_state = Get<IMAGE_STATE>(mem_info->dedicated->handle.Cast<VkImage>()); |
| if ((nullptr == image_state) || (0 == (image_state->GetBoundMemory().count(mem_info->mem())))) { |
| LogObjectList objlist(device); |
| objlist.add(pInfo->memory); |
| objlist.add(mem_info->dedicated->handle); |
| skip |= LogError(objlist, "VUID-VkMemoryGetAndroidHardwareBufferInfoANDROID-pNext-01883", |
| "vkGetMemoryAndroidHardwareBufferANDROID: %s was allocated using a dedicated " |
| "%s, but that image is not bound to the VkDeviceMemory object.", |
| report_data->FormatHandle(pInfo->memory).c_str(), |
| report_data->FormatHandle(mem_info->dedicated->handle).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| |
| // |
| // AHB-specific validation within non-AHB APIs |
| // |
| bool CoreChecks::ValidateAllocateMemoryANDROID(const VkMemoryAllocateInfo *alloc_info) const { |
| bool skip = false; |
| auto import_ahb_info = LvlFindInChain<VkImportAndroidHardwareBufferInfoANDROID>(alloc_info->pNext); |
| auto exp_mem_alloc_info = LvlFindInChain<VkExportMemoryAllocateInfo>(alloc_info->pNext); |
| auto mem_ded_alloc_info = LvlFindInChain<VkMemoryDedicatedAllocateInfo>(alloc_info->pNext); |
| |
| if ((import_ahb_info) && (NULL != import_ahb_info->buffer)) { |
| // This is an import with handleType of VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID |
| AHardwareBuffer_Desc ahb_desc = {}; |
| AHardwareBuffer_describe(import_ahb_info->buffer, &ahb_desc); |
| |
| // Validate AHardwareBuffer_Desc::usage is a valid usage for imported AHB |
| // |
| // BLOB & GPU_DATA_BUFFER combo specifically allowed |
| if ((AHARDWAREBUFFER_FORMAT_BLOB != ahb_desc.format) || (0 == (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER))) { |
| // Otherwise, must be a combination from the AHardwareBuffer Format and Usage Equivalence tables |
| // Usage must have at least one bit from the table. It may have additional bits not in the table |
| uint64_t ahb_equiv_usage_bits = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | |
| AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP | AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | |
| AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT; |
| if (0 == (ahb_desc.usage & ahb_equiv_usage_bits)) { |
| skip |= |
| LogError(device, "VUID-VkImportAndroidHardwareBufferInfoANDROID-buffer-01881", |
| "vkAllocateMemory: The AHardwareBuffer_Desc's usage (0x%" PRIx64 ") is not compatible with Vulkan.", |
| ahb_desc.usage); |
| } |
| } |
| |
| // Collect external buffer info |
| auto pdebi = LvlInitStruct<VkPhysicalDeviceExternalBufferInfo>(); |
| pdebi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| if (AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE & ahb_desc.usage) { |
| pdebi.usage |= ahb_usage_map_a2v[AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE]; |
| } |
| if (AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER & ahb_desc.usage) { |
| pdebi.usage |= ahb_usage_map_a2v[AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER]; |
| } |
| auto ext_buf_props = LvlInitStruct<VkExternalBufferProperties>(); |
| DispatchGetPhysicalDeviceExternalBufferProperties(physical_device, &pdebi, &ext_buf_props); |
| |
| // If buffer is not NULL, Android hardware buffers must be supported for import, as reported by |
| // VkExternalImageFormatProperties or VkExternalBufferProperties. |
| if (0 == (ext_buf_props.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) { |
| // Collect external format info |
| auto pdeifi = LvlInitStruct<VkPhysicalDeviceExternalImageFormatInfo>(); |
| pdeifi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| auto pdifi2 = LvlInitStruct<VkPhysicalDeviceImageFormatInfo2>(&pdeifi); |
| if (0 < ahb_format_map_a2v.count(ahb_desc.format)) pdifi2.format = ahb_format_map_a2v[ahb_desc.format]; |
| pdifi2.type = VK_IMAGE_TYPE_2D; // Seems likely |
| pdifi2.tiling = VK_IMAGE_TILING_OPTIMAL; // Ditto |
| if (AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE & ahb_desc.usage) { |
| pdifi2.usage |= ahb_usage_map_a2v[AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE]; |
| } |
| if (AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER & ahb_desc.usage) { |
| pdifi2.usage |= ahb_usage_map_a2v[AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER]; |
| } |
| if (AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP & ahb_desc.usage) { |
| pdifi2.flags |= ahb_create_map_a2v[AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP]; |
| } |
| if (AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT & ahb_desc.usage) { |
| pdifi2.flags |= ahb_create_map_a2v[AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT]; |
| } |
| |
| auto ext_img_fmt_props = LvlInitStruct<VkExternalImageFormatProperties>(); |
| auto ifp2 = LvlInitStruct<VkImageFormatProperties2>(&ext_img_fmt_props); |
| |
| VkResult fmt_lookup_result = DispatchGetPhysicalDeviceImageFormatProperties2(physical_device, &pdifi2, &ifp2); |
| |
| if ((VK_SUCCESS != fmt_lookup_result) || (0 == (ext_img_fmt_props.externalMemoryProperties.externalMemoryFeatures & |
| VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT))) { |
| skip |= LogError(device, "VUID-VkImportAndroidHardwareBufferInfoANDROID-buffer-01880", |
| "vkAllocateMemory: Neither the VkExternalImageFormatProperties nor the VkExternalBufferProperties " |
| "structs for the AHardwareBuffer include the VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT flag."); |
| } |
| } |
| |
| // Retrieve buffer and format properties of the provided AHardwareBuffer |
| auto ahb_format_props = LvlInitStruct<VkAndroidHardwareBufferFormatPropertiesANDROID>(); |
| auto ahb_props = LvlInitStruct<VkAndroidHardwareBufferPropertiesANDROID>(&ahb_format_props); |
| DispatchGetAndroidHardwareBufferPropertiesANDROID(device, import_ahb_info->buffer, &ahb_props); |
| |
| // allocationSize must be the size returned by vkGetAndroidHardwareBufferPropertiesANDROID for the Android hardware buffer |
| if (alloc_info->allocationSize != ahb_props.allocationSize) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-allocationSize-02383", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID " |
| "struct, allocationSize (%" PRId64 |
| ") does not match the AHardwareBuffer's reported allocationSize (%" PRId64 ").", |
| alloc_info->allocationSize, ahb_props.allocationSize); |
| } |
| |
| // memoryTypeIndex must be one of those returned by vkGetAndroidHardwareBufferPropertiesANDROID for the AHardwareBuffer |
| // Note: memoryTypeIndex is an index, memoryTypeBits is a bitmask |
| uint32_t mem_type_bitmask = 1 << alloc_info->memoryTypeIndex; |
| if (0 == (mem_type_bitmask & ahb_props.memoryTypeBits)) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-memoryTypeIndex-02385", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID " |
| "struct, memoryTypeIndex (%" PRId32 |
| ") does not correspond to a bit set in AHardwareBuffer's reported " |
| "memoryTypeBits bitmask (0x%" PRIx32 ").", |
| alloc_info->memoryTypeIndex, ahb_props.memoryTypeBits); |
| } |
| |
| // Checks for allocations without a dedicated allocation requirement |
| if ((nullptr == mem_ded_alloc_info) || (VK_NULL_HANDLE == mem_ded_alloc_info->image)) { |
| // the Android hardware buffer must have a format of AHARDWAREBUFFER_FORMAT_BLOB and a usage that includes |
| // AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER |
| if (((uint64_t)AHARDWAREBUFFER_FORMAT_BLOB != ahb_desc.format) || |
| (0 == (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER))) { |
| skip |= LogError( |
| device, "VUID-VkMemoryAllocateInfo-pNext-02384", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID " |
| "struct without a dedicated allocation requirement, while the AHardwareBuffer_Desc's format ( %u ) is not " |
| "AHARDWAREBUFFER_FORMAT_BLOB or usage (0x%" PRIx64 ") does not include AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER.", |
| ahb_desc.format, ahb_desc.usage); |
| } |
| } else { // Checks specific to import with a dedicated allocation requirement |
| auto image_state = Get<IMAGE_STATE>(mem_ded_alloc_info->image); |
| const auto *ici = &image_state->createInfo; |
| |
| // Dedicated allocation have limit usage flags supported |
| if (0 == (ahb_desc.usage & (AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | |
| AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER))) { |
| skip |= LogError( |
| device, "VUID-VkMemoryAllocateInfo-pNext-02386", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID and a " |
| "dedicated allocation requirement, while the AHardwareBuffer's usage (0x%" PRIx64 |
| ") contains neither AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER, nor " |
| "AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE.", |
| ahb_desc.usage); |
| } |
| |
| // the format of image must be VK_FORMAT_UNDEFINED or the format returned by |
| // vkGetAndroidHardwareBufferPropertiesANDROID |
| if ((ici->format != ahb_format_props.format) && (VK_FORMAT_UNDEFINED != ici->format)) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-pNext-02387", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained " |
| "VkImportAndroidHardwareBufferInfoANDROID, the dedicated allocation image's " |
| "format (%s) is not VK_FORMAT_UNDEFINED and does not match the AHardwareBuffer's format (%s).", |
| string_VkFormat(ici->format), string_VkFormat(ahb_format_props.format)); |
| } |
| |
| // The width, height, and array layer dimensions of image and the Android hardwarebuffer must be identical |
| if ((ici->extent.width != ahb_desc.width) || (ici->extent.height != ahb_desc.height) || |
| (ici->arrayLayers != ahb_desc.layers)) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-pNext-02388", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained " |
| "VkImportAndroidHardwareBufferInfoANDROID, the dedicated allocation image's " |
| "width, height, and arrayLayers (%" PRId32 " %" PRId32 " %" PRId32 |
| ") do not match those of the AHardwareBuffer (%" PRId32 " %" PRId32 " %" PRId32 ").", |
| ici->extent.width, ici->extent.height, ici->arrayLayers, ahb_desc.width, ahb_desc.height, |
| ahb_desc.layers); |
| } |
| |
| // If the Android hardware buffer's usage includes AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE, the image must |
| // have either a full mipmap chain or exactly 1 mip level. |
| // |
| // NOTE! The language of this VUID contradicts the language in the spec (1.1.93), which says "The |
| // AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE flag does not correspond to a Vulkan image usage or creation flag. Instead, |
| // its presence indicates that the Android hardware buffer contains a complete mipmap chain, and its absence indicates |
| // that the Android hardware buffer contains only a single mip level." |
| // |
| // TODO: This code implements the VUID's meaning, but it seems likely that the spec text is actually correct. |
| // Clarification requested. |
| if ((ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE) && (ici->mipLevels != 1) && |
| (ici->mipLevels != FullMipChainLevels(ici->extent))) { |
| skip |= |
| LogError(device, "VUID-VkMemoryAllocateInfo-pNext-02389", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID, " |
| "usage includes AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE but mipLevels (%" PRId32 |
| ") is neither 1 nor full mip " |
| "chain levels (%" PRId32 ").", |
| ici->mipLevels, FullMipChainLevels(ici->extent)); |
| } |
| |
| // each bit set in the usage of image must be listed in AHardwareBuffer Usage Equivalence, and if there is a |
| // corresponding AHARDWAREBUFFER_USAGE bit listed that bit must be included in the Android hardware buffer's |
| // AHardwareBuffer_Desc::usage |
| if (ici->usage & ~(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { |
| skip |= |
| LogError(device, "VUID-VkMemoryAllocateInfo-pNext-02390", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID, " |
| "dedicated image usage bits (0x%" PRIx32 |
| ") include an issue not listed in the AHardwareBuffer Usage Equivalence table.", |
| ici->usage); |
| } |
| |
| std::vector<VkImageUsageFlags> usages = {VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT}; |
| for (VkImageUsageFlags ubit : usages) { |
| if (ici->usage & ubit) { |
| uint64_t ahb_usage = ahb_usage_map_v2a[ubit]; |
| if (0 == (ahb_usage & ahb_desc.usage)) { |
| skip |= LogError( |
| device, "VUID-VkMemoryAllocateInfo-pNext-02390", |
| "vkAllocateMemory: VkMemoryAllocateInfo struct with chained VkImportAndroidHardwareBufferInfoANDROID, " |
| "The dedicated image usage bit %s equivalent is not in AHardwareBuffer_Desc.usage (0x%" PRIx64 ") ", |
| string_VkImageUsageFlags(ubit).c_str(), ahb_desc.usage); |
| } |
| } |
| } |
| } |
| } else { // Not an import |
| if ((exp_mem_alloc_info) && (mem_ded_alloc_info) && |
| (0 != (VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID & exp_mem_alloc_info->handleTypes)) && |
| (VK_NULL_HANDLE != mem_ded_alloc_info->image)) { |
| // This is an Android HW Buffer export |
| if (0 != alloc_info->allocationSize) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-pNext-01874", |
| "vkAllocateMemory: pNext chain indicates a dedicated Android Hardware Buffer export allocation, " |
| "but allocationSize is non-zero."); |
| } |
| } else { |
| if (0 == alloc_info->allocationSize) { |
| skip |= LogError( |
| device, "VUID-VkMemoryAllocateInfo-pNext-01874", |
| "vkAllocateMemory: pNext chain does not indicate a dedicated export allocation, but allocationSize is 0."); |
| }; |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGetImageMemoryRequirementsANDROID(const VkImage image, const char *func_name) const { |
| bool skip = false; |
| |
| auto image_state = Get<IMAGE_STATE>(image); |
| if (image_state != nullptr) { |
| if (image_state->IsExternalAHB() && (0 == image_state->GetBoundMemory().size())) { |
| const char *vuid = strcmp(func_name, "vkGetImageMemoryRequirements()") == 0 |
| ? "VUID-vkGetImageMemoryRequirements-image-04004" |
| : "VUID-VkImageMemoryRequirementsInfo2-image-01897"; |
| skip |= |
| LogError(image, vuid, |
| "%s: Attempt get image memory requirements for an image created with a " |
| "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID handleType, which has not yet been " |
| "bound to memory.", |
| func_name); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGetPhysicalDeviceImageFormatProperties2ANDROID( |
| const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, const VkImageFormatProperties2 *pImageFormatProperties) const { |
| bool skip = false; |
| const VkAndroidHardwareBufferUsageANDROID *ahb_usage = |
| LvlFindInChain<VkAndroidHardwareBufferUsageANDROID>(pImageFormatProperties->pNext); |
| if (nullptr != ahb_usage) { |
| const VkPhysicalDeviceExternalImageFormatInfo *pdeifi = |
| LvlFindInChain<VkPhysicalDeviceExternalImageFormatInfo>(pImageFormatInfo->pNext); |
| if ((nullptr == pdeifi) || (VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID != pdeifi->handleType)) { |
| skip |= LogError(device, "VUID-vkGetPhysicalDeviceImageFormatProperties2-pNext-01868", |
| "vkGetPhysicalDeviceImageFormatProperties2: pImageFormatProperties includes a chained " |
| "VkAndroidHardwareBufferUsageANDROID struct, but pImageFormatInfo does not include a chained " |
| "VkPhysicalDeviceExternalImageFormatInfo struct with handleType " |
| "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID."); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateBufferImportedHandleANDROID(const char *func_name, VkExternalMemoryHandleTypeFlags handleType, |
| VkDeviceMemory memory, VkBuffer buffer) const { |
| bool skip = false; |
| if ((handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) == 0) { |
| const char *vuid = (strcmp(func_name, "vkBindBufferMemory()") == 0) ? "VUID-vkBindBufferMemory-memory-02986" |
| : "VUID-VkBindBufferMemoryInfo-memory-02986"; |
| LogObjectList objlist(buffer); |
| objlist.add(memory); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was created with an AHB import operation which is not set " |
| "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID in the VkBuffer (%s) " |
| "VkExternalMemoryBufferreateInfo::handleType (%s)", |
| func_name, report_data->FormatHandle(memory).c_str(), report_data->FormatHandle(buffer).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(handleType).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateImageImportedHandleANDROID(const char *func_name, VkExternalMemoryHandleTypeFlags handleType, |
| VkDeviceMemory memory, VkImage image) const { |
| bool skip = false; |
| if ((handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) == 0) { |
| const char *vuid = (strcmp(func_name, "vkBindImageMemory()") == 0) ? "VUID-vkBindImageMemory-memory-02990" |
| : "VUID-VkBindImageMemoryInfo-memory-02990"; |
| LogObjectList objlist(image); |
| objlist.add(memory); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was created with an AHB import operation which is not set " |
| "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID in the VkImage (%s) " |
| "VkExternalMemoryImageCreateInfo::handleType (%s)", |
| func_name, report_data->FormatHandle(memory).c_str(), report_data->FormatHandle(image).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(handleType).c_str()); |
| } |
| return skip; |
| } |
| |
| #else // !AHB_VALIDATION_SUPPORT |
| |
| // Case building for Android without AHB Validation |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| bool CoreChecks::PreCallValidateGetAndroidHardwareBufferPropertiesANDROID( |
| VkDevice device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties) const { |
| return false; |
| } |
| bool CoreChecks::PreCallValidateGetMemoryAndroidHardwareBufferANDROID(VkDevice device, |
| const VkMemoryGetAndroidHardwareBufferInfoANDROID *pInfo, |
| struct AHardwareBuffer **pBuffer) const { |
| return false; |
| } |
| #endif // VK_USE_PLATFORM_ANDROID_KHR |
| |
| bool CoreChecks::ValidateAllocateMemoryANDROID(const VkMemoryAllocateInfo *alloc_info) const { return false; } |
| |
| bool CoreChecks::ValidateGetPhysicalDeviceImageFormatProperties2ANDROID( |
| const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, const VkImageFormatProperties2 *pImageFormatProperties) const { |
| return false; |
| } |
| |
| bool CoreChecks::ValidateGetImageMemoryRequirementsANDROID(const VkImage image, const char *func_name) const { return false; } |
| |
| bool CoreChecks::ValidateBufferImportedHandleANDROID(const char *func_name, VkExternalMemoryHandleTypeFlags handleType, |
| VkDeviceMemory memory, VkBuffer buffer) const { |
| return false; |
| } |
| |
| bool CoreChecks::ValidateImageImportedHandleANDROID(const char *func_name, VkExternalMemoryHandleTypeFlags handleType, |
| VkDeviceMemory memory, VkImage image) const { |
| return false; |
| } |
| |
| #endif // AHB_VALIDATION_SUPPORT |
| |
| bool CoreChecks::PreCallValidateAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, |
| const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory) const { |
| bool skip = false; |
| if (Count<DEVICE_MEMORY_STATE>() >= phys_dev_props.limits.maxMemoryAllocationCount) { |
| skip |= LogError(device, "VUID-vkAllocateMemory-maxMemoryAllocationCount-04101", |
| "vkAllocateMemory: Number of currently valid memory objects is not less than the maximum allowed (%u).", |
| phys_dev_props.limits.maxMemoryAllocationCount); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| skip |= ValidateAllocateMemoryANDROID(pAllocateInfo); |
| } else { |
| if (0 == pAllocateInfo->allocationSize) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-allocationSize-00638", "vkAllocateMemory: allocationSize is 0."); |
| }; |
| } |
| |
| auto chained_flags_struct = LvlFindInChain<VkMemoryAllocateFlagsInfo>(pAllocateInfo->pNext); |
| if (chained_flags_struct && chained_flags_struct->flags == VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT) { |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(chained_flags_struct->deviceMask, device, |
| "VUID-VkMemoryAllocateFlagsInfo-deviceMask-00675"); |
| skip |= |
| ValidateDeviceMaskToZero(chained_flags_struct->deviceMask, device, "VUID-VkMemoryAllocateFlagsInfo-deviceMask-00676"); |
| } |
| |
| if (pAllocateInfo->memoryTypeIndex >= phys_dev_mem_props.memoryTypeCount) { |
| skip |= LogError(device, "VUID-vkAllocateMemory-pAllocateInfo-01714", |
| "vkAllocateMemory: attempting to allocate memory type %u, which is not a valid index. Device only " |
| "advertises %u memory types.", |
| pAllocateInfo->memoryTypeIndex, phys_dev_mem_props.memoryTypeCount); |
| } else { |
| const VkMemoryType memory_type = phys_dev_mem_props.memoryTypes[pAllocateInfo->memoryTypeIndex]; |
| if (pAllocateInfo->allocationSize > phys_dev_mem_props.memoryHeaps[memory_type.heapIndex].size) { |
| skip |= LogError(device, "VUID-vkAllocateMemory-pAllocateInfo-01713", |
| "vkAllocateMemory: attempting to allocate %" PRIu64 |
| " bytes from heap %u," |
| "but size of that heap is only %" PRIu64 " bytes.", |
| pAllocateInfo->allocationSize, memory_type.heapIndex, |
| phys_dev_mem_props.memoryHeaps[memory_type.heapIndex].size); |
| } |
| |
| if (!enabled_features.device_coherent_memory_features.deviceCoherentMemory && |
| ((memory_type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) != 0)) { |
| skip |= LogError(device, "VUID-vkAllocateMemory-deviceCoherentMemory-02790", |
| "vkAllocateMemory: attempting to allocate memory type %u, which includes the " |
| "VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD memory property, but the deviceCoherentMemory feature " |
| "is not enabled.", |
| pAllocateInfo->memoryTypeIndex); |
| } |
| |
| if ((enabled_features.core11.protectedMemory == VK_FALSE) && |
| ((memory_type.propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)) { |
| skip |= LogError(device, "VUID-VkMemoryAllocateInfo-memoryTypeIndex-01872", |
| "vkAllocateMemory(): attempting to allocate memory type %u, which includes the " |
| "VK_MEMORY_PROPERTY_PROTECTED_BIT memory property, but the protectedMemory feature " |
| "is not enabled.", |
| pAllocateInfo->memoryTypeIndex); |
| } |
| } |
| |
| bool imported_ahb = false; |
| #ifdef AHB_VALIDATION_SUPPORT |
| // "memory is not an imported Android Hardware Buffer" refers to VkImportAndroidHardwareBufferInfoANDROID with a non-NULL |
| // buffer value. Memory imported has another VUID to check size and allocationSize match up |
| auto imported_ahb_info = LvlFindInChain<VkImportAndroidHardwareBufferInfoANDROID>(pAllocateInfo->pNext); |
| if (imported_ahb_info != nullptr) { |
| imported_ahb = imported_ahb_info->buffer != nullptr; |
| } |
| #endif // AHB_VALIDATION_SUPPORT |
| auto dedicated_allocate_info = LvlFindInChain<VkMemoryDedicatedAllocateInfo>(pAllocateInfo->pNext); |
| if (dedicated_allocate_info) { |
| if ((dedicated_allocate_info->buffer != VK_NULL_HANDLE) && (dedicated_allocate_info->image != VK_NULL_HANDLE)) { |
| skip |= LogError(device, "VUID-VkMemoryDedicatedAllocateInfo-image-01432", |
| "vkAllocateMemory: Either buffer or image has to be VK_NULL_HANDLE in VkMemoryDedicatedAllocateInfo"); |
| } else if (dedicated_allocate_info->image != VK_NULL_HANDLE) { |
| // Dedicated VkImage |
| auto image_state = Get<IMAGE_STATE>(dedicated_allocate_info->image); |
| if (image_state->disjoint == true) { |
| skip |= LogError( |
| device, "VUID-VkMemoryDedicatedAllocateInfo-image-01797", |
| "vkAllocateMemory: VkImage %s can't be used in VkMemoryDedicatedAllocateInfo because it was created with " |
| "VK_IMAGE_CREATE_DISJOINT_BIT", |
| report_data->FormatHandle(dedicated_allocate_info->image).c_str()); |
| } else { |
| if ((pAllocateInfo->allocationSize != image_state->requirements[0].size) && (imported_ahb == false)) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer) |
| ? "VUID-VkMemoryDedicatedAllocateInfo-image-02964" |
| : "VUID-VkMemoryDedicatedAllocateInfo-image-01433"; |
| skip |= |
| LogError(device, vuid, |
| "vkAllocateMemory: Allocation Size (%" PRIu64 |
| ") needs to be equal to VkImage %s VkMemoryRequirements::size (%" PRIu64 ")", |
| pAllocateInfo->allocationSize, report_data->FormatHandle(dedicated_allocate_info->image).c_str(), |
| image_state->requirements[0].size); |
| } |
| if ((image_state->createInfo.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0) { |
| skip |= LogError( |
| device, "VUID-VkMemoryDedicatedAllocateInfo-image-01434", |
| "vkAllocateMemory: VkImage %s can't be used in VkMemoryDedicatedAllocateInfo because it was created with " |
| "VK_IMAGE_CREATE_SPARSE_BINDING_BIT", |
| report_data->FormatHandle(dedicated_allocate_info->image).c_str()); |
| } |
| } |
| } else if (dedicated_allocate_info->buffer != VK_NULL_HANDLE) { |
| // Dedicated VkBuffer |
| auto buffer_state = Get<BUFFER_STATE>(dedicated_allocate_info->buffer); |
| if ((pAllocateInfo->allocationSize != buffer_state->requirements.size) && (imported_ahb == false)) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer) |
| ? "VUID-VkMemoryDedicatedAllocateInfo-buffer-02965" |
| : "VUID-VkMemoryDedicatedAllocateInfo-buffer-01435"; |
| skip |= LogError( |
| device, vuid, |
| "vkAllocateMemory: Allocation Size (%" PRIu64 ") needs to be equal to VkBuffer %s VkMemoryRequirements::size (%" PRIu64 ")", |
| pAllocateInfo->allocationSize, report_data->FormatHandle(dedicated_allocate_info->buffer).c_str(), |
| buffer_state->requirements.size); |
| } |
| if ((buffer_state->createInfo.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0) { |
| skip |= LogError( |
| device, "VUID-VkMemoryDedicatedAllocateInfo-buffer-01436", |
| "vkAllocateMemory: VkBuffer %s can't be used in VkMemoryDedicatedAllocateInfo because it was created with " |
| "VK_BUFFER_CREATE_SPARSE_BINDING_BIT", |
| report_data->FormatHandle(dedicated_allocate_info->buffer).c_str()); |
| } |
| } |
| } |
| |
| // TODO: VUIDs ending in 00643, 00644, 00646, 00647, 01742, 01743, 01745, 00645, 00648, 01744 |
| return skip; |
| } |
| |
| // For given obj node, if it is use, flag a validation error and return callback result, else return false |
| bool CoreChecks::ValidateObjectNotInUse(const BASE_NODE *obj_node, const char *caller_name, const char *error_code) const { |
| if (disabled[object_in_use]) return false; |
| auto obj_struct = obj_node->Handle(); |
| bool skip = false; |
| if (obj_node->InUse()) { |
| skip |= LogError(device, error_code, "Cannot call %s on %s that is currently in use by a command buffer.", caller_name, |
| report_data->FormatHandle(obj_struct).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateFreeMemory(VkDevice device, VkDeviceMemory mem, const VkAllocationCallbacks *pAllocator) const { |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem); |
| bool skip = false; |
| if (mem_info) { |
| skip |= ValidateObjectNotInUse(mem_info.get(), "vkFreeMemory", "VUID-vkFreeMemory-memory-00677"); |
| } |
| return skip; |
| } |
| |
| // Validate that given Map memory range is valid. This means that the memory should not already be mapped, |
| // and that the size of the map range should be: |
| // 1. Not zero |
| // 2. Within the size of the memory allocation |
| bool CoreChecks::ValidateMapMemRange(const DEVICE_MEMORY_STATE *mem_info, VkDeviceSize offset, VkDeviceSize size) const { |
| bool skip = false; |
| assert(mem_info); |
| const auto mem = mem_info->mem(); |
| if (size == 0) { |
| skip = LogError(mem, "VUID-vkMapMemory-size-00680", "VkMapMemory: Attempting to map memory range of size zero"); |
| } |
| |
| // It is an application error to call VkMapMemory on an object that is already mapped |
| if (mem_info->mapped_range.size != 0) { |
| skip = LogError(mem, "VUID-vkMapMemory-memory-00678", "VkMapMemory: Attempting to map memory on an already-mapped %s.", |
| report_data->FormatHandle(mem).c_str()); |
| } |
| |
| // Validate offset is not over allocaiton size |
| if (offset >= mem_info->alloc_info.allocationSize) { |
| skip = LogError(mem, "VUID-vkMapMemory-offset-00679", |
| "VkMapMemory: Attempting to map memory with an offset of 0x%" PRIx64 |
| " which is larger than the total array size 0x%" PRIx64, |
| offset, mem_info->alloc_info.allocationSize); |
| } |
| // Validate that offset + size is within object's allocationSize |
| if (size != VK_WHOLE_SIZE) { |
| if ((offset + size) > mem_info->alloc_info.allocationSize) { |
| skip = LogError(mem, "VUID-vkMapMemory-size-00681", |
| "VkMapMemory: Mapping Memory from 0x%" PRIx64 " to 0x%" PRIx64 " oversteps total array size 0x%" PRIx64 |
| ".", |
| offset, size + offset, mem_info->alloc_info.allocationSize); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, |
| VkQueue *pQueue) const { |
| bool skip = false; |
| |
| skip |= ValidateDeviceQueueFamily(queueFamilyIndex, "vkGetDeviceQueue", "queueFamilyIndex", |
| "VUID-vkGetDeviceQueue-queueFamilyIndex-00384"); |
| |
| for (size_t i = 0; i < device_queue_info_list.size(); i++) { |
| const auto device_queue_info = device_queue_info_list.at(i); |
| if (device_queue_info.queue_family_index != queueFamilyIndex) { |
| continue; |
| } |
| |
| // flag must be zero |
| if (device_queue_info.flags != 0) { |
| skip |= LogError( |
| device, "VUID-vkGetDeviceQueue-flags-01841", |
| "vkGetDeviceQueue: queueIndex (=%" PRIu32 |
| ") was created with a non-zero VkDeviceQueueCreateFlags in vkCreateDevice::pCreateInfo->pQueueCreateInfos[%" PRIu32 |
| "]. Need to use vkGetDeviceQueue2 instead.", |
| queueIndex, device_queue_info.index); |
| } |
| |
| if (device_queue_info.queue_count <= queueIndex) { |
| skip |= LogError(device, "VUID-vkGetDeviceQueue-queueIndex-00385", |
| "vkGetDeviceQueue: queueIndex (=%" PRIu32 |
| ") is not less than the number of queues requested from queueFamilyIndex (=%" PRIu32 |
| ") when the device was created vkCreateDevice::pCreateInfo->pQueueCreateInfos[%" PRIu32 |
| "] (i.e. is not less than %" PRIu32 ").", |
| queueIndex, queueFamilyIndex, device_queue_info.index, device_queue_info.queue_count); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2 *pQueueInfo, VkQueue *pQueue) const { |
| bool skip = false; |
| |
| if (pQueueInfo) { |
| const uint32_t queueFamilyIndex = pQueueInfo->queueFamilyIndex; |
| const uint32_t queueIndex = pQueueInfo->queueIndex; |
| const VkDeviceQueueCreateFlags flags = pQueueInfo->flags; |
| |
| skip |= ValidateDeviceQueueFamily(queueFamilyIndex, "vkGetDeviceQueue2", "pQueueInfo->queueFamilyIndex", |
| "VUID-VkDeviceQueueInfo2-queueFamilyIndex-01842"); |
| |
| // ValidateDeviceQueueFamily() already checks if queueFamilyIndex but need to make sure flags match with it |
| bool valid_flags = false; |
| |
| for (size_t i = 0; i < device_queue_info_list.size(); i++) { |
| const auto device_queue_info = device_queue_info_list.at(i); |
| // vkGetDeviceQueue2 only checks if both family index AND flags are same as device creation |
| // this handle case where the same queueFamilyIndex is used with/without the protected flag |
| if ((device_queue_info.queue_family_index != queueFamilyIndex) || (device_queue_info.flags != flags)) { |
| continue; |
| } |
| valid_flags = true; |
| |
| if (device_queue_info.queue_count <= queueIndex) { |
| skip |= LogError( |
| device, "VUID-VkDeviceQueueInfo2-queueIndex-01843", |
| "vkGetDeviceQueue2: queueIndex (=%" PRIu32 |
| ") is not less than the number of queues requested from [queueFamilyIndex (=%" PRIu32 |
| "), flags (%s)] combination when the device was created vkCreateDevice::pCreateInfo->pQueueCreateInfos[%" PRIu32 |
| "] (i.e. is not less than %" PRIu32 ").", |
| queueIndex, queueFamilyIndex, string_VkDeviceQueueCreateFlags(flags).c_str(), device_queue_info.index, |
| device_queue_info.queue_count); |
| } |
| } |
| |
| // Don't double error message if already skipping from ValidateDeviceQueueFamily |
| if (!valid_flags && !skip) { |
| skip |= LogError(device, "VUID-VkDeviceQueueInfo2-flags-06225", |
| "vkGetDeviceQueue2: The combination of queueFamilyIndex (=%" PRIu32 |
| ") and flags (%s) were never both set together in any element of " |
| "vkCreateDevice::pCreateInfo->pQueueCreateInfos at device creation time.", |
| queueFamilyIndex, string_VkDeviceQueueCreateFlags(flags).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateSemaphore(VkDevice device, const VkSemaphoreCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkSemaphore *pSemaphore) const { |
| bool skip = false; |
| auto *sem_type_create_info = LvlFindInChain<VkSemaphoreTypeCreateInfo>(pCreateInfo->pNext); |
| |
| if (sem_type_create_info) { |
| if (sem_type_create_info->semaphoreType == VK_SEMAPHORE_TYPE_TIMELINE && !enabled_features.core12.timelineSemaphore) { |
| skip |= LogError(device, "VUID-VkSemaphoreTypeCreateInfo-timelineSemaphore-03252", |
| "VkCreateSemaphore: timelineSemaphore feature is not enabled, can not create timeline semaphores"); |
| } |
| |
| if (sem_type_create_info->semaphoreType == VK_SEMAPHORE_TYPE_BINARY && sem_type_create_info->initialValue != 0) { |
| skip |= LogError(device, "VUID-VkSemaphoreTypeCreateInfo-semaphoreType-03279", |
| "vkCreateSemaphore: if semaphoreType is VK_SEMAPHORE_TYPE_BINARY, initialValue must be zero"); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo *pWaitInfo, uint64_t timeout) const { |
| return ValidateWaitSemaphores(device, pWaitInfo, timeout, "VkWaitSemaphores"); |
| } |
| |
| bool CoreChecks::PreCallValidateWaitSemaphoresKHR(VkDevice device, const VkSemaphoreWaitInfo *pWaitInfo, uint64_t timeout) const { |
| return ValidateWaitSemaphores(device, pWaitInfo, timeout, "VkWaitSemaphoresKHR"); |
| } |
| |
| bool CoreChecks::ValidateWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo *pWaitInfo, uint64_t timeout, |
| const char *apiName) const { |
| bool skip = false; |
| |
| for (uint32_t i = 0; i < pWaitInfo->semaphoreCount; i++) { |
| auto semaphore_state = Get<SEMAPHORE_STATE>(pWaitInfo->pSemaphores[i]); |
| if (semaphore_state && semaphore_state->type != VK_SEMAPHORE_TYPE_TIMELINE) { |
| skip |= LogError(pWaitInfo->pSemaphores[i], "VUID-VkSemaphoreWaitInfo-pSemaphores-03256", |
| "%s(): all semaphores in pWaitInfo must be timeline semaphores, but %s is not", apiName, |
| report_data->FormatHandle(pWaitInfo->pSemaphores[i]).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator) const { |
| auto fence_node = Get<FENCE_STATE>(fence); |
| bool skip = false; |
| if (fence_node) { |
| if (fence_node->Scope() == kSyncScopeInternal && fence_node->State() == FENCE_INFLIGHT) { |
| skip |= LogError(fence, "VUID-vkDestroyFence-fence-01120", "%s is in use.", report_data->FormatHandle(fence).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroySemaphore(VkDevice device, VkSemaphore semaphore, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto sema_node = Get<SEMAPHORE_STATE>(semaphore); |
| bool skip = false; |
| if (sema_node) { |
| skip |= ValidateObjectNotInUse(sema_node.get(), "vkDestroySemaphore", "VUID-vkDestroySemaphore-semaphore-01137"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyEvent(VkDevice device, VkEvent event, const VkAllocationCallbacks *pAllocator) const { |
| auto event_state = Get<EVENT_STATE>(event); |
| bool skip = false; |
| if (event_state) { |
| skip |= ValidateObjectNotInUse(event_state.get(), "vkDestroyEvent", "VUID-vkDestroyEvent-event-01145"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyQueryPool(VkDevice device, VkQueryPool queryPool, |
| const VkAllocationCallbacks *pAllocator) const { |
| if (disabled[query_validation]) return false; |
| auto qp_state = Get<QUERY_POOL_STATE>(queryPool); |
| bool skip = false; |
| if (qp_state) { |
| bool completed_by_get_results = true; |
| for (uint32_t i = 0; i < qp_state->createInfo.queryCount; ++i) { |
| auto state = qp_state->GetQueryState(i, 0); |
| if (state != QUERYSTATE_AVAILABLE) { |
| completed_by_get_results = false; |
| break; |
| } |
| } |
| if (!completed_by_get_results) { |
| skip |= ValidateObjectNotInUse(qp_state.get(), "vkDestroyQueryPool", "VUID-vkDestroyQueryPool-queryPool-00793"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePerformanceQueryResults(const char *cmd_name, const QUERY_POOL_STATE *query_pool_state, |
| uint32_t firstQuery, uint32_t queryCount, VkQueryResultFlags flags) const { |
| bool skip = false; |
| |
| if (flags & (VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | VK_QUERY_RESULT_PARTIAL_BIT | VK_QUERY_RESULT_64_BIT)) { |
| string invalid_flags_string; |
| for (auto flag : {VK_QUERY_RESULT_WITH_AVAILABILITY_BIT, VK_QUERY_RESULT_PARTIAL_BIT, VK_QUERY_RESULT_64_BIT}) { |
| if (flag & flags) { |
| if (invalid_flags_string.size()) { |
| invalid_flags_string += " and "; |
| } |
| invalid_flags_string += string_VkQueryResultFlagBits(flag); |
| } |
| } |
| skip |= LogError(query_pool_state->pool(), |
| strcmp(cmd_name, "vkGetQueryPoolResults") == 0 ? "VUID-vkGetQueryPoolResults-queryType-03230" |
| : "VUID-vkCmdCopyQueryPoolResults-queryType-03233", |
| "%s: QueryPool %s was created with a queryType of" |
| "VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR but flags contains %s.", |
| cmd_name, report_data->FormatHandle(query_pool_state->pool()).c_str(), invalid_flags_string.c_str()); |
| } |
| |
| for (uint32_t query_index = firstQuery; query_index < queryCount; query_index++) { |
| uint32_t submitted = 0; |
| for (uint32_t pass_index = 0; pass_index < query_pool_state->n_performance_passes; pass_index++) { |
| auto state = query_pool_state->GetQueryState(query_index, pass_index); |
| if (state == QUERYSTATE_AVAILABLE) { |
| submitted++; |
| } |
| } |
| if (submitted < query_pool_state->n_performance_passes) { |
| skip |= LogError(query_pool_state->pool(), "VUID-vkGetQueryPoolResults-queryType-03231", |
| "%s: QueryPool %s has %u performance query passes, but the query has only been " |
| "submitted for %u of the passes.", |
| cmd_name, report_data->FormatHandle(query_pool_state->pool()).c_str(), |
| query_pool_state->n_performance_passes, submitted); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGetQueryPoolPerformanceResults(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, |
| void *pData, VkDeviceSize stride, VkQueryResultFlags flags, |
| const char *apiName) const { |
| bool skip = false; |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| |
| if (!query_pool_state || query_pool_state->createInfo.queryType != VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) return skip; |
| |
| if (((((uintptr_t)pData) % sizeof(VkPerformanceCounterResultKHR)) != 0 || |
| (stride % sizeof(VkPerformanceCounterResultKHR)) != 0)) { |
| skip |= LogError(queryPool, "VUID-vkGetQueryPoolResults-queryType-03229", |
| "%s(): QueryPool %s was created with a queryType of " |
| "VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR but pData & stride are not multiple of the " |
| "size of VkPerformanceCounterResultKHR.", |
| apiName, report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| skip |= ValidatePerformanceQueryResults(apiName, query_pool_state.get(), firstQuery, queryCount, flags); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, |
| uint32_t queryCount, size_t dataSize, void *pData, VkDeviceSize stride, |
| VkQueryResultFlags flags) const { |
| if (disabled[query_validation]) return false; |
| bool skip = false; |
| skip |= ValidateQueryPoolIndex(queryPool, firstQuery, queryCount, "vkGetQueryPoolResults()", |
| "VUID-vkGetQueryPoolResults-firstQuery-00813", "VUID-vkGetQueryPoolResults-firstQuery-00816"); |
| skip |= |
| ValidateGetQueryPoolPerformanceResults(queryPool, firstQuery, queryCount, pData, stride, flags, "vkGetQueryPoolResults"); |
| |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if (query_pool_state) { |
| if (query_pool_state->createInfo.queryType != VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_khr_performance_query) ? "VUID-vkGetQueryPoolResults-flags-02828" |
| : "VUID-vkGetQueryPoolResults-flags-02827"; |
| skip |= ValidateQueryPoolStride(vuid, "VUID-vkGetQueryPoolResults-flags-00815", |
| stride, "dataSize", dataSize, flags); |
| } |
| if ((query_pool_state->createInfo.queryType == VK_QUERY_TYPE_TIMESTAMP) && (flags & VK_QUERY_RESULT_PARTIAL_BIT)) { |
| skip |= LogError( |
| queryPool, "VUID-vkGetQueryPoolResults-queryType-00818", |
| "%s was created with a queryType of VK_QUERY_TYPE_TIMESTAMP but flags contains VK_QUERY_RESULT_PARTIAL_BIT.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| if (!skip) { |
| uint32_t query_avail_data = (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) ? 1 : 0; |
| uint32_t query_size_in_bytes = (flags & VK_QUERY_RESULT_64_BIT) ? sizeof(uint64_t) : sizeof(uint32_t); |
| uint32_t query_items = 0; |
| uint32_t query_size = 0; |
| |
| switch (query_pool_state->createInfo.queryType) { |
| case VK_QUERY_TYPE_OCCLUSION: |
| // Occlusion queries write one integer value - the number of samples passed. |
| query_items = 1; |
| query_size = query_size_in_bytes * (query_items + query_avail_data); |
| break; |
| |
| case VK_QUERY_TYPE_PIPELINE_STATISTICS: |
| // Pipeline statistics queries write one integer value for each bit that is enabled in the pipelineStatistics |
| // when the pool is created |
| { |
| const int num_bits = sizeof(VkFlags) * CHAR_BIT; |
| std::bitset<num_bits> pipe_stats_bits(query_pool_state->createInfo.pipelineStatistics); |
| query_items = static_cast<uint32_t>(pipe_stats_bits.count()); |
| query_size = query_size_in_bytes * (query_items + query_avail_data); |
| } |
| break; |
| |
| case VK_QUERY_TYPE_TIMESTAMP: |
| // Timestamp queries write one integer |
| query_items = 1; |
| query_size = query_size_in_bytes * (query_items + query_avail_data); |
| break; |
| |
| case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: |
| // Transform feedback queries write two integers |
| query_items = 2; |
| query_size = query_size_in_bytes * (query_items + query_avail_data); |
| break; |
| |
| case VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR: |
| // Performance queries store results in a tightly packed array of VkPerformanceCounterResultsKHR |
| query_items = query_pool_state->perf_counter_index_count; |
| query_size = sizeof(VkPerformanceCounterResultKHR) * query_items; |
| if (query_size > stride) { |
| skip |= LogError(queryPool, "VUID-vkGetQueryPoolResults-queryType-04519", |
| "vkGetQueryPoolResults() on querypool %s specified stride %" PRIu64 |
| " which must be at least counterIndexCount (%d) " |
| "multiplied by sizeof(VkPerformanceCounterResultKHR) (%zu).", |
| report_data->FormatHandle(queryPool).c_str(), stride, query_items, |
| sizeof(VkPerformanceCounterResultKHR)); |
| } |
| break; |
| |
| // These cases intentionally fall through to the default |
| case VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR: // VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV |
| case VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR: |
| case VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL: |
| default: |
| query_size = 0; |
| break; |
| } |
| |
| if (query_size && (((queryCount - 1) * stride + query_size) > dataSize)) { |
| skip |= LogError(queryPool, "VUID-vkGetQueryPoolResults-dataSize-00817", |
| "vkGetQueryPoolResults() on querypool %s specified dataSize %zu which is " |
| "incompatible with the specified query type and options.", |
| report_data->FormatHandle(queryPool).c_str(), dataSize); |
| } |
| } |
| if (query_pool_state->createInfo.queryType == VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR && |
| (flags & VK_QUERY_RESULT_WITH_STATUS_BIT_KHR) == 0) { |
| skip |= LogError(queryPool, "VUID-vkGetQueryPoolResults-queryType-04810", |
| "vkGetQueryPoolResults(): querypool %s was created with VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR " |
| "queryType, but flags do not contain VK_QUERY_RESULT_WITH_STATUS_BIT_KHR bit.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateInsertMemoryRange(const VulkanTypedHandle &typed_handle, const DEVICE_MEMORY_STATE *mem_info, |
| VkDeviceSize memoryOffset, const char *api_name) const { |
| bool skip = false; |
| |
| if (memoryOffset >= mem_info->alloc_info.allocationSize) { |
| const char *error_code = nullptr; |
| if (typed_handle.type == kVulkanObjectTypeBuffer) { |
| if (strcmp(api_name, "vkBindBufferMemory()") == 0) { |
| error_code = "VUID-vkBindBufferMemory-memoryOffset-01031"; |
| } else { |
| error_code = "VUID-VkBindBufferMemoryInfo-memoryOffset-01031"; |
| } |
| } else if (typed_handle.type == kVulkanObjectTypeImage) { |
| if (strcmp(api_name, "vkBindImageMemory()") == 0) { |
| error_code = "VUID-vkBindImageMemory-memoryOffset-01046"; |
| } else { |
| error_code = "VUID-VkBindImageMemoryInfo-memoryOffset-01046"; |
| } |
| } else if (typed_handle.type == kVulkanObjectTypeAccelerationStructureNV) { |
| error_code = "VUID-VkBindAccelerationStructureMemoryInfoNV-memoryOffset-03621"; |
| } else { |
| // Unsupported object type |
| assert(false); |
| } |
| |
| LogObjectList objlist(mem_info->mem()); |
| objlist.add(typed_handle); |
| skip = LogError(objlist, error_code, |
| "In %s, attempting to bind %s to %s, memoryOffset=0x%" PRIxLEAST64 |
| " must be less than the memory allocation size 0x%" PRIxLEAST64 ".", |
| api_name, report_data->FormatHandle(mem_info->mem()).c_str(), report_data->FormatHandle(typed_handle).c_str(), |
| memoryOffset, mem_info->alloc_info.allocationSize); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateInsertImageMemoryRange(VkImage image, const DEVICE_MEMORY_STATE *mem_info, VkDeviceSize mem_offset, |
| const char *api_name) const { |
| return ValidateInsertMemoryRange(VulkanTypedHandle(image, kVulkanObjectTypeImage), mem_info, mem_offset, api_name); |
| } |
| |
| bool CoreChecks::ValidateInsertBufferMemoryRange(VkBuffer buffer, const DEVICE_MEMORY_STATE *mem_info, VkDeviceSize mem_offset, |
| const char *api_name) const { |
| return ValidateInsertMemoryRange(VulkanTypedHandle(buffer, kVulkanObjectTypeBuffer), mem_info, mem_offset, api_name); |
| } |
| |
| bool CoreChecks::ValidateInsertAccelerationStructureMemoryRange(VkAccelerationStructureNV as, const DEVICE_MEMORY_STATE *mem_info, |
| VkDeviceSize mem_offset, const char *api_name) const { |
| return ValidateInsertMemoryRange(VulkanTypedHandle(as, kVulkanObjectTypeAccelerationStructureNV), mem_info, mem_offset, |
| api_name); |
| } |
| |
| bool CoreChecks::ValidateMemoryTypes(const DEVICE_MEMORY_STATE *mem_info, const uint32_t memory_type_bits, const char *funcName, |
| const char *msgCode) const { |
| bool skip = false; |
| if (((1 << mem_info->alloc_info.memoryTypeIndex) & memory_type_bits) == 0) { |
| skip = LogError(mem_info->mem(), msgCode, |
| "%s(): MemoryRequirements->memoryTypeBits (0x%X) for this object type are not compatible with the memory " |
| "type (0x%X) of %s.", |
| funcName, memory_type_bits, mem_info->alloc_info.memoryTypeIndex, |
| report_data->FormatHandle(mem_info->mem()).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateBindBufferMemory(VkBuffer buffer, VkDeviceMemory mem, VkDeviceSize memoryOffset, |
| const char *api_name) const { |
| auto buffer_state = Get<BUFFER_STATE>(buffer); |
| bool bind_buffer_mem_2 = strcmp(api_name, "vkBindBufferMemory()") != 0; |
| |
| bool skip = false; |
| if (buffer_state) { |
| // Track objects tied to memory |
| skip = ValidateSetMemBinding(mem, *buffer_state, api_name); |
| |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem); |
| |
| // Validate memory requirements alignment |
| if (SafeModulo(memoryOffset, buffer_state->requirements.alignment) != 0) { |
| const char *vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-memoryOffset-01036" : "VUID-vkBindBufferMemory-memoryOffset-01036"; |
| skip |= LogError(buffer, vuid, |
| "%s: memoryOffset is 0x%" PRIxLEAST64 |
| " but must be an integer multiple of the VkMemoryRequirements::alignment value 0x%" PRIxLEAST64 |
| ", returned from a call to vkGetBufferMemoryRequirements with buffer.", |
| api_name, memoryOffset, buffer_state->requirements.alignment); |
| } |
| |
| if (mem_info) { |
| // Validate bound memory range information |
| skip |= ValidateInsertBufferMemoryRange(buffer, mem_info.get(), memoryOffset, api_name); |
| |
| const char *mem_type_vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-memory-01035" : "VUID-vkBindBufferMemory-memory-01035"; |
| skip |= ValidateMemoryTypes(mem_info.get(), buffer_state->requirements.memoryTypeBits, api_name, mem_type_vuid); |
| |
| // Validate memory requirements size |
| if (buffer_state->requirements.size > (mem_info->alloc_info.allocationSize - memoryOffset)) { |
| const char *vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-size-01037" : "VUID-vkBindBufferMemory-size-01037"; |
| skip |= LogError(buffer, vuid, |
| "%s: memory size minus memoryOffset is 0x%" PRIxLEAST64 |
| " but must be at least as large as VkMemoryRequirements::size value 0x%" PRIxLEAST64 |
| ", returned from a call to vkGetBufferMemoryRequirements with buffer.", |
| api_name, mem_info->alloc_info.allocationSize - memoryOffset, buffer_state->requirements.size); |
| } |
| |
| // Validate dedicated allocation |
| if (mem_info->IsDedicatedBuffer() && |
| ((mem_info->dedicated->handle.Cast<VkBuffer>() != buffer) || (memoryOffset != 0))) { |
| const char *vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-memory-01508" : "VUID-vkBindBufferMemory-memory-01508"; |
| LogObjectList objlist(buffer); |
| objlist.add(mem); |
| objlist.add(mem_info->dedicated->handle); |
| skip |= LogError(objlist, vuid, |
| "%s: for dedicated %s, VkMemoryDedicatedAllocateInfo::buffer %s must be equal " |
| "to %s and memoryOffset 0x%" PRIxLEAST64 " must be zero.", |
| api_name, report_data->FormatHandle(mem).c_str(), |
| report_data->FormatHandle(mem_info->dedicated->handle).c_str(), |
| report_data->FormatHandle(buffer).c_str(), memoryOffset); |
| } |
| |
| auto chained_flags_struct = LvlFindInChain<VkMemoryAllocateFlagsInfo>(mem_info->alloc_info.pNext); |
| if (enabled_features.core12.bufferDeviceAddress && |
| (buffer_state->createInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) && |
| (!chained_flags_struct || !(chained_flags_struct->flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT))) { |
| skip |= LogError(buffer, "VUID-vkBindBufferMemory-bufferDeviceAddress-03339", |
| "%s: If buffer was created with the VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT bit set, " |
| "memory must have been allocated with the VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT bit set.", |
| api_name); |
| } |
| |
| // Validate export memory handles |
| if ((mem_info->export_handle_type_flags != 0) && |
| ((mem_info->export_handle_type_flags & buffer_state->external_memory_handle) == 0)) { |
| const char *vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-memory-02726" : "VUID-vkBindBufferMemory-memory-02726"; |
| LogObjectList objlist(buffer); |
| objlist.add(mem); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) has an external handleType of %s which does not include at least one " |
| "handle from VkBuffer (%s) handleType %s.", |
| api_name, report_data->FormatHandle(mem).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(mem_info->export_handle_type_flags).c_str(), |
| report_data->FormatHandle(buffer).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(buffer_state->external_memory_handle).c_str()); |
| } |
| |
| // Validate import memory handles |
| if (mem_info->IsImportAHB() == true) { |
| skip |= ValidateBufferImportedHandleANDROID(api_name, buffer_state->external_memory_handle, mem, buffer); |
| } else if (mem_info->IsImport() == true) { |
| if ((mem_info->import_handle_type_flags & buffer_state->external_memory_handle) == 0) { |
| const char *vuid = nullptr; |
| if ((bind_buffer_mem_2) && IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-VkBindBufferMemoryInfo-memory-02985"; |
| } else if ((!bind_buffer_mem_2) && |
| IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-vkBindBufferMemory-memory-02985"; |
| } else if ((bind_buffer_mem_2) && |
| !IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-VkBindBufferMemoryInfo-memory-02727"; |
| } else if ((!bind_buffer_mem_2) && |
| !IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-vkBindBufferMemory-memory-02727"; |
| } |
| LogObjectList objlist(buffer); |
| objlist.add(mem); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was created with an import operation with handleType of %s which " |
| "is not set in the VkBuffer (%s) VkExternalMemoryBufferCreateInfo::handleType (%s)", |
| api_name, report_data->FormatHandle(mem).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(mem_info->import_handle_type_flags).c_str(), |
| report_data->FormatHandle(buffer).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(buffer_state->external_memory_handle).c_str()); |
| } |
| } |
| |
| // Validate mix of protected buffer and memory |
| if ((buffer_state->unprotected == false) && (mem_info->unprotected == true)) { |
| const char *vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-None-01898" : "VUID-vkBindBufferMemory-None-01898"; |
| LogObjectList objlist(buffer); |
| objlist.add(mem); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was not created with protected memory but the VkBuffer (%s) was set " |
| "to use protected memory.", |
| api_name, report_data->FormatHandle(mem).c_str(), report_data->FormatHandle(buffer).c_str()); |
| } else if ((buffer_state->unprotected == true) && (mem_info->unprotected == false)) { |
| const char *vuid = |
| bind_buffer_mem_2 ? "VUID-VkBindBufferMemoryInfo-None-01899" : "VUID-vkBindBufferMemory-None-01899"; |
| LogObjectList objlist(buffer); |
| objlist.add(mem); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was created with protected memory but the VkBuffer (%s) was not set " |
| "to use protected memory.", |
| api_name, report_data->FormatHandle(mem).c_str(), report_data->FormatHandle(buffer).c_str()); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory mem, |
| VkDeviceSize memoryOffset) const { |
| const char *api_name = "vkBindBufferMemory()"; |
| return ValidateBindBufferMemory(buffer, mem, memoryOffset, api_name); |
| } |
| |
| bool CoreChecks::PreCallValidateBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, |
| const VkBindBufferMemoryInfo *pBindInfos) const { |
| char api_name[64]; |
| bool skip = false; |
| |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| sprintf(api_name, "vkBindBufferMemory2() pBindInfos[%u]", i); |
| skip |= ValidateBindBufferMemory(pBindInfos[i].buffer, pBindInfos[i].memory, pBindInfos[i].memoryOffset, api_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateBindBufferMemory2KHR(VkDevice device, uint32_t bindInfoCount, |
| const VkBindBufferMemoryInfo *pBindInfos) const { |
| char api_name[64]; |
| bool skip = false; |
| |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| sprintf(api_name, "vkBindBufferMemory2KHR() pBindInfos[%u]", i); |
| skip |= ValidateBindBufferMemory(pBindInfos[i].buffer, pBindInfos[i].memory, pBindInfos[i].memoryOffset, api_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetImageMemoryRequirements(VkDevice device, VkImage image, |
| VkMemoryRequirements *pMemoryRequirements) const { |
| bool skip = false; |
| if (IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| skip |= ValidateGetImageMemoryRequirementsANDROID(image, "vkGetImageMemoryRequirements()"); |
| } |
| |
| auto image_state = Get<IMAGE_STATE>(image); |
| if (image_state) { |
| // Checks for no disjoint bit |
| if (image_state->disjoint == true) { |
| skip |= LogError(image, "VUID-vkGetImageMemoryRequirements-image-01588", |
| "vkGetImageMemoryRequirements(): %s must not have been created with the VK_IMAGE_CREATE_DISJOINT_BIT " |
| "(need to use vkGetImageMemoryRequirements2).", |
| report_data->FormatHandle(image).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGetImageMemoryRequirements2(const VkImageMemoryRequirementsInfo2 *pInfo, const char *func_name) const { |
| bool skip = false; |
| if (IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| skip |= ValidateGetImageMemoryRequirementsANDROID(pInfo->image, func_name); |
| } |
| |
| auto image_state = Get<IMAGE_STATE>(pInfo->image); |
| const VkFormat image_format = image_state->createInfo.format; |
| const VkImageTiling image_tiling = image_state->createInfo.tiling; |
| const VkImagePlaneMemoryRequirementsInfo *image_plane_info = LvlFindInChain<VkImagePlaneMemoryRequirementsInfo>(pInfo->pNext); |
| |
| if ((FormatIsMultiplane(image_format)) && (image_state->disjoint == true) && (image_plane_info == nullptr)) { |
| skip |= LogError(pInfo->image, "VUID-VkImageMemoryRequirementsInfo2-image-01589", |
| "%s: %s image was created with a multi-planar format (%s) and " |
| "VK_IMAGE_CREATE_DISJOINT_BIT, but the current pNext doesn't include a " |
| "VkImagePlaneMemoryRequirementsInfo struct", |
| func_name, report_data->FormatHandle(pInfo->image).c_str(), string_VkFormat(image_format)); |
| } |
| |
| if ((image_state->disjoint == false) && (image_plane_info != nullptr)) { |
| skip |= LogError(pInfo->image, "VUID-VkImageMemoryRequirementsInfo2-image-01590", |
| "%s: %s image was not created with VK_IMAGE_CREATE_DISJOINT_BIT," |
| "but the current pNext includes a VkImagePlaneMemoryRequirementsInfo struct", |
| func_name, report_data->FormatHandle(pInfo->image).c_str()); |
| } |
| |
| if ((FormatIsMultiplane(image_format) == false) && (image_tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) && |
| (image_plane_info != nullptr)) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_image_drm_format_modifier) |
| ? "VUID-VkImageMemoryRequirementsInfo2-image-02280" |
| : "VUID-VkImageMemoryRequirementsInfo2-image-01591"; |
| skip |= LogError(pInfo->image, vuid, |
| "%s: %s image is a single-plane format (%s) and does not have tiling of " |
| "VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT," |
| "but the current pNext includes a VkImagePlaneMemoryRequirementsInfo struct", |
| func_name, report_data->FormatHandle(pInfo->image).c_str(), string_VkFormat(image_format)); |
| } |
| |
| if (image_plane_info != nullptr) { |
| if ((image_tiling == VK_IMAGE_TILING_LINEAR) || (image_tiling == VK_IMAGE_TILING_OPTIMAL)) { |
| // Make sure planeAspect is only a single, valid plane |
| uint32_t planes = FormatPlaneCount(image_format); |
| VkImageAspectFlags aspect = image_plane_info->planeAspect; |
| if ((2 == planes) && (aspect != VK_IMAGE_ASPECT_PLANE_0_BIT) && (aspect != VK_IMAGE_ASPECT_PLANE_1_BIT)) { |
| skip |= LogError( |
| pInfo->image, "VUID-VkImagePlaneMemoryRequirementsInfo-planeAspect-02281", |
| "%s: Image %s VkImagePlaneMemoryRequirementsInfo::planeAspect is %s but can only be VK_IMAGE_ASPECT_PLANE_0_BIT" |
| "or VK_IMAGE_ASPECT_PLANE_1_BIT.", |
| func_name, report_data->FormatHandle(image_state->image()).c_str(), string_VkImageAspectFlags(aspect).c_str()); |
| } |
| if ((3 == planes) && (aspect != VK_IMAGE_ASPECT_PLANE_0_BIT) && (aspect != VK_IMAGE_ASPECT_PLANE_1_BIT) && |
| (aspect != VK_IMAGE_ASPECT_PLANE_2_BIT)) { |
| skip |= LogError( |
| pInfo->image, "VUID-VkImagePlaneMemoryRequirementsInfo-planeAspect-02281", |
| "%s: Image %s VkImagePlaneMemoryRequirementsInfo::planeAspect is %s but can only be VK_IMAGE_ASPECT_PLANE_0_BIT" |
| "or VK_IMAGE_ASPECT_PLANE_1_BIT or VK_IMAGE_ASPECT_PLANE_2_BIT.", |
| func_name, report_data->FormatHandle(image_state->image()).c_str(), string_VkImageAspectFlags(aspect).c_str()); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, |
| VkMemoryRequirements2 *pMemoryRequirements) const { |
| return ValidateGetImageMemoryRequirements2(pInfo, "vkGetImageMemoryRequirements2()"); |
| } |
| |
| bool CoreChecks::PreCallValidateGetImageMemoryRequirements2KHR(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, |
| VkMemoryRequirements2 *pMemoryRequirements) const { |
| return ValidateGetImageMemoryRequirements2(pInfo, "vkGetImageMemoryRequirements2KHR()"); |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, |
| VkImageFormatProperties2 *pImageFormatProperties) const { |
| // Can't wrap AHB-specific validation in a device extension check here, but no harm |
| bool skip = ValidateGetPhysicalDeviceImageFormatProperties2ANDROID(pImageFormatInfo, pImageFormatProperties); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo, |
| VkImageFormatProperties2 *pImageFormatProperties) const { |
| // Can't wrap AHB-specific validation in a device extension check here, but no harm |
| bool skip = ValidateGetPhysicalDeviceImageFormatProperties2ANDROID(pImageFormatInfo, pImageFormatProperties); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyPipeline(VkDevice device, VkPipeline pipeline, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto pipeline_state = Get<PIPELINE_STATE>(pipeline); |
| bool skip = false; |
| if (pipeline_state) { |
| skip |= ValidateObjectNotInUse(pipeline_state.get(), "vkDestroyPipeline", "VUID-vkDestroyPipeline-pipeline-00765"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroySampler(VkDevice device, VkSampler sampler, const VkAllocationCallbacks *pAllocator) const { |
| auto sampler_state = Get<SAMPLER_STATE>(sampler); |
| bool skip = false; |
| if (sampler_state) { |
| skip |= ValidateObjectNotInUse(sampler_state.get(), "vkDestroySampler", "VUID-vkDestroySampler-sampler-01082"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto desc_pool_state = Get<DESCRIPTOR_POOL_STATE>(descriptorPool); |
| bool skip = false; |
| if (desc_pool_state) { |
| skip |= ValidateObjectNotInUse(desc_pool_state.get(), "vkDestroyDescriptorPool", |
| "VUID-vkDestroyDescriptorPool-descriptorPool-00303"); |
| } |
| return skip; |
| } |
| |
| // Verify cmdBuffer in given cb_node is not in global in-flight set, and return skip result |
| // If this is a secondary command buffer, then make sure its primary is also in-flight |
| // If primary is not in-flight, then remove secondary from global in-flight set |
| // This function is only valid at a point when cmdBuffer is being reset or freed |
| bool CoreChecks::CheckCommandBufferInFlight(const CMD_BUFFER_STATE *cb_node, const char *action, const char *error_code) const { |
| bool skip = false; |
| if (cb_node->InUse()) { |
| skip |= LogError(cb_node->commandBuffer(), error_code, "Attempt to %s %s which is in use.", action, |
| report_data->FormatHandle(cb_node->commandBuffer()).c_str()); |
| } |
| return skip; |
| } |
| |
| // Iterate over all cmdBuffers in given commandPool and verify that each is not in use |
| bool CoreChecks::CheckCommandBuffersInFlight(const COMMAND_POOL_STATE *pPool, const char *action, const char *error_code) const { |
| bool skip = false; |
| for (auto &entry : pPool->commandBuffers) { |
| auto cb_state = entry.second; |
| skip |= CheckCommandBufferInFlight(cb_state, action, error_code); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, |
| const VkCommandBuffer *pCommandBuffers) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < commandBufferCount; i++) { |
| auto cb_node = GetRead<CMD_BUFFER_STATE>(pCommandBuffers[i]); |
| // Delete CB information structure, and remove from commandBufferMap |
| if (cb_node) { |
| skip |= CheckCommandBufferInFlight(cb_node.get(), "free", "VUID-vkFreeCommandBuffers-pCommandBuffers-00047"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool) const { |
| bool skip = false; |
| skip |= ValidateDeviceQueueFamily(pCreateInfo->queueFamilyIndex, "vkCreateCommandPool", "pCreateInfo->queueFamilyIndex", |
| "VUID-vkCreateCommandPool-queueFamilyIndex-01937"); |
| if ((enabled_features.core11.protectedMemory == VK_FALSE) && |
| ((pCreateInfo->flags & VK_COMMAND_POOL_CREATE_PROTECTED_BIT) != 0)) { |
| skip |= LogError(device, "VUID-VkCommandPoolCreateInfo-flags-02860", |
| "vkCreateCommandPool(): the protectedMemory device feature is disabled: CommandPools cannot be created " |
| "with the VK_COMMAND_POOL_CREATE_PROTECTED_BIT set."); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkQueryPool *pQueryPool) const { |
| if (disabled[query_validation]) return false; |
| bool skip = false; |
| if (pCreateInfo && pCreateInfo->queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) { |
| if (!enabled_features.core.pipelineStatisticsQuery) { |
| skip |= LogError(device, "VUID-VkQueryPoolCreateInfo-queryType-00791", |
| "vkCreateQueryPool(): Query pool with type VK_QUERY_TYPE_PIPELINE_STATISTICS created on a device with " |
| "VkDeviceCreateInfo.pEnabledFeatures.pipelineStatisticsQuery == VK_FALSE."); |
| } |
| } |
| if (pCreateInfo && pCreateInfo->queryType == VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) { |
| if (!enabled_features.performance_query_features.performanceCounterQueryPools) { |
| skip |= |
| LogError(device, "VUID-VkQueryPoolPerformanceCreateInfoKHR-performanceCounterQueryPools-03237", |
| "vkCreateQueryPool(): Query pool with type VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR created on a device with " |
| "VkPhysicalDevicePerformanceQueryFeaturesKHR.performanceCounterQueryPools == VK_FALSE."); |
| } |
| |
| auto perf_ci = LvlFindInChain<VkQueryPoolPerformanceCreateInfoKHR>(pCreateInfo->pNext); |
| if (!perf_ci) { |
| skip |= LogError( |
| device, "VUID-VkQueryPoolCreateInfo-queryType-03222", |
| "vkCreateQueryPool(): Query pool with type VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR created but the pNext chain of " |
| "pCreateInfo does not contain in instance of VkQueryPoolPerformanceCreateInfoKHR."); |
| } else { |
| const auto &perf_counter_iter = physical_device_state->perf_counters.find(perf_ci->queueFamilyIndex); |
| if (perf_counter_iter == physical_device_state->perf_counters.end()) { |
| skip |= LogError( |
| device, "VUID-VkQueryPoolPerformanceCreateInfoKHR-queueFamilyIndex-03236", |
| "vkCreateQueryPool(): VkQueryPerformanceCreateInfoKHR::queueFamilyIndex is not a valid queue family index."); |
| } else { |
| const QUEUE_FAMILY_PERF_COUNTERS *perf_counters = perf_counter_iter->second.get(); |
| for (uint32_t idx = 0; idx < perf_ci->counterIndexCount; idx++) { |
| if (perf_ci->pCounterIndices[idx] >= perf_counters->counters.size()) { |
| skip |= LogError( |
| device, "VUID-VkQueryPoolPerformanceCreateInfoKHR-pCounterIndices-03321", |
| "vkCreateQueryPool(): VkQueryPerformanceCreateInfoKHR::pCounterIndices[%u] = %u is not a valid " |
| "counter index.", |
| idx, perf_ci->pCounterIndices[idx]); |
| } |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyCommandPool(VkDevice device, VkCommandPool commandPool, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto cp_state = Get<COMMAND_POOL_STATE>(commandPool); |
| bool skip = false; |
| if (cp_state) { |
| // Verify that command buffers in pool are complete (not in-flight) |
| skip |= |
| CheckCommandBuffersInFlight(cp_state.get(), "destroy command pool with", "VUID-vkDestroyCommandPool-commandPool-00041"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags) const { |
| auto command_pool_state = Get<COMMAND_POOL_STATE>(commandPool); |
| return CheckCommandBuffersInFlight(command_pool_state.get(), "reset command pool with", |
| "VUID-vkResetCommandPool-commandPool-00040"); |
| } |
| |
| bool CoreChecks::PreCallValidateResetFences(VkDevice device, uint32_t fenceCount, const VkFence *pFences) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < fenceCount; ++i) { |
| auto fence_state = Get<FENCE_STATE>(pFences[i]); |
| if (fence_state && fence_state->Scope() == kSyncScopeInternal && fence_state->State() == FENCE_INFLIGHT) { |
| skip |= LogError(pFences[i], "VUID-vkResetFences-pFences-01123", "%s is in use.", |
| report_data->FormatHandle(pFences[i]).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto framebuffer_state = Get<FRAMEBUFFER_STATE>(framebuffer); |
| bool skip = false; |
| if (framebuffer_state) { |
| skip |= |
| ValidateObjectNotInUse(framebuffer_state.get(), "vkDestroyFramebuffer", "VUID-vkDestroyFramebuffer-framebuffer-00892"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyRenderPass(VkDevice device, VkRenderPass renderPass, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto rp_state = Get<RENDER_PASS_STATE>(renderPass); |
| bool skip = false; |
| if (rp_state) { |
| skip |= ValidateObjectNotInUse(rp_state.get(), "vkDestroyRenderPass", "VUID-vkDestroyRenderPass-renderPass-00873"); |
| } |
| return skip; |
| } |
| |
| // Access helper functions for external modules |
| VkFormatProperties3KHR CoreChecks::GetPDFormatProperties(const VkFormat format) const { |
| auto fmt_props_3 = LvlInitStruct<VkFormatProperties3KHR>(); |
| auto fmt_props_2 = LvlInitStruct<VkFormatProperties2>(&fmt_props_3); |
| |
| if (has_format_feature2) { |
| DispatchGetPhysicalDeviceFormatProperties2(physical_device, format, &fmt_props_2); |
| } else { |
| VkFormatProperties format_properties; |
| DispatchGetPhysicalDeviceFormatProperties(physical_device, format, &format_properties); |
| fmt_props_3.linearTilingFeatures = format_properties.linearTilingFeatures; |
| fmt_props_3.optimalTilingFeatures = format_properties.optimalTilingFeatures; |
| fmt_props_3.bufferFeatures = format_properties.bufferFeatures; |
| } |
| return fmt_props_3; |
| } |
| |
| bool CoreChecks::ValidatePipelineVertexDivisors(std::vector<std::shared_ptr<PIPELINE_STATE>> const &pipe_state_vec, |
| const uint32_t count, const VkGraphicsPipelineCreateInfo *pipe_cis) const { |
| bool skip = false; |
| const VkPhysicalDeviceLimits *device_limits = &phys_dev_props.limits; |
| |
| for (uint32_t i = 0; i < count; i++) { |
| auto pvids_ci = (pipe_cis[i].pVertexInputState) ? LvlFindInChain<VkPipelineVertexInputDivisorStateCreateInfoEXT>(pipe_cis[i].pVertexInputState->pNext) : nullptr; |
| if (nullptr == pvids_ci) continue; |
| |
| const PIPELINE_STATE *pipe_state = pipe_state_vec[i].get(); |
| for (uint32_t j = 0; j < pvids_ci->vertexBindingDivisorCount; j++) { |
| const VkVertexInputBindingDivisorDescriptionEXT *vibdd = &(pvids_ci->pVertexBindingDivisors[j]); |
| if (vibdd->binding >= device_limits->maxVertexInputBindings) { |
| skip |= LogError( |
| device, "VUID-VkVertexInputBindingDivisorDescriptionEXT-binding-01869", |
| "vkCreateGraphicsPipelines(): Pipeline[%1u] with chained VkPipelineVertexInputDivisorStateCreateInfoEXT, " |
| "pVertexBindingDivisors[%1u] binding index of (%1u) exceeds device maxVertexInputBindings (%1u).", |
| i, j, vibdd->binding, device_limits->maxVertexInputBindings); |
| } |
| if (vibdd->divisor > phys_dev_ext_props.vtx_attrib_divisor_props.maxVertexAttribDivisor) { |
| skip |= LogError( |
| device, "VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870", |
| "vkCreateGraphicsPipelines(): Pipeline[%1u] with chained VkPipelineVertexInputDivisorStateCreateInfoEXT, " |
| "pVertexBindingDivisors[%1u] divisor of (%1u) exceeds extension maxVertexAttribDivisor (%1u).", |
| i, j, vibdd->divisor, phys_dev_ext_props.vtx_attrib_divisor_props.maxVertexAttribDivisor); |
| } |
| if ((0 == vibdd->divisor) && !enabled_features.vtx_attrib_divisor_features.vertexAttributeInstanceRateZeroDivisor) { |
| skip |= LogError( |
| device, "VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228", |
| "vkCreateGraphicsPipelines(): Pipeline[%1u] with chained VkPipelineVertexInputDivisorStateCreateInfoEXT, " |
| "pVertexBindingDivisors[%1u] divisor must not be 0 when vertexAttributeInstanceRateZeroDivisor feature is not " |
| "enabled.", |
| i, j); |
| } |
| if ((1 != vibdd->divisor) && !enabled_features.vtx_attrib_divisor_features.vertexAttributeInstanceRateDivisor) { |
| skip |= LogError( |
| device, "VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateDivisor-02229", |
| "vkCreateGraphicsPipelines(): Pipeline[%1u] with chained VkPipelineVertexInputDivisorStateCreateInfoEXT, " |
| "pVertexBindingDivisors[%1u] divisor (%1u) must be 1 when vertexAttributeInstanceRateDivisor feature is not " |
| "enabled.", |
| i, j, vibdd->divisor); |
| } |
| |
| // Find the corresponding binding description and validate input rate setting |
| bool failed_01871 = true; |
| for (size_t k = 0; k < pipe_state->vertex_input_state->binding_descriptions.size(); k++) { |
| if ((vibdd->binding == pipe_state->vertex_input_state->binding_descriptions[k].binding) && |
| (VK_VERTEX_INPUT_RATE_INSTANCE == pipe_state->vertex_input_state->binding_descriptions[k].inputRate)) { |
| failed_01871 = false; |
| break; |
| } |
| } |
| if (failed_01871) { // Description not found, or has incorrect inputRate value |
| skip |= LogError( |
| device, "VUID-VkVertexInputBindingDivisorDescriptionEXT-inputRate-01871", |
| "vkCreateGraphicsPipelines(): Pipeline[%1u] with chained VkPipelineVertexInputDivisorStateCreateInfoEXT, " |
| "pVertexBindingDivisors[%1u] specifies binding index (%1u), but that binding index's " |
| "VkVertexInputBindingDescription.inputRate member is not VK_VERTEX_INPUT_RATE_INSTANCE.", |
| i, j, vibdd->binding); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineCacheControlFlags(VkPipelineCreateFlags flags, uint32_t index, const char *caller_name, |
| const char *vuid) const { |
| bool skip = false; |
| if (enabled_features.core13.pipelineCreationCacheControl == VK_FALSE) { |
| const VkPipelineCreateFlags invalid_flags = |
| VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT | VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT; |
| if ((flags & invalid_flags) != 0) { |
| skip |= LogError(device, vuid, |
| "%s(): pipelineCreationCacheControl is turned off but pipeline[%u] has VkPipelineCreateFlags " |
| "containing VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT or " |
| "VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT", |
| caller_name, index); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreatePipelineCache(VkDevice device, const VkPipelineCacheCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkPipelineCache *pPipelineCache) const { |
| bool skip = false; |
| if (enabled_features.core13.pipelineCreationCacheControl == VK_FALSE) { |
| if ((pCreateInfo->flags & VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT) != 0) { |
| skip |= LogError(device, "VUID-VkPipelineCacheCreateInfo-pipelineCreationCacheControl-02892", |
| "vkCreatePipelineCache(): pipelineCreationCacheControl is turned off but pCreateInfo::flags contains " |
| "VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t count, |
| const VkGraphicsPipelineCreateInfo *pCreateInfos, |
| const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines, |
| void *cgpl_state_data) const { |
| bool skip = StateTracker::PreCallValidateCreateGraphicsPipelines(device, pipelineCache, count, pCreateInfos, pAllocator, |
| pPipelines, cgpl_state_data); |
| create_graphics_pipeline_api_state *cgpl_state = reinterpret_cast<create_graphics_pipeline_api_state *>(cgpl_state_data); |
| |
| for (uint32_t i = 0; i < count; i++) { |
| const auto &pipe_state = cgpl_state->pipe_state[i]; |
| if (!pipe_state->RenderPassState() && |
| (pipe_state->pre_raster_state || pipe_state->fragment_shader_state || pipe_state->fragment_output_state)) { |
| if ((api_version < VK_API_VERSION_1_3) && (!enabled_features.core13.dynamicRendering)) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06575", |
| "vkCreateGraphicsPipelines() pCreateInfo[%" PRIu32 "]: renderpass must not be VK_NULL_HANDLE.", i); |
| return true; |
| } else if (!enabled_features.core13.dynamicRendering) { |
| skip |= LogError(device, "VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576", |
| "vkCreateGraphicsPipeline: pCreateInfos[%" PRIu32 |
| "].renderPass is VK_NULL_HANDLE but dynamicRendering is not enabled.", |
| i); |
| |
| return true; |
| } |
| } |
| } |
| |
| for (uint32_t i = 0; i < count; i++) { |
| skip |= ValidatePipelineLocked(cgpl_state->pipe_state, i); |
| } |
| |
| for (uint32_t i = 0; i < count; i++) { |
| skip |= ValidatePipelineUnlocked(cgpl_state->pipe_state[i].get(), i); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_ext_vertex_attribute_divisor)) { |
| skip |= ValidatePipelineVertexDivisors(cgpl_state->pipe_state, count, pCreateInfos); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) { |
| for (uint32_t i = 0; i < count; ++i) { |
| // Validate depth-stencil state |
| auto raster_state_ci = pCreateInfos[i].pRasterizationState; |
| if ((VK_FALSE == enabled_features.portability_subset_features.separateStencilMaskRef) && raster_state_ci && |
| (VK_CULL_MODE_NONE == raster_state_ci->cullMode)) { |
| auto depth_stencil_ci = pCreateInfos[i].pDepthStencilState; |
| if (depth_stencil_ci && (VK_TRUE == depth_stencil_ci->stencilTestEnable) && |
| (depth_stencil_ci->front.reference != depth_stencil_ci->back.reference)) { |
| skip |= LogError(device, "VUID-VkPipelineDepthStencilStateCreateInfo-separateStencilMaskRef-04453", |
| "Invalid Pipeline CreateInfo[%d] (portability error): VkStencilOpState::reference must be the " |
| "same for front and back", |
| i); |
| } |
| } |
| |
| // Validate color attachments |
| uint32_t subpass = pCreateInfos[i].subpass; |
| auto render_pass = Get<RENDER_PASS_STATE>(pCreateInfos[i].renderPass); |
| bool ignore_color_blend_state = pCreateInfos[i].pRasterizationState->rasterizerDiscardEnable || |
| render_pass->createInfo.pSubpasses[subpass].colorAttachmentCount == 0; |
| if ((VK_FALSE == enabled_features.portability_subset_features.constantAlphaColorBlendFactors) && |
| !ignore_color_blend_state) { |
| auto color_blend_state = pCreateInfos[i].pColorBlendState; |
| const auto attachments = color_blend_state->pAttachments; |
| for (uint32_t color_attachment_index = 0; i < color_blend_state->attachmentCount; ++i) { |
| if ((VK_BLEND_FACTOR_CONSTANT_ALPHA == attachments[color_attachment_index].srcColorBlendFactor) || |
| (VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA == attachments[color_attachment_index].srcColorBlendFactor)) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04454", |
| "Invalid Pipeline CreateInfo[%d] (portability error): srcColorBlendFactor for color attachment %d must " |
| "not be VK_BLEND_FACTOR_CONSTANT_ALPHA or VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA", |
| i, color_attachment_index); |
| } |
| if ((VK_BLEND_FACTOR_CONSTANT_ALPHA == attachments[color_attachment_index].dstColorBlendFactor) || |
| (VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA == attachments[color_attachment_index].dstColorBlendFactor)) { |
| skip |= LogError( |
| device, "VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04455", |
| "Invalid Pipeline CreateInfo[%d] (portability error): dstColorBlendFactor for color attachment %d must " |
| "not be VK_BLEND_FACTOR_CONSTANT_ALPHA or VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA", |
| i, color_attachment_index); |
| } |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t count, |
| const VkComputePipelineCreateInfo *pCreateInfos, |
| const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines, |
| void *ccpl_state_data) const { |
| bool skip = StateTracker::PreCallValidateCreateComputePipelines(device, pipelineCache, count, pCreateInfos, pAllocator, |
| pPipelines, ccpl_state_data); |
| |
| auto *ccpl_state = reinterpret_cast<create_compute_pipeline_api_state *>(ccpl_state_data); |
| for (uint32_t i = 0; i < count; i++) { |
| // TODO: Add Compute Pipeline Verification |
| skip |= ValidateComputePipelineShaderState(ccpl_state->pipe_state[i].get()); |
| skip |= ValidatePipelineCacheControlFlags(pCreateInfos->flags, i, "vkCreateComputePipelines", |
| "VUID-VkComputePipelineCreateInfo-pipelineCreationCacheControl-02875"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateRayTracingPipelinesNV(VkDevice device, VkPipelineCache pipelineCache, uint32_t count, |
| const VkRayTracingPipelineCreateInfoNV *pCreateInfos, |
| const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines, |
| void *crtpl_state_data) const { |
| bool skip = StateTracker::PreCallValidateCreateRayTracingPipelinesNV(device, pipelineCache, count, pCreateInfos, pAllocator, |
| pPipelines, crtpl_state_data); |
| |
| auto *crtpl_state = reinterpret_cast<create_ray_tracing_pipeline_api_state *>(crtpl_state_data); |
| for (uint32_t i = 0; i < count; i++) { |
| PIPELINE_STATE *pipeline = crtpl_state->pipe_state[i].get(); |
| const auto &create_info = pipeline->GetUnifiedCreateInfo().raytracing; |
| if (create_info.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT) { |
| std::shared_ptr<const PIPELINE_STATE> base_pipeline; |
| if (create_info.basePipelineIndex != -1) { |
| base_pipeline = crtpl_state->pipe_state[create_info.basePipelineIndex]; |
| } else if (create_info.basePipelineHandle != VK_NULL_HANDLE) { |
| base_pipeline = Get<PIPELINE_STATE>(create_info.basePipelineHandle); |
| } |
| if (!base_pipeline || !(base_pipeline->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT)) { |
| skip |= LogError( |
| device, "VUID-vkCreateRayTracingPipelinesNV-flags-03416", |
| "vkCreateRayTracingPipelinesNV: If the flags member of any element of pCreateInfos contains the " |
| "VK_PIPELINE_CREATE_DERIVATIVE_BIT flag," |
| "the base pipeline must have been created with the VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT flag set."); |
| } |
| } |
| skip |= ValidateRayTracingPipeline(pipeline, pCreateInfos[i].flags, /*isKHR*/ false); |
| skip |= ValidatePipelineCacheControlFlags(pCreateInfos[i].flags, i, "vkCreateRayTracingPipelinesNV", |
| "VUID-VkRayTracingPipelineCreateInfoNV-pipelineCreationCacheControl-02905"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateRayTracingPipelinesKHR(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| VkPipelineCache pipelineCache, uint32_t count, |
| const VkRayTracingPipelineCreateInfoKHR *pCreateInfos, |
| const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines, |
| void *crtpl_state_data) const { |
| bool skip = StateTracker::PreCallValidateCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, count, |
| pCreateInfos, pAllocator, pPipelines, crtpl_state_data); |
| |
| auto *crtpl_state = reinterpret_cast<create_ray_tracing_pipeline_khr_api_state *>(crtpl_state_data); |
| for (uint32_t i = 0; i < count; i++) { |
| PIPELINE_STATE *pipeline = crtpl_state->pipe_state[i].get(); |
| const auto &create_info = pipeline->GetUnifiedCreateInfo().raytracing; |
| if (create_info.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT) { |
| std::shared_ptr<const PIPELINE_STATE> base_pipeline; |
| if (create_info.basePipelineIndex != -1) { |
| base_pipeline = crtpl_state->pipe_state[create_info.basePipelineIndex]; |
| } else if (create_info.basePipelineHandle != VK_NULL_HANDLE) { |
| base_pipeline = Get<PIPELINE_STATE>(create_info.basePipelineHandle); |
| } |
| if (!base_pipeline || !(base_pipeline->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT)) { |
| skip |= LogError( |
| device, "VUID-vkCreateRayTracingPipelinesKHR-flags-03416", |
| "vkCreateRayTracingPipelinesKHR: If the flags member of any element of pCreateInfos contains the " |
| "VK_PIPELINE_CREATE_DERIVATIVE_BIT flag," |
| "the base pipeline must have been created with the VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT flag set."); |
| } |
| } |
| skip |= ValidateRayTracingPipeline(pipeline, pCreateInfos[i].flags, /*isKHR*/ true); |
| skip |= ValidatePipelineCacheControlFlags(pCreateInfos[i].flags, i, "vkCreateRayTracingPipelinesKHR", |
| "VUID-VkRayTracingPipelineCreateInfoKHR-pipelineCreationCacheControl-02905"); |
| if (create_info.pLibraryInfo) { |
| const std::vector<std::pair<const char *, VkPipelineCreateFlags>> vuid_map = { |
| {"VUID-VkRayTracingPipelineCreateInfoKHR-flags-04718", VK_PIPELINE_CREATE_RAY_TRACING_SKIP_AABBS_BIT_KHR}, |
| {"VUID-VkRayTracingPipelineCreateInfoKHR-flags-04719", VK_PIPELINE_CREATE_RAY_TRACING_SKIP_TRIANGLES_BIT_KHR}, |
| {"VUID-VkRayTracingPipelineCreateInfoKHR-flags-04720", |
| VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR}, |
| {"VUID-VkRayTracingPipelineCreateInfoKHR-flags-04721", |
| VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR}, |
| {"VUID-VkRayTracingPipelineCreateInfoKHR-flags-04722", |
| VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR}, |
| {"VUID-VkRayTracingPipelineCreateInfoKHR-flags-04723", VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR}, |
| }; |
| for (uint32_t j = 0; j < create_info.pLibraryInfo->libraryCount; ++j) { |
| const auto lib = Get<PIPELINE_STATE>(create_info.pLibraryInfo->pLibraries[j]); |
| if ((lib->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) == 0) { |
| skip |= LogError( |
| device, "VUID-VkPipelineLibraryCreateInfoKHR-pLibraries-03381", |
| "vkCreateRayTracingPipelinesKHR(): pCreateInfo[%" PRIu32 "].pLibraryInfo->pLibraries[%" PRIu32 |
| "] was not created with VK_PIPELINE_CREATE_LIBRARY_BIT_KHR.", i, j); |
| } |
| for (const auto &pair : vuid_map) { |
| if (create_info.flags & pair.second) { |
| if ((lib->GetPipelineCreateFlags() & pair.second) == 0) { |
| skip |= LogError( |
| device, pair.first, |
| "vkCreateRayTracingPipelinesKHR(): pCreateInfo[%" PRIu32 |
| "].flags contains %s bit, but pCreateInfo[%" PRIu32 |
| "].pLibraryInfo->pLibraries[%" PRIu32 "] was created without it.", |
| i, string_VkPipelineCreateFlags(pair.second).c_str(), i, j); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPipelineExecutablePropertiesKHR(VkDevice device, const VkPipelineInfoKHR *pPipelineInfo, |
| uint32_t *pExecutableCount, |
| VkPipelineExecutablePropertiesKHR *pProperties) const { |
| bool skip = false; |
| skip |= ValidatePipelineExecutableInfo(device, nullptr, "vkGetPipelineExecutablePropertiesKHR", |
| "VUID-vkGetPipelineExecutablePropertiesKHR-pipelineExecutableInfo-03270"); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineExecutableInfo(VkDevice device, const VkPipelineExecutableInfoKHR *pExecutableInfo, |
| const char *caller_name, const char *feature_vuid) const { |
| bool skip = false; |
| |
| if (!enabled_features.pipeline_exe_props_features.pipelineExecutableInfo) { |
| skip |= LogError(device, feature_vuid, "%s(): called when pipelineExecutableInfo feature is not enabled.", caller_name); |
| } |
| |
| // vkGetPipelineExecutablePropertiesKHR will not have struct to validate further |
| if (pExecutableInfo) { |
| auto pi = LvlInitStruct<VkPipelineInfoKHR>(); |
| pi.pipeline = pExecutableInfo->pipeline; |
| |
| // We could probably cache this instead of fetching it every time |
| uint32_t executable_count = 0; |
| DispatchGetPipelineExecutablePropertiesKHR(device, &pi, &executable_count, NULL); |
| |
| if (pExecutableInfo->executableIndex >= executable_count) { |
| skip |= LogError( |
| pExecutableInfo->pipeline, "VUID-VkPipelineExecutableInfoKHR-executableIndex-03275", |
| "%s(): VkPipelineExecutableInfo::executableIndex (%1u) must be less than the number of executables associated with " |
| "the pipeline (%1u) as returned by vkGetPipelineExecutablePropertiessKHR", |
| caller_name, pExecutableInfo->executableIndex, executable_count); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPipelineExecutableStatisticsKHR(VkDevice device, |
| const VkPipelineExecutableInfoKHR *pExecutableInfo, |
| uint32_t *pStatisticCount, |
| VkPipelineExecutableStatisticKHR *pStatistics) const { |
| bool skip = false; |
| skip |= ValidatePipelineExecutableInfo(device, pExecutableInfo, "vkGetPipelineExecutableStatisticsKHR", |
| "VUID-vkGetPipelineExecutableStatisticsKHR-pipelineExecutableInfo-03272"); |
| |
| auto pipeline_state = Get<PIPELINE_STATE>(pExecutableInfo->pipeline); |
| if (!(pipeline_state->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR)) { |
| skip |= LogError(pExecutableInfo->pipeline, "VUID-vkGetPipelineExecutableStatisticsKHR-pipeline-03274", |
| "vkGetPipelineExecutableStatisticsKHR called on a pipeline created without the " |
| "VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR flag set"); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPipelineExecutableInternalRepresentationsKHR( |
| VkDevice device, const VkPipelineExecutableInfoKHR *pExecutableInfo, uint32_t *pInternalRepresentationCount, |
| VkPipelineExecutableInternalRepresentationKHR *pStatistics) const { |
| bool skip = false; |
| skip |= ValidatePipelineExecutableInfo(device, pExecutableInfo, "vkGetPipelineExecutableInternalRepresentationsKHR", |
| "VUID-vkGetPipelineExecutableInternalRepresentationsKHR-pipelineExecutableInfo-03276"); |
| |
| auto pipeline_state = Get<PIPELINE_STATE>(pExecutableInfo->pipeline); |
| if (!(pipeline_state->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR)) { |
| skip |= LogError(pExecutableInfo->pipeline, "VUID-vkGetPipelineExecutableInternalRepresentationsKHR-pipeline-03278", |
| "vkGetPipelineExecutableInternalRepresentationsKHR called on a pipeline created without the " |
| "VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR flag set"); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateDescriptorSetLayout(VkDevice device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkDescriptorSetLayout *pSetLayout) const { |
| return cvdescriptorset::ValidateDescriptorSetLayoutCreateInfo( |
| this, pCreateInfo, IsExtEnabled(device_extensions.vk_khr_push_descriptor), |
| phys_dev_ext_props.push_descriptor_props.maxPushDescriptors, IsExtEnabled(device_extensions.vk_ext_descriptor_indexing), |
| &enabled_features.core12, &enabled_features.core13, &phys_dev_ext_props.inline_uniform_block_props, |
| &enabled_features.ray_tracing_acceleration_structure_features, &device_extensions); |
| } |
| |
| enum DSL_DESCRIPTOR_GROUPS { |
| DSL_TYPE_SAMPLERS = 0, |
| DSL_TYPE_UNIFORM_BUFFERS, |
| DSL_TYPE_STORAGE_BUFFERS, |
| DSL_TYPE_SAMPLED_IMAGES, |
| DSL_TYPE_STORAGE_IMAGES, |
| DSL_TYPE_INPUT_ATTACHMENTS, |
| DSL_TYPE_INLINE_UNIFORM_BLOCK, |
| DSL_TYPE_ACCELERATION_STRUCTURE, |
| DSL_TYPE_ACCELERATION_STRUCTURE_NV, |
| DSL_NUM_DESCRIPTOR_GROUPS |
| }; |
| |
| // Used by PreCallValidateCreatePipelineLayout. |
| // Returns an array of size DSL_NUM_DESCRIPTOR_GROUPS of the maximum number of descriptors used in any single pipeline stage |
| std::valarray<uint32_t> GetDescriptorCountMaxPerStage( |
| const DeviceFeatures *enabled_features, |
| const std::vector<std::shared_ptr<cvdescriptorset::DescriptorSetLayout const>> &set_layouts, bool skip_update_after_bind) { |
| // Identify active pipeline stages |
| std::vector<VkShaderStageFlags> stage_flags = {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT, |
| VK_SHADER_STAGE_COMPUTE_BIT}; |
| if (enabled_features->core.geometryShader) { |
| stage_flags.push_back(VK_SHADER_STAGE_GEOMETRY_BIT); |
| } |
| if (enabled_features->core.tessellationShader) { |
| stage_flags.push_back(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT); |
| stage_flags.push_back(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT); |
| } |
| |
| // Allow iteration over enum values |
| std::vector<DSL_DESCRIPTOR_GROUPS> dsl_groups = { |
| DSL_TYPE_SAMPLERS, |
| DSL_TYPE_UNIFORM_BUFFERS, |
| DSL_TYPE_STORAGE_BUFFERS, |
| DSL_TYPE_SAMPLED_IMAGES, |
| DSL_TYPE_STORAGE_IMAGES, |
| DSL_TYPE_INPUT_ATTACHMENTS, |
| DSL_TYPE_INLINE_UNIFORM_BLOCK, |
| DSL_TYPE_ACCELERATION_STRUCTURE, |
| DSL_TYPE_ACCELERATION_STRUCTURE_NV, |
| }; |
| |
| // Sum by layouts per stage, then pick max of stages per type |
| std::valarray<uint32_t> max_sum(0U, DSL_NUM_DESCRIPTOR_GROUPS); // max descriptor sum among all pipeline stages |
| for (auto stage : stage_flags) { |
| std::valarray<uint32_t> stage_sum(0U, DSL_NUM_DESCRIPTOR_GROUPS); // per-stage sums |
| for (const auto &dsl : set_layouts) { |
| if (!dsl) { |
| continue; |
| } |
| if (skip_update_after_bind && (dsl->GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT)) { |
| continue; |
| } |
| |
| for (uint32_t binding_idx = 0; binding_idx < dsl->GetBindingCount(); binding_idx++) { |
| const VkDescriptorSetLayoutBinding *binding = dsl->GetDescriptorSetLayoutBindingPtrFromIndex(binding_idx); |
| // Bindings with a descriptorCount of 0 are "reserved" and should be skipped |
| if (0 != (stage & binding->stageFlags) && binding->descriptorCount > 0) { |
| switch (binding->descriptorType) { |
| case VK_DESCRIPTOR_TYPE_SAMPLER: |
| stage_sum[DSL_TYPE_SAMPLERS] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: |
| stage_sum[DSL_TYPE_UNIFORM_BUFFERS] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: |
| case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: |
| stage_sum[DSL_TYPE_STORAGE_BUFFERS] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: |
| case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: |
| stage_sum[DSL_TYPE_SAMPLED_IMAGES] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: |
| case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: |
| stage_sum[DSL_TYPE_STORAGE_IMAGES] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: |
| stage_sum[DSL_TYPE_SAMPLED_IMAGES] += binding->descriptorCount; |
| stage_sum[DSL_TYPE_SAMPLERS] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: |
| stage_sum[DSL_TYPE_INPUT_ATTACHMENTS] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: |
| // count one block per binding. descriptorCount is number of bytes |
| stage_sum[DSL_TYPE_INLINE_UNIFORM_BLOCK]++; |
| break; |
| case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: |
| stage_sum[DSL_TYPE_ACCELERATION_STRUCTURE] += binding->descriptorCount; |
| break; |
| case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV: |
| stage_sum[DSL_TYPE_ACCELERATION_STRUCTURE_NV] += binding->descriptorCount; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| for (auto type : dsl_groups) { |
| max_sum[type] = std::max(stage_sum[type], max_sum[type]); |
| } |
| } |
| return max_sum; |
| } |
| |
| // Used by PreCallValidateCreatePipelineLayout. |
| // Returns a map indexed by VK_DESCRIPTOR_TYPE_* enum of the summed descriptors by type. |
| // Note: descriptors only count against the limit once even if used by multiple stages. |
| std::map<uint32_t, uint32_t> GetDescriptorSum( |
| const std::vector<std::shared_ptr<cvdescriptorset::DescriptorSetLayout const>> &set_layouts, bool skip_update_after_bind) { |
| std::map<uint32_t, uint32_t> sum_by_type; |
| for (const auto &dsl : set_layouts) { |
| if (!dsl) { |
| continue; |
| } |
| if (skip_update_after_bind && (dsl->GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT)) { |
| continue; |
| } |
| |
| for (uint32_t binding_idx = 0; binding_idx < dsl->GetBindingCount(); binding_idx++) { |
| const VkDescriptorSetLayoutBinding *binding = dsl->GetDescriptorSetLayoutBindingPtrFromIndex(binding_idx); |
| // Bindings with a descriptorCount of 0 are "reserved" and should be skipped |
| if (binding->descriptorCount > 0) { |
| if (binding->descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { |
| // count one block per binding. descriptorCount is number of bytes |
| sum_by_type[binding->descriptorType]++; |
| } else { |
| sum_by_type[binding->descriptorType] += binding->descriptorCount; |
| } |
| } |
| } |
| } |
| return sum_by_type; |
| } |
| |
| bool CoreChecks::PreCallValidateCreatePipelineLayout(VkDevice device, const VkPipelineLayoutCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkPipelineLayout *pPipelineLayout) const { |
| bool skip = false; |
| |
| std::vector<std::shared_ptr<cvdescriptorset::DescriptorSetLayout const>> set_layouts(pCreateInfo->setLayoutCount, nullptr); |
| unsigned int push_descriptor_set_count = 0; |
| { |
| for (uint32_t i = 0; i < pCreateInfo->setLayoutCount; ++i) { |
| set_layouts[i] = Get<cvdescriptorset::DescriptorSetLayout>(pCreateInfo->pSetLayouts[i]); |
| if (set_layouts[i]) { |
| if (set_layouts[i]->IsPushDescriptor()) ++push_descriptor_set_count; |
| if (set_layouts[i]->GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_HOST_ONLY_POOL_BIT_VALVE) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-04606", |
| "vkCreatePipelineLayout(): pCreateInfo->pSetLayouts[%" PRIu32 |
| "] was created with VK_DESCRIPTOR_SET_LAYOUT_CREATE_HOST_ONLY_POOL_BIT_VALVE bit.", |
| i); |
| } |
| } |
| } |
| } |
| |
| if (push_descriptor_set_count > 1) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00293", |
| "vkCreatePipelineLayout() Multiple push descriptor sets found."); |
| } |
| |
| // Max descriptors by type, within a single pipeline stage |
| std::valarray<uint32_t> max_descriptors_per_stage = GetDescriptorCountMaxPerStage(&enabled_features, set_layouts, true); |
| // Samplers |
| if (max_descriptors_per_stage[DSL_TYPE_SAMPLERS] > phys_dev_props.limits.maxPerStageDescriptorSamplers) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03016" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00287"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage sampler bindings count (%d) exceeds device " |
| "maxPerStageDescriptorSamplers limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_SAMPLERS], phys_dev_props.limits.maxPerStageDescriptorSamplers); |
| } |
| |
| // Uniform buffers |
| if (max_descriptors_per_stage[DSL_TYPE_UNIFORM_BUFFERS] > phys_dev_props.limits.maxPerStageDescriptorUniformBuffers) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03017" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00288"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage uniform buffer bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUniformBuffers limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_UNIFORM_BUFFERS], |
| phys_dev_props.limits.maxPerStageDescriptorUniformBuffers); |
| } |
| |
| // Storage buffers |
| if (max_descriptors_per_stage[DSL_TYPE_STORAGE_BUFFERS] > phys_dev_props.limits.maxPerStageDescriptorStorageBuffers) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03018" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00289"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage storage buffer bindings count (%d) exceeds device " |
| "maxPerStageDescriptorStorageBuffers limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_STORAGE_BUFFERS], |
| phys_dev_props.limits.maxPerStageDescriptorStorageBuffers); |
| } |
| |
| // Sampled images |
| if (max_descriptors_per_stage[DSL_TYPE_SAMPLED_IMAGES] > phys_dev_props.limits.maxPerStageDescriptorSampledImages) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03019" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00290"; |
| skip |= |
| LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage sampled image bindings count (%d) exceeds device " |
| "maxPerStageDescriptorSampledImages limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_SAMPLED_IMAGES], phys_dev_props.limits.maxPerStageDescriptorSampledImages); |
| } |
| |
| // Storage images |
| if (max_descriptors_per_stage[DSL_TYPE_STORAGE_IMAGES] > phys_dev_props.limits.maxPerStageDescriptorStorageImages) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03020" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-00291"; |
| skip |= |
| LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage storage image bindings count (%d) exceeds device " |
| "maxPerStageDescriptorStorageImages limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_STORAGE_IMAGES], phys_dev_props.limits.maxPerStageDescriptorStorageImages); |
| } |
| |
| // Input attachments |
| if (max_descriptors_per_stage[DSL_TYPE_INPUT_ATTACHMENTS] > phys_dev_props.limits.maxPerStageDescriptorInputAttachments) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03021" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01676"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage input attachment bindings count (%d) exceeds device " |
| "maxPerStageDescriptorInputAttachments limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_INPUT_ATTACHMENTS], |
| phys_dev_props.limits.maxPerStageDescriptorInputAttachments); |
| } |
| |
| // Inline uniform blocks |
| if (max_descriptors_per_stage[DSL_TYPE_INLINE_UNIFORM_BLOCK] > |
| phys_dev_ext_props.inline_uniform_block_props.maxPerStageDescriptorInlineUniformBlocks) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-02214" |
| : "VUID-VkPipelineLayoutCreateInfo-descriptorType-02212"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): max per-stage inline uniform block bindings count (%d) exceeds device " |
| "maxPerStageDescriptorInlineUniformBlocks limit (%d).", |
| max_descriptors_per_stage[DSL_TYPE_INLINE_UNIFORM_BLOCK], |
| phys_dev_ext_props.inline_uniform_block_props.maxPerStageDescriptorInlineUniformBlocks); |
| } |
| if (max_descriptors_per_stage[DSL_TYPE_ACCELERATION_STRUCTURE] > |
| phys_dev_ext_props.acc_structure_props.maxPerStageDescriptorUpdateAfterBindAccelerationStructures) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03572", |
| "vkCreatePipelineLayout(): max per-stage acceleration structure bindings count (%" PRIu32 ") exceeds device " |
| "maxPerStageDescriptorInlineUniformBlocks limit (%" PRIu32 ").", |
| max_descriptors_per_stage[DSL_TYPE_ACCELERATION_STRUCTURE], |
| phys_dev_ext_props.inline_uniform_block_props.maxPerStageDescriptorInlineUniformBlocks); |
| } |
| |
| // Total descriptors by type |
| // |
| std::map<uint32_t, uint32_t> sum_all_stages = GetDescriptorSum(set_layouts, true); |
| // Samplers |
| uint32_t sum = sum_all_stages[VK_DESCRIPTOR_TYPE_SAMPLER] + sum_all_stages[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]; |
| if (sum > phys_dev_props.limits.maxDescriptorSetSamplers) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03028" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01677"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of sampler bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetSamplers limit (%d).", |
| sum, phys_dev_props.limits.maxDescriptorSetSamplers); |
| } |
| |
| // Uniform buffers |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER] > phys_dev_props.limits.maxDescriptorSetUniformBuffers) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03029" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01678"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of uniform buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUniformBuffers limit (%d).", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER], phys_dev_props.limits.maxDescriptorSetUniformBuffers); |
| } |
| |
| // Dynamic uniform buffers |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > phys_dev_props.limits.maxDescriptorSetUniformBuffersDynamic) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03030" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01679"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of dynamic uniform buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUniformBuffersDynamic limit (%d).", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC], |
| phys_dev_props.limits.maxDescriptorSetUniformBuffersDynamic); |
| } |
| |
| // Storage buffers |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER] > phys_dev_props.limits.maxDescriptorSetStorageBuffers) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03031" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01680"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of storage buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetStorageBuffers limit (%d).", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER], phys_dev_props.limits.maxDescriptorSetStorageBuffers); |
| } |
| |
| // Dynamic storage buffers |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] > phys_dev_props.limits.maxDescriptorSetStorageBuffersDynamic) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03032" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01681"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of dynamic storage buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetStorageBuffersDynamic limit (%d).", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC], |
| phys_dev_props.limits.maxDescriptorSetStorageBuffersDynamic); |
| } |
| |
| // Sampled images |
| sum = sum_all_stages[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] + sum_all_stages[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] + |
| sum_all_stages[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]; |
| if (sum > phys_dev_props.limits.maxDescriptorSetSampledImages) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03033" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01682"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of sampled image bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetSampledImages limit (%d).", |
| sum, phys_dev_props.limits.maxDescriptorSetSampledImages); |
| } |
| |
| // Storage images |
| sum = sum_all_stages[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] + sum_all_stages[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]; |
| if (sum > phys_dev_props.limits.maxDescriptorSetStorageImages) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03034" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01683"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of storage image bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetStorageImages limit (%d).", |
| sum, phys_dev_props.limits.maxDescriptorSetStorageImages); |
| } |
| |
| // Input attachments |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT] > phys_dev_props.limits.maxDescriptorSetInputAttachments) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-03035" |
| : "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-01684"; |
| skip |= |
| LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of input attachment bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetInputAttachments limit (%d).", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT], phys_dev_props.limits.maxDescriptorSetInputAttachments); |
| } |
| |
| // Inline uniform blocks |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT] > |
| phys_dev_ext_props.inline_uniform_block_props.maxDescriptorSetInlineUniformBlocks) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_ext_descriptor_indexing) |
| ? "VUID-VkPipelineLayoutCreateInfo-descriptorType-02216" |
| : "VUID-VkPipelineLayoutCreateInfo-descriptorType-02213"; |
| skip |= LogError(device, vuid, |
| "vkCreatePipelineLayout(): sum of inline uniform block bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetInlineUniformBlocks limit (%d).", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT], |
| phys_dev_ext_props.inline_uniform_block_props.maxDescriptorSetInlineUniformBlocks); |
| } |
| |
| // Acceleration structures NV |
| if (sum_all_stages[VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV] > |
| phys_dev_ext_props.ray_tracing_propsNV.maxDescriptorSetAccelerationStructures) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-02381", |
| "vkCreatePipelineLayout(): sum of acceleration structures NV bindings among all stages (%" PRIu32 ") exceeds device " |
| "VkPhysicalDeviceRayTracingPropertiesNV::maxDescriptorSetAccelerationStructures limit (%" PRIu32 ").", |
| sum_all_stages[VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV], |
| phys_dev_ext_props.ray_tracing_propsNV.maxDescriptorSetAccelerationStructures); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_ext_descriptor_indexing)) { |
| // XXX TODO: replace with correct VU messages |
| |
| // Max descriptors by type, within a single pipeline stage |
| std::valarray<uint32_t> max_descriptors_per_stage_update_after_bind = |
| GetDescriptorCountMaxPerStage(&enabled_features, set_layouts, false); |
| // Samplers |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_SAMPLERS] > |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindSamplers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03022", |
| "vkCreatePipelineLayout(): max per-stage sampler bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindSamplers limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_SAMPLERS], |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindSamplers); |
| } |
| |
| // Uniform buffers |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_UNIFORM_BUFFERS] > |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindUniformBuffers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03023", |
| "vkCreatePipelineLayout(): max per-stage uniform buffer bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindUniformBuffers limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_UNIFORM_BUFFERS], |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindUniformBuffers); |
| } |
| |
| // Storage buffers |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_STORAGE_BUFFERS] > |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindStorageBuffers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03024", |
| "vkCreatePipelineLayout(): max per-stage storage buffer bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindStorageBuffers limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_STORAGE_BUFFERS], |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindStorageBuffers); |
| } |
| |
| // Sampled images |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_SAMPLED_IMAGES] > |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindSampledImages) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03025", |
| "vkCreatePipelineLayout(): max per-stage sampled image bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindSampledImages limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_SAMPLED_IMAGES], |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindSampledImages); |
| } |
| |
| // Storage images |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_STORAGE_IMAGES] > |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindStorageImages) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03026", |
| "vkCreatePipelineLayout(): max per-stage storage image bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindStorageImages limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_STORAGE_IMAGES], |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindStorageImages); |
| } |
| |
| // Input attachments |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_INPUT_ATTACHMENTS] > |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindInputAttachments) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-03027", |
| "vkCreatePipelineLayout(): max per-stage input attachment bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindInputAttachments limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_INPUT_ATTACHMENTS], |
| phys_dev_props_core12.maxPerStageDescriptorUpdateAfterBindInputAttachments); |
| } |
| |
| // Inline uniform blocks |
| if (max_descriptors_per_stage_update_after_bind[DSL_TYPE_INLINE_UNIFORM_BLOCK] > |
| phys_dev_ext_props.inline_uniform_block_props.maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-02215", |
| "vkCreatePipelineLayout(): max per-stage inline uniform block bindings count (%d) exceeds device " |
| "maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks limit (%d).", |
| max_descriptors_per_stage_update_after_bind[DSL_TYPE_INLINE_UNIFORM_BLOCK], |
| phys_dev_ext_props.inline_uniform_block_props.maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks); |
| } |
| |
| // Total descriptors by type, summed across all pipeline stages |
| // |
| std::map<uint32_t, uint32_t> sum_all_stages_update_after_bind = GetDescriptorSum(set_layouts, false); |
| // Samplers |
| sum = sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_SAMPLER] + |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]; |
| if (sum > phys_dev_props_core12.maxDescriptorSetUpdateAfterBindSamplers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03036", |
| "vkCreatePipelineLayout(): sum of sampler bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindSamplers limit (%d).", |
| sum, phys_dev_props_core12.maxDescriptorSetUpdateAfterBindSamplers); |
| } |
| |
| // Uniform buffers |
| if (sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER] > |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindUniformBuffers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03037", |
| "vkCreatePipelineLayout(): sum of uniform buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindUniformBuffers limit (%d).", |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER], |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindUniformBuffers); |
| } |
| |
| // Dynamic uniform buffers |
| if (sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) { |
| skip |= |
| LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03038", |
| "vkCreatePipelineLayout(): sum of dynamic uniform buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindUniformBuffersDynamic limit (%d).", |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC], |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic); |
| } |
| |
| // Storage buffers |
| if (sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER] > |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindStorageBuffers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03039", |
| "vkCreatePipelineLayout(): sum of storage buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindStorageBuffers limit (%d).", |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER], |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindStorageBuffers); |
| } |
| |
| // Dynamic storage buffers |
| if (sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] > |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) { |
| skip |= |
| LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03040", |
| "vkCreatePipelineLayout(): sum of dynamic storage buffer bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindStorageBuffersDynamic limit (%d).", |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC], |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic); |
| } |
| |
| // Sampled images |
| sum = sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] + |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] + |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]; |
| if (sum > phys_dev_props_core12.maxDescriptorSetUpdateAfterBindSampledImages) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03041", |
| "vkCreatePipelineLayout(): sum of sampled image bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindSampledImages limit (%d).", |
| sum, phys_dev_props_core12.maxDescriptorSetUpdateAfterBindSampledImages); |
| } |
| |
| // Storage images |
| sum = sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] + |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]; |
| if (sum > phys_dev_props_core12.maxDescriptorSetUpdateAfterBindStorageImages) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03042", |
| "vkCreatePipelineLayout(): sum of storage image bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindStorageImages limit (%d).", |
| sum, phys_dev_props_core12.maxDescriptorSetUpdateAfterBindStorageImages); |
| } |
| |
| // Input attachments |
| if (sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT] > |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindInputAttachments) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pSetLayouts-03043", |
| "vkCreatePipelineLayout(): sum of input attachment bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindInputAttachments limit (%d).", |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT], |
| phys_dev_props_core12.maxDescriptorSetUpdateAfterBindInputAttachments); |
| } |
| |
| // Inline uniform blocks |
| if (sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT] > |
| phys_dev_ext_props.inline_uniform_block_props.maxDescriptorSetUpdateAfterBindInlineUniformBlocks) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-descriptorType-02217", |
| "vkCreatePipelineLayout(): sum of inline uniform block bindings among all stages (%d) exceeds device " |
| "maxDescriptorSetUpdateAfterBindInlineUniformBlocks limit (%d).", |
| sum_all_stages_update_after_bind[VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT], |
| phys_dev_ext_props.inline_uniform_block_props.maxDescriptorSetUpdateAfterBindInlineUniformBlocks); |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_ext_fragment_density_map2)) { |
| uint32_t sum_subsampled_samplers = 0; |
| for (const auto &dsl : set_layouts) { |
| // find the number of subsampled samplers across all stages |
| // NOTE: this does not use the GetDescriptorSum patter because it needs the Get<SAMPLER_STATE> method |
| if ((dsl->GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT)) { |
| continue; |
| } |
| for (uint32_t binding_idx = 0; binding_idx < dsl->GetBindingCount(); binding_idx++) { |
| const VkDescriptorSetLayoutBinding *binding = dsl->GetDescriptorSetLayoutBindingPtrFromIndex(binding_idx); |
| |
| // Bindings with a descriptorCount of 0 are "reserved" and should be skipped |
| if (binding->descriptorCount > 0) { |
| if (((binding->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) || |
| (binding->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER)) && |
| (binding->pImmutableSamplers != nullptr)) { |
| for (uint32_t sampler_idx = 0; sampler_idx < binding->descriptorCount; sampler_idx++) { |
| auto state = Get<SAMPLER_STATE>(binding->pImmutableSamplers[sampler_idx]); |
| if (state && (state->createInfo.flags & (VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT | |
| VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT))) { |
| sum_subsampled_samplers++; |
| } |
| } |
| } |
| } |
| } |
| } |
| if (sum_subsampled_samplers > phys_dev_ext_props.fragment_density_map2_props.maxDescriptorSetSubsampledSamplers) { |
| skip |= LogError(device, "VUID-VkPipelineLayoutCreateInfo-pImmutableSamplers-03566", |
| "vkCreatePipelineLayout(): sum of sampler bindings with flags containing " |
| "VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT or " |
| "VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT among all stages(% d) " |
| "exceeds device maxDescriptorSetSubsampledSamplers limit (%d).", |
| sum_subsampled_samplers, |
| phys_dev_ext_props.fragment_density_map2_props.maxDescriptorSetSubsampledSamplers); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, |
| VkDescriptorPoolResetFlags flags) const { |
| // Make sure sets being destroyed are not currently in-use |
| if (disabled[object_in_use]) return false; |
| bool skip = false; |
| auto pool = Get<DESCRIPTOR_POOL_STATE>(descriptorPool); |
| if (pool && pool->InUse()) { |
| skip |= LogError(descriptorPool, "VUID-vkResetDescriptorPool-descriptorPool-00313", |
| "It is invalid to call vkResetDescriptorPool() with descriptor sets in use by a command buffer."); |
| } |
| return skip; |
| } |
| |
| // Ensure the pool contains enough descriptors and descriptor sets to satisfy |
| // an allocation request. Fills common_data with the total number of descriptors of each type required, |
| // as well as DescriptorSetLayout ptrs used for later update. |
| bool CoreChecks::PreCallValidateAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo *pAllocateInfo, |
| VkDescriptorSet *pDescriptorSets, void *ads_state_data) const { |
| StateTracker::PreCallValidateAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, ads_state_data); |
| |
| cvdescriptorset::AllocateDescriptorSetsData *ads_state = |
| reinterpret_cast<cvdescriptorset::AllocateDescriptorSetsData *>(ads_state_data); |
| // All state checks for AllocateDescriptorSets is done in single function |
| return ValidateAllocateDescriptorSets(pAllocateInfo, ads_state); |
| } |
| |
| bool CoreChecks::PreCallValidateFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t count, |
| const VkDescriptorSet *pDescriptorSets) const { |
| // Make sure that no sets being destroyed are in-flight |
| bool skip = false; |
| // First make sure sets being destroyed are not currently in-use |
| for (uint32_t i = 0; i < count; ++i) { |
| if (pDescriptorSets[i] != VK_NULL_HANDLE) { |
| skip |= ValidateIdleDescriptorSet(pDescriptorSets[i], "vkFreeDescriptorSets"); |
| } |
| } |
| auto pool_state = Get<DESCRIPTOR_POOL_STATE>(descriptorPool); |
| if (pool_state && !(VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT & pool_state->createInfo.flags)) { |
| // Can't Free from a NON_FREE pool |
| skip |= LogError(descriptorPool, "VUID-vkFreeDescriptorSets-descriptorPool-00312", |
| "It is invalid to call vkFreeDescriptorSets() with a pool created without setting " |
| "VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateUpdateDescriptorSets(VkDevice device, uint32_t descriptorWriteCount, |
| const VkWriteDescriptorSet *pDescriptorWrites, uint32_t descriptorCopyCount, |
| const VkCopyDescriptorSet *pDescriptorCopies) const { |
| // First thing to do is perform map look-ups. |
| // NOTE : UpdateDescriptorSets is somewhat unique in that it's operating on a number of DescriptorSets |
| // so we can't just do a single map look-up up-front, but do them individually in functions below |
| |
| // Now make call(s) that validate state, but don't perform state updates in this function |
| // Note, here DescriptorSets is unique in that we don't yet have an instance. Using a helper function in the |
| // namespace which will parse params and make calls into specific class instances |
| return ValidateUpdateDescriptorSets(descriptorWriteCount, pDescriptorWrites, descriptorCopyCount, pDescriptorCopies, |
| "vkUpdateDescriptorSets()"); |
| } |
| |
| static bool UniqueImageViews(const VkRenderingInfo* pRenderingInfo, VkImageView imageView) { |
| bool unique_views = true; |
| for (uint32_t i = 0; i < pRenderingInfo->colorAttachmentCount; ++i) { |
| if (pRenderingInfo->pColorAttachments[i].imageView == imageView) { |
| unique_views = false; |
| } |
| |
| if (pRenderingInfo->pColorAttachments[i].resolveImageView == imageView) { |
| unique_views = false; |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment) { |
| if (pRenderingInfo->pDepthAttachment->imageView == imageView) { |
| unique_views = false; |
| } |
| |
| if (pRenderingInfo->pDepthAttachment->resolveImageView == imageView) { |
| unique_views = false; |
| } |
| } |
| |
| if (pRenderingInfo->pStencilAttachment) { |
| if (pRenderingInfo->pStencilAttachment->imageView == imageView) { |
| unique_views = false; |
| } |
| |
| if (pRenderingInfo->pStencilAttachment->resolveImageView == imageView) { |
| unique_views = false; |
| } |
| } |
| return unique_views; |
| } |
| |
| bool CoreChecks::ValidateCmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo *pRenderingInfo, |
| CMD_TYPE cmd_type) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (!cb_state) return false; |
| bool skip = false; |
| const char *func_name = CommandTypeString(cmd_type); |
| |
| if (!enabled_features.core13.dynamicRendering) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBeginRendering-dynamicRendering-06446", "%s(): dynamicRendering is not enabled.", |
| func_name); |
| } |
| |
| if ((cb_state->createInfo.level == VK_COMMAND_BUFFER_LEVEL_SECONDARY) && |
| ((pRenderingInfo->flags & VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR) != 0)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBeginRendering-commandBuffer-06068", |
| "%s(): pRenderingInfo->flags must not include " |
| "VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR in a secondary command buffer.", |
| func_name); |
| } |
| |
| if (pRenderingInfo->viewMask == 0 && pRenderingInfo->layerCount == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-viewMask-06069", |
| "%s(): If viewMask is 0 (%" PRIu32 "), layerCount must not be 0 (%" PRIu32 ").", func_name, |
| pRenderingInfo->viewMask, pRenderingInfo->layerCount); |
| } |
| |
| auto rendering_fragment_shading_rate_attachment_info = |
| LvlFindInChain<VkRenderingFragmentShadingRateAttachmentInfoKHR>(pRenderingInfo->pNext); |
| if (rendering_fragment_shading_rate_attachment_info && |
| (rendering_fragment_shading_rate_attachment_info->imageView != VK_NULL_HANDLE)) { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_fragment_shading_rate_attachment_info->imageView); |
| if (pRenderingInfo->viewMask == 0) { |
| if (view_state->create_info.subresourceRange.layerCount != 1 && |
| view_state->create_info.subresourceRange.layerCount < pRenderingInfo->layerCount) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06123", |
| "%s(): imageView must have a layerCount (%" PRIu32 ") that is either equal to 1 or greater than or equal to " |
| "VkRenderingInfo::layerCount (%" PRIu32 ").", |
| func_name, view_state->create_info.subresourceRange.layerCount, pRenderingInfo->layerCount); |
| } |
| } else { |
| int highest_view_bit = MostSignificantBit(pRenderingInfo->viewMask); |
| int32_t layer_count = view_state->create_info.subresourceRange.layerCount; |
| if (layer_count != 1 && layer_count < highest_view_bit) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06124", |
| "%s(): imageView must have a layerCount (%" PRIi32 ") that either is equal to 1 or greater than " |
| " or equal to the index of the most significant bit in viewMask (%d)", |
| func_name, layer_count, highest_view_bit); |
| } |
| } |
| |
| if (UniqueImageViews(pRenderingInfo, rendering_fragment_shading_rate_attachment_info->imageView) == false) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06125", |
| "%s(): imageView or resolveImageView member of pDepthAttachment, pStencilAttachment, or any element " |
| "of pColorAttachments must not equal VkRenderingFragmentShadingRateAttachmentInfoKHR->vimageView.", |
| func_name); |
| } |
| |
| if (rendering_fragment_shading_rate_attachment_info->imageLayout != VK_IMAGE_LAYOUT_GENERAL && |
| rendering_fragment_shading_rate_attachment_info->imageLayout != |
| VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06147", |
| "%s(): VkRenderingFragmentShadingRateAttachmentInfoKHR->layout (%s) must be VK_IMAGE_LAYOUT_GENERAL " |
| "or VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR.", |
| func_name, string_VkImageLayout(rendering_fragment_shading_rate_attachment_info->imageLayout)); |
| } |
| |
| if (!IsPowerOfTwo(rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06149", |
| "%s(): shadingRateAttachmentTexelSize.width (%u) must be a power of two.", func_name, |
| rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width); |
| } |
| |
| auto max_frs_attach_texel_width = |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSize.width; |
| if (rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width > max_frs_attach_texel_width) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06150", |
| "%s(): shadingRateAttachmentTexelSize.width (%u) must be less than or equal to " |
| "maxFragmentShadingRateAttachmentTexelSize.width (%u).", |
| func_name, rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width, |
| max_frs_attach_texel_width); |
| } |
| |
| auto min_frs_attach_texel_width = |
| phys_dev_ext_props.fragment_shading_rate_props.minFragmentShadingRateAttachmentTexelSize.width; |
| if (rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width < min_frs_attach_texel_width) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06151", |
| "%s(): shadingRateAttachmentTexelSize.width (%u) must be greater than or equal to " |
| "minFragmentShadingRateAttachmentTexelSize.width (%u).", |
| func_name, rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width, |
| min_frs_attach_texel_width); |
| } |
| |
| if (!IsPowerOfTwo(rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06152", |
| "%s(): shadingRateAttachmentTexelSize.height (%u) must be a power of two.", func_name, |
| rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height); |
| } |
| |
| auto max_frs_attach_texel_height = |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSize.height; |
| if (rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height > max_frs_attach_texel_height) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06153", |
| "%s(): shadingRateAttachmentTexelSize.height (%u) must be less than or equal to " |
| "maxFragmentShadingRateAttachmentTexelSize.height (%u).", |
| func_name, rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height, |
| max_frs_attach_texel_height); |
| } |
| |
| auto min_frs_attach_texel_height = |
| phys_dev_ext_props.fragment_shading_rate_props.minFragmentShadingRateAttachmentTexelSize.height; |
| if (rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height < min_frs_attach_texel_height) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06154", |
| "%s(): shadingRateAttachmentTexelSize.height (%u) must be greater than or equal to " |
| "minFragmentShadingRateAttachmentTexelSize.height (%u).", |
| func_name, rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height, |
| min_frs_attach_texel_height); |
| } |
| |
| auto max_frs_attach_texel_aspect_ratio = |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSizeAspectRatio; |
| if ((rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width / |
| rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height) > |
| max_frs_attach_texel_aspect_ratio) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06155", |
| "%s(): the quotient of shadingRateAttachmentTexelSize.width (%u) and shadingRateAttachmentTexelSize.height (%u) " |
| "must be less than or equal to maxFragmentShadingRateAttachmentTexelSizeAspectRatio (%u).", |
| func_name, rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width, |
| rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height, |
| max_frs_attach_texel_aspect_ratio); |
| } |
| |
| if ((rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height / |
| rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width) > |
| max_frs_attach_texel_aspect_ratio) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkRenderingFragmentShadingRateAttachmentInfoKHR-imageView-06156", |
| "%s(): the quotient of shadingRateAttachmentTexelSize.height (%u) and shadingRateAttachmentTexelSize.width (%u) " |
| "must be less than or equal to maxFragmentShadingRateAttachmentTexelSizeAspectRatio (%u).", |
| func_name, rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.height, |
| rendering_fragment_shading_rate_attachment_info->shadingRateAttachmentTexelSize.width, |
| max_frs_attach_texel_aspect_ratio); |
| } |
| } |
| |
| if (!(IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples) || |
| IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples))) { |
| uint32_t first_sample_count_attachment = VK_ATTACHMENT_UNUSED; |
| for (uint32_t j = 0; j < pRenderingInfo->colorAttachmentCount; ++j) { |
| if (pRenderingInfo->pColorAttachments[j].imageView != VK_NULL_HANDLE) { |
| const auto image_view = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pColorAttachments[j].imageView); |
| first_sample_count_attachment = (first_sample_count_attachment == VK_ATTACHMENT_UNUSED) |
| ? static_cast<uint32_t>(image_view->samples) |
| : first_sample_count_attachment; |
| if (first_sample_count_attachment != image_view->samples) { |
| skip |= |
| LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06070", |
| "%s(): Color attachment ref %" PRIu32 |
| " has sample count %s, whereas first used color " |
| "attachment ref has sample count %" PRIu32 ".", |
| func_name, j, string_VkSampleCountFlagBits(image_view->samples), first_sample_count_attachment); |
| } |
| } |
| } |
| if (pRenderingInfo->pDepthAttachment && pRenderingInfo->pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| const auto image_view = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pDepthAttachment->imageView); |
| first_sample_count_attachment = (first_sample_count_attachment == VK_ATTACHMENT_UNUSED) |
| ? static_cast<uint32_t>(image_view->samples) |
| : first_sample_count_attachment; |
| if (first_sample_count_attachment != image_view->samples) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06070", |
| "%s(): Depth attachment ref has sample count %s, whereas first used color " |
| "attachment ref has sample count %" PRIu32 ".", |
| func_name, string_VkSampleCountFlagBits(image_view->samples), (first_sample_count_attachment)); |
| } |
| } |
| if (pRenderingInfo->pStencilAttachment && pRenderingInfo->pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| const auto image_view = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pStencilAttachment->imageView); |
| first_sample_count_attachment = (first_sample_count_attachment == VK_ATTACHMENT_UNUSED) |
| ? static_cast<uint32_t>(image_view->samples) |
| : first_sample_count_attachment; |
| if (first_sample_count_attachment != image_view->samples) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06070", |
| "%s(): Stencil attachment ref has sample count %s, whereas another " |
| "attachment ref has sample count %" PRIu32 ".", |
| func_name, string_VkSampleCountFlagBits(image_view->samples), (first_sample_count_attachment)); |
| } |
| } |
| } |
| |
| if (pRenderingInfo->colorAttachmentCount > phys_dev_props.limits.maxColorAttachments) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06106", |
| "%s(): colorAttachmentCount (%u) must be less than or equal to " |
| "VkPhysicalDeviceLimits::maxColorAttachments (%u).", |
| func_name, pRenderingInfo->colorAttachmentCount, phys_dev_props.limits.maxColorAttachments); |
| } |
| |
| auto fragment_density_map_attachment_info = |
| LvlFindInChain<VkRenderingFragmentDensityMapAttachmentInfoEXT>(pRenderingInfo->pNext); |
| if (fragment_density_map_attachment_info) { |
| if (!enabled_features.fragment_density_map_features.fragmentDensityMapNonSubsampledImages) { |
| for (uint32_t j = 0; j < pRenderingInfo->colorAttachmentCount; ++j) { |
| if (pRenderingInfo->pColorAttachments[j].imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pColorAttachments[j].imageView); |
| if (!(image_view_state->create_info.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06107", |
| "%s(): color image must be created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT.", func_name); |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment && (pRenderingInfo->pDepthAttachment->imageView != VK_NULL_HANDLE)) { |
| auto depth_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pDepthAttachment->imageView); |
| if (!(depth_view_state->create_info.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06107", |
| "%s(): depth image must be created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT.", func_name); |
| } |
| } |
| |
| if (pRenderingInfo->pStencilAttachment && (pRenderingInfo->pStencilAttachment->imageView != VK_NULL_HANDLE)) { |
| auto stencil_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pStencilAttachment->imageView); |
| if (!(stencil_view_state->create_info.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06107", |
| "%s(): stencil image must be created with VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT.", func_name); |
| } |
| } |
| } |
| |
| if (UniqueImageViews(pRenderingInfo, fragment_density_map_attachment_info->imageView) == false) { |
| skip |= |
| LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06116", |
| "%s(): imageView or resolveImageView member of pDepthAttachment, pStencilAttachment, or any" |
| "element of pColorAttachments must not equal VkRenderingFragmentDensityMapAttachmentInfoEXT->imageView.", |
| func_name); |
| } |
| |
| if (fragment_density_map_attachment_info->imageView != VK_NULL_HANDLE) { |
| auto fragment_density_map_view_state = Get<IMAGE_VIEW_STATE>(fragment_density_map_attachment_info->imageView); |
| int32_t layer_count = fragment_density_map_view_state->create_info.subresourceRange.layerCount; |
| if ((pRenderingInfo->viewMask == 0) && (layer_count != 1)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06109", |
| "%s(): imageView of VkRenderingFragmentDensityMapAttachmentInfoEXT must " |
| "have a laycount (" |
| "%" PRIi32 ") equal to 1 when viewMask is equal to 0", |
| func_name, layer_count); |
| } |
| |
| if ((pRenderingInfo->viewMask != 0) && |
| (layer_count < MostSignificantBit(pRenderingInfo->viewMask))) { |
| skip |= |
| LogError(commandBuffer, "VUID-VkRenderingInfo-imageView-06108", |
| "%s(): imageView of VkRenderingFragmentDensityMapAttachmentInfoEXT must " |
| "have a laycount (" |
| "%" PRIi32 ") greater than or equal to the most significant bit in viewMask (%" PRIu32 ")", |
| func_name, layer_count, pRenderingInfo->viewMask); |
| } |
| } |
| } |
| |
| if ((enabled_features.core11.multiview == VK_FALSE) && (pRenderingInfo->viewMask != 0)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-multiview-06127", |
| "%s(): If the multiview feature is not enabled, viewMask must be 0 (%u).", func_name, |
| pRenderingInfo->viewMask); |
| } |
| |
| auto chained_device_group_struct = LvlFindInChain<VkDeviceGroupRenderPassBeginInfo>(pRenderingInfo->pNext); |
| if (!chained_device_group_struct || (chained_device_group_struct && chained_device_group_struct->deviceRenderAreaCount == 0)) { |
| if (pRenderingInfo->renderArea.offset.x < 0) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06077", |
| "%s(): renderArea.offset.x is %d and must be greater than 0.", func_name, |
| pRenderingInfo->renderArea.offset.x); |
| } |
| |
| if (pRenderingInfo->renderArea.offset.y < 0) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06078", |
| "%s(): renderArea.offset.y is %d and must be greater than 0.", func_name, |
| pRenderingInfo->renderArea.offset.y); |
| } |
| |
| for (uint32_t j = 0; j < pRenderingInfo->colorAttachmentCount; ++j) { |
| if (pRenderingInfo->pColorAttachments[j].imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pColorAttachments[j].imageView); |
| IMAGE_STATE *image_state = image_view_state->image_state.get(); |
| if (!(image_state->createInfo.extent.width >= |
| pRenderingInfo->renderArea.offset.x + pRenderingInfo->renderArea.extent.width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06079", |
| "%s(): width of the pColorAttachments[%u].imageView: %u must be greater than" |
| "renderArea.offset.x + renderArea.extent.width.", |
| func_name, j, image_state->createInfo.extent.width); |
| } |
| if (!(image_state->createInfo.extent.height >= |
| pRenderingInfo->renderArea.offset.y + pRenderingInfo->renderArea.extent.height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06080", |
| "%s(): height of the pColorAttachments[%u].imageView: %u must be greater than" |
| "renderArea.offset.y + renderArea.extent.height.", |
| func_name, j, image_state->createInfo.extent.height); |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment != VK_NULL_HANDLE && pRenderingInfo->pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| auto depth_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pDepthAttachment->imageView); |
| IMAGE_STATE *image_state = depth_view_state->image_state.get(); |
| if (!(image_state->createInfo.extent.width >= |
| pRenderingInfo->renderArea.offset.x + pRenderingInfo->renderArea.extent.width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06079", |
| "%s(): width of the pDepthAttachment->imageView: %u must be greater than" |
| "renderArea.offset.x + renderArea.extent.width.", |
| func_name, image_state->createInfo.extent.width); |
| } |
| if (!(image_state->createInfo.extent.height >= |
| pRenderingInfo->renderArea.offset.y + pRenderingInfo->renderArea.extent.height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06080", |
| "%s(): height of the pDepthAttachment->imageView: %u must be greater than" |
| "renderArea.offset.y + renderArea.extent.height.", |
| func_name, image_state->createInfo.extent.height); |
| } |
| } |
| |
| if (pRenderingInfo->pStencilAttachment != VK_NULL_HANDLE && |
| pRenderingInfo->pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| auto stencil_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pStencilAttachment->imageView); |
| IMAGE_STATE *image_state = stencil_view_state->image_state.get(); |
| if (!(image_state->createInfo.extent.width >= |
| pRenderingInfo->renderArea.offset.x + pRenderingInfo->renderArea.extent.width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06079", |
| "%s(): width of the pStencilAttachment->imageView: %u must be greater than" |
| "renderArea.offset.x + renderArea.extent.width.", |
| func_name, image_state->createInfo.extent.width); |
| } |
| if (!(image_state->createInfo.extent.height >= |
| pRenderingInfo->renderArea.offset.y + pRenderingInfo->renderArea.extent.height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06080", |
| "%s(): height of the pStencilAttachment->imageView: %u must be greater than" |
| "renderArea.offset.y + renderArea.extent.height.", |
| func_name, image_state->createInfo.extent.height); |
| } |
| } |
| } |
| |
| if (chained_device_group_struct) { |
| for (uint32_t deviceRenderAreaIndex = 0; deviceRenderAreaIndex < chained_device_group_struct->deviceRenderAreaCount; |
| ++deviceRenderAreaIndex) { |
| auto offset_x = chained_device_group_struct->pDeviceRenderAreas[deviceRenderAreaIndex].offset.x; |
| auto width = chained_device_group_struct->pDeviceRenderAreas[deviceRenderAreaIndex].extent.width; |
| if (!(offset_x >= 0)) { |
| skip |= LogError(commandBuffer, "VUID-VkDeviceGroupRenderPassBeginInfo-offset-06166", |
| "%s(): pDeviceRenderAreas[%u].offset.x: %d must be greater than or equal to 0.", func_name, |
| deviceRenderAreaIndex, offset_x); |
| } |
| if ((offset_x + width) > phys_dev_props.limits.maxFramebufferWidth) { |
| skip |= LogError(commandBuffer, "VUID-VkDeviceGroupRenderPassBeginInfo-offset-06168", |
| "vkCmdBeginRenderingKHR(): pDeviceRenderAreas[%" PRIu32 "] sum of offset.x (%" PRId32 |
| ") and extent.width (%" PRIu32 ") is greater than maxFramebufferWidth (%" PRIu32 ").", |
| deviceRenderAreaIndex, offset_x, width, phys_dev_props.limits.maxFramebufferWidth); |
| } |
| auto offset_y = chained_device_group_struct->pDeviceRenderAreas[deviceRenderAreaIndex].offset.y; |
| auto height = chained_device_group_struct->pDeviceRenderAreas[deviceRenderAreaIndex].extent.height; |
| if (!(offset_y >= 0)) { |
| skip |= LogError(commandBuffer, "VUID-VkDeviceGroupRenderPassBeginInfo-offset-06167", |
| "%s(): pDeviceRenderAreas[%u].offset.y: %d must be greater than or equal to 0.", func_name, |
| deviceRenderAreaIndex, offset_y); |
| } |
| if ((offset_y + height) > phys_dev_props.limits.maxFramebufferHeight) { |
| skip |= LogError(commandBuffer, "VUID-VkDeviceGroupRenderPassBeginInfo-offset-06169", |
| "vkCmdBeginRenderingKHR(): pDeviceRenderAreas[%" PRIu32 "] sum of offset.y (%" PRId32 |
| ") and extent.height (%" PRIu32 ") is greater than maxFramebufferHeight (%" PRIu32 ").", |
| deviceRenderAreaIndex, offset_y, height, phys_dev_props.limits.maxFramebufferHeight); |
| } |
| |
| for (uint32_t j = 0; j < pRenderingInfo->colorAttachmentCount; ++j) { |
| if (pRenderingInfo->pColorAttachments[j].imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pColorAttachments[j].imageView); |
| IMAGE_STATE *image_state = image_view_state->image_state.get(); |
| if (!(image_state->createInfo.extent.width >= offset_x + width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06083", |
| "%s(): width of the pColorAttachments[%" PRIu32 "].imageView: %" PRIu32 |
| " must be greater than or equal to" |
| "renderArea.offset.x (%" PRIu32 ") + renderArea.extent.width (%" PRIu32 ").", |
| func_name, j, image_state->createInfo.extent.width, offset_x, width); |
| } |
| if (!(image_state->createInfo.extent.height >= offset_y + height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06084", |
| "%s(): height of the pColorAttachments[%" PRIu32 "].imageView: %" PRIu32 |
| " must be greater than or equal to" |
| "renderArea.offset.y (%" PRIu32 ") + renderArea.extent.height (%" PRIu32 ").", |
| func_name, j, image_state->createInfo.extent.height, offset_y, height); |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment != VK_NULL_HANDLE) { |
| auto depth_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pDepthAttachment->imageView); |
| IMAGE_STATE *image_state = depth_view_state->image_state.get(); |
| if (!(image_state->createInfo.extent.width >= offset_x + width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06083", |
| "%s(): width of the pDepthAttachment->imageView: %" PRIu32 |
| " must be greater than or equal to" |
| "renderArea.offset.x (%" PRIu32 ") + renderArea.extent.width (%" PRIu32 ").", |
| func_name, image_state->createInfo.extent.width, offset_x, width); |
| } |
| if (!(image_state->createInfo.extent.height >= offset_y + height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06084", |
| "%s(): height of the pDepthAttachment->imageView: %" PRIu32 |
| " must be greater than or equal to" |
| "renderArea.offset.y (%" PRIu32 ") + renderArea.extent.height (%" PRIu32 ").", |
| func_name, image_state->createInfo.extent.height, offset_y, height); |
| } |
| } |
| |
| if (pRenderingInfo->pStencilAttachment != VK_NULL_HANDLE) { |
| auto stencil_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pStencilAttachment->imageView); |
| IMAGE_STATE *image_state = stencil_view_state->image_state.get(); |
| if (!(image_state->createInfo.extent.width >= offset_x + width)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06083", |
| "%s(): width of the pStencilAttachment->imageView: %" PRIu32 |
| " must be greater than or equal to" |
| "renderArea.offset.x (%" PRIu32 ") + renderArea.extent.width (%" PRIu32 ").", |
| func_name, image_state->createInfo.extent.width, offset_x, width); |
| } |
| if (!(image_state->createInfo.extent.height >= offset_y + height)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pNext-06084", |
| "%s(): height of the pStencilAttachment->imageView: %" PRIu32 |
| " must be greater than or equal to" |
| "renderArea.offset.y (%" PRIu32 ") + renderArea.extent.height(%" PRIu32 ").", |
| func_name, image_state->createInfo.extent.height, offset_y, height); |
| } |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment != nullptr && pRenderingInfo->pStencilAttachment != nullptr) { |
| if (pRenderingInfo->pDepthAttachment->imageView != VK_NULL_HANDLE && |
| pRenderingInfo->pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| if (!(pRenderingInfo->pDepthAttachment->imageView == pRenderingInfo->pStencilAttachment->imageView)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06085", |
| "%s(): imageView of pDepthAttachment and pStencilAttachment must be the same.", func_name); |
| } |
| |
| if ((phys_dev_props_core12.independentResolveNone == VK_FALSE) && |
| (pRenderingInfo->pDepthAttachment->resolveMode != pRenderingInfo->pStencilAttachment->resolveMode)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06104", |
| "%s(): The values of depthResolveMode (%s) and stencilResolveMode (%s) must be identical.", |
| func_name, string_VkResolveModeFlagBits(pRenderingInfo->pDepthAttachment->resolveMode), |
| string_VkResolveModeFlagBits(pRenderingInfo->pStencilAttachment->resolveMode)); |
| } |
| |
| if ((phys_dev_props_core12.independentResolve == VK_FALSE) && |
| (pRenderingInfo->pDepthAttachment->resolveMode != VK_RESOLVE_MODE_NONE) && |
| (pRenderingInfo->pStencilAttachment->resolveMode != VK_RESOLVE_MODE_NONE) && |
| (pRenderingInfo->pStencilAttachment->resolveMode != pRenderingInfo->pDepthAttachment->resolveMode)) { |
| skip |= LogError(device, "VUID-VkRenderingInfo-pDepthAttachment-06105", |
| "%s(): The values of depthResolveMode (%s) and stencilResolveMode (%s) must " |
| "be identical, or one of them must be VK_RESOLVE_MODE_NONE.", |
| func_name, string_VkResolveModeFlagBits(pRenderingInfo->pDepthAttachment->resolveMode), |
| string_VkResolveModeFlagBits(pRenderingInfo->pStencilAttachment->resolveMode)); |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment->resolveMode != VK_RESOLVE_MODE_NONE && |
| pRenderingInfo->pStencilAttachment->resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (!(pRenderingInfo->pDepthAttachment->resolveImageView == pRenderingInfo->pStencilAttachment->resolveImageView)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06086", |
| "%s(): resolveImageView of pDepthAttachment and pStencilAttachment must be the same.", func_name); |
| } |
| } |
| } |
| |
| for (uint32_t j = 0; j < pRenderingInfo->colorAttachmentCount; ++j) { |
| skip |= ValidateRenderingAttachmentInfo(commandBuffer, &pRenderingInfo->pColorAttachments[j], func_name); |
| |
| if (pRenderingInfo->pColorAttachments[j].imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pColorAttachments[j].imageView); |
| IMAGE_STATE *image_state = image_view_state->image_state.get(); |
| if (!(image_state->createInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06087", |
| "%s(): VkRenderingInfo->colorAttachment[%u] must have been created with VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.", |
| func_name, j); |
| } |
| |
| if (pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06090", |
| "%s(): imageLayout must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL or " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL.", |
| func_name); |
| } |
| |
| if (pRenderingInfo->pColorAttachments[j].resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (pRenderingInfo->pColorAttachments[j].resolveImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].resolveImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06091", |
| "%s(): resolveImageLayout must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL or " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL.", |
| func_name); |
| } |
| } |
| |
| if (pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06096", |
| "%s(): imageLayout must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL " |
| "or VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL.", |
| func_name); |
| } |
| |
| if (pRenderingInfo->pColorAttachments[j].resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (pRenderingInfo->pColorAttachments[j].resolveImageLayout == |
| VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].resolveImageLayout == |
| VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06097", |
| "%s(): resolveImageLayout must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL or " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL.", |
| func_name); |
| } |
| } |
| |
| if (pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].imageLayout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06100", |
| "%s(): imageLayout must not be VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL" |
| " or VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL" |
| " or VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL" |
| " or VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL.", |
| func_name); |
| } |
| |
| if (pRenderingInfo->pColorAttachments[j].resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (pRenderingInfo->pColorAttachments[j].resolveImageLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL || |
| pRenderingInfo->pColorAttachments[j].resolveImageLayout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-colorAttachmentCount-06101", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL or " |
| "VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL.", |
| func_name); |
| } |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment) { |
| skip |= ValidateRenderingAttachmentInfo(commandBuffer, pRenderingInfo->pDepthAttachment, func_name); |
| |
| if (pRenderingInfo->pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| auto depth_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pDepthAttachment->imageView); |
| IMAGE_STATE *image_state = depth_view_state->image_state.get(); |
| if (!(image_state->createInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06088", |
| "%s(): depth image must have been created with VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| func_name); |
| } |
| |
| if (pRenderingInfo->pDepthAttachment->imageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { |
| skip |= |
| LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06092", |
| "%s(): image must not have been created with VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.", func_name); |
| } |
| |
| if (pRenderingInfo->pDepthAttachment->resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (pRenderingInfo->pDepthAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06093", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.", func_name); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_maintenance2) && |
| pRenderingInfo->pDepthAttachment->resolveImageLayout == |
| VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06098", |
| "%s(): resolveImageLayout must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL.", |
| func_name); |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_depth_stencil_resolve) && |
| !(pRenderingInfo->pDepthAttachment->resolveMode & phys_dev_props_core12.supportedDepthResolveModes)) { |
| skip |= LogError(device, "VUID-VkRenderingInfo-pDepthAttachment-06102", |
| "%s(): Includes a resolveMode structure with invalid mode=%u.", func_name, |
| pRenderingInfo->pDepthAttachment->resolveMode); |
| } |
| } |
| |
| if (!FormatHasDepth(depth_view_state->create_info.format)) { |
| skip |= |
| LogError(commandBuffer, "VUID-VkRenderingInfo-pDepthAttachment-06547", |
| "%s(): pRenderingInfo->pDepthAttachment->imageView was created with a format (%s) that does not have " |
| "a depth aspect.", |
| func_name, string_VkFormat(depth_view_state->create_info.format)); |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pStencilAttachment != nullptr) { |
| skip |= ValidateRenderingAttachmentInfo(commandBuffer, pRenderingInfo->pStencilAttachment, func_name); |
| |
| if (pRenderingInfo->pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| auto stencil_view_state = Get<IMAGE_VIEW_STATE>(pRenderingInfo->pStencilAttachment->imageView); |
| IMAGE_STATE *image_state = stencil_view_state->image_state.get(); |
| if (!(image_state->createInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pStencilAttachment-06089", |
| "%s(): stencil image must have been created with VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| func_name); |
| } |
| |
| if (pRenderingInfo->pStencilAttachment->imageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pStencilAttachment-06094", |
| "%s(): imageLayout must not be VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.", func_name); |
| } |
| |
| if (pRenderingInfo->pStencilAttachment->resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (pRenderingInfo->pStencilAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pStencilAttachment-06095", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.", func_name); |
| } |
| if (pRenderingInfo->pStencilAttachment->resolveImageLayout == |
| VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingInfo-pStencilAttachment-06099", |
| "%s(): resolveImageLayout must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL.", |
| func_name); |
| } |
| if (!(pRenderingInfo->pStencilAttachment->resolveMode & phys_dev_props_core12.supportedStencilResolveModes)) { |
| skip |= LogError(device, "VUID-VkRenderingInfo-pStencilAttachment-06103", |
| "%s(): Includes a resolveMode structure with invalid mode (%s).", func_name, |
| string_VkResolveModeFlagBits(pRenderingInfo->pStencilAttachment->resolveMode)); |
| } |
| } |
| |
| if (!FormatHasStencil(stencil_view_state->create_info.format)) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkRenderingInfo-pStencilAttachment-06548", |
| "%s(): pRenderingInfo->pStencilAttachment->imageView was created with a format (%s) that does not have " |
| "a stencil aspect.", |
| func_name, string_VkFormat(stencil_view_state->create_info.format)); |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginRenderingKHR(VkCommandBuffer commandBuffer, const VkRenderingInfoKHR *pRenderingInfo) const { |
| return ValidateCmdBeginRendering(commandBuffer, pRenderingInfo, CMD_BEGINRENDERINGKHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginRendering(VkCommandBuffer commandBuffer, |
| const VkRenderingInfo *pRenderingInfo) const { |
| return ValidateCmdBeginRendering(commandBuffer, pRenderingInfo, CMD_BEGINRENDERING); |
| } |
| |
| bool CoreChecks::ValidateRenderingAttachmentInfo(VkCommandBuffer commandBuffer, const VkRenderingAttachmentInfo *pAttachment, |
| const char *func_name) const { |
| bool skip = false; |
| |
| if (pAttachment->imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>(pAttachment->imageView); |
| |
| if (pAttachment->imageLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06145", |
| "%s(): ImageLayout must not be VK_IMAGE_LAYOUT_PRESENT_SRC_KHR", func_name); |
| } |
| |
| if ((!FormatIsSINT(image_view_state->create_info.format) && !FormatIsUINT(image_view_state->create_info.format)) && |
| !(pAttachment->resolveMode == VK_RESOLVE_MODE_NONE || pAttachment->resolveMode == VK_RESOLVE_MODE_AVERAGE_BIT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06129", |
| "%s(): Current resolve mode (%s) must be VK_RESOLVE_MODE_NONE or " |
| "VK_RESOLVE_MODE_AVERAGE_BIT for non-integar formats (%s)", |
| func_name, string_VkResolveModeFlags(pAttachment->resolveMode).c_str(), |
| string_VkFormat(image_view_state->create_info.format)); |
| } |
| |
| if ((FormatIsSINT(image_view_state->create_info.format) || FormatIsUINT(image_view_state->create_info.format)) && |
| !(pAttachment->resolveMode == VK_RESOLVE_MODE_NONE || pAttachment->resolveMode == VK_RESOLVE_MODE_SAMPLE_ZERO_BIT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06130", |
| "%s(): Current resolve mode (%s) must be VK_RESOLVE_MODE_NONE or " |
| "VK_RESOLVE_MODE_SAMPLE_ZERO_BIT for integar formats (%s)", |
| func_name, string_VkResolveModeFlags(pAttachment->resolveMode).c_str(), |
| string_VkFormat(image_view_state->create_info.format)); |
| } |
| |
| if (pAttachment->imageLayout == VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06143", |
| "%s(): layout must not be VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR", func_name); |
| } |
| |
| if (pAttachment->imageLayout == VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06140", |
| "%s(): layout must not be VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT", func_name); |
| } |
| |
| if (pAttachment->resolveMode != VK_RESOLVE_MODE_NONE && image_view_state->samples == VK_SAMPLE_COUNT_1_BIT) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06132", |
| "%s(): Image sample count must not have a VK_SAMPLE_COUNT_1_BIT for Resolve Mode %s", func_name, |
| string_VkResolveModeFlags(pAttachment->resolveMode).c_str()); |
| } |
| |
| auto resolve_view_state = Get<IMAGE_VIEW_STATE>(pAttachment->resolveImageView); |
| if (resolve_view_state && (pAttachment->resolveMode != VK_RESOLVE_MODE_NONE) && |
| (resolve_view_state->samples != VK_SAMPLE_COUNT_1_BIT)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06133", |
| "%s(): resolveImageView sample count must have a VK_SAMPLE_COUNT_1_BIT for Resolve Mode %s", func_name, |
| string_VkResolveModeFlags(pAttachment->resolveMode).c_str()); |
| } |
| |
| if (pAttachment->resolveMode != VK_RESOLVE_MODE_NONE) { |
| if (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06146", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_PRESENT_SRC_KHR", func_name); |
| } |
| |
| if (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06144", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR", func_name); |
| } |
| |
| if (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06141", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT", func_name); |
| } |
| |
| if (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06142", |
| "%s(): resolveImageLayout must not be VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR", func_name); |
| } |
| |
| if (resolve_view_state && (image_view_state->create_info.format != resolve_view_state->create_info.format)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06134", |
| "%s(): resolveImageView format (%s) and ImageView format (%s) must have the same VkFormat", |
| func_name, string_VkFormat(resolve_view_state->create_info.format), |
| string_VkFormat(image_view_state->create_info.format)); |
| } |
| |
| if (((pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_UNDEFINED) || |
| (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) || |
| (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) || |
| (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) || |
| (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) || |
| (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_PREINITIALIZED))) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06136", |
| "%s(): resolveImageLayout (%s) must not be VK_IMAGE_LAYOUT_UNDEFINED, " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, or VK_IMAGE_LAYOUT_PREINITIALIZED", |
| func_name, string_VkImageLayout(pAttachment->resolveImageLayout)); |
| } |
| |
| if (((pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL) || |
| (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL))) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06137", |
| "%s(): resolveImageLayout (%s) must not be VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL", |
| func_name, string_VkImageLayout(pAttachment->resolveImageLayout)); |
| } |
| |
| if (FormatIsDepthOrStencil(image_view_state->create_info.format) && |
| !IsExtEnabled(device_extensions.vk_khr_depth_stencil_resolve)) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06131", |
| "%s(): VkRenderingAttachmentInfo::imageView format is %s, but resolveMode is %s.", func_name, |
| string_VkFormat(image_view_state->create_info.format), |
| string_VkResolveModeFlagBits(pAttachment->resolveMode)); |
| } |
| } |
| |
| if ((pAttachment->imageLayout == VK_IMAGE_LAYOUT_UNDEFINED) || |
| (pAttachment->imageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) || |
| (pAttachment->imageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) || |
| (pAttachment->imageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) || |
| (pAttachment->imageLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06135", |
| "%s(): layout (%s) must not be VK_IMAGE_LAYOUT_UNDEFINED VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, or VK_IMAGE_LAYOUT_PREINITIALIZED", |
| func_name, string_VkImageLayout(pAttachment->imageLayout)); |
| } |
| |
| if (pAttachment->imageLayout == VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06138", |
| "%s(): layout (%s) must not be VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV", func_name, |
| string_VkImageLayout(pAttachment->imageLayout)); |
| } |
| |
| if (pAttachment->resolveImageLayout == VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV) { |
| skip |= LogError(commandBuffer, "VUID-VkRenderingAttachmentInfo-imageView-06139", |
| "%s(): layout (%s) must not be VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV", func_name, |
| string_VkImageLayout(pAttachment->resolveImageLayout)); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndRenderingKHR(VkCommandBuffer commandBuffer) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (!cb_state) return false; |
| bool skip = false; |
| |
| if (cb_state->activeRenderPass) { |
| if ((cb_state->activeRenderPass->use_dynamic_rendering == false) && |
| (cb_state->activeRenderPass->use_dynamic_rendering_inherited == false)) { |
| skip |= LogError( |
| commandBuffer, "VUID-vkCmdEndRendering-None-06161", |
| "Calling vkCmdEndRenderingKHR() in a render pass instance that was not begun with vkCmdBeginRenderingKHR()."); |
| } |
| if (cb_state->activeRenderPass->use_dynamic_rendering_inherited == true) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndRendering-commandBuffer-06162", |
| "Calling vkCmdEndRenderingKHR() in a render pass instance that was not begun in this command buffer."); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndRendering(VkCommandBuffer commandBuffer) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (!cb_state) return false; |
| bool skip = false; |
| |
| if (cb_state->activeRenderPass) { |
| if (cb_state->activeRenderPass->use_dynamic_rendering == false) { |
| skip |= LogError( |
| commandBuffer, "VUID-vkCmdEndRendering-None-06161", |
| "Calling vkCmdEndRendering() in a render pass instance that was not begun with vkCmdBeginRendering()."); |
| } |
| if (cb_state->activeRenderPass->use_dynamic_rendering_inherited == true) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndRendering-commandBuffer-06162", |
| "Calling vkCmdEndRendering() in a render pass instance that was not begun in this command buffer."); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateBeginCommandBuffer(VkCommandBuffer commandBuffer, |
| const VkCommandBufferBeginInfo *pBeginInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (!cb_state) return false; |
| bool skip = false; |
| if (cb_state->InUse()) { |
| skip |= LogError(commandBuffer, "VUID-vkBeginCommandBuffer-commandBuffer-00049", |
| "Calling vkBeginCommandBuffer() on active %s before it has completed. You must check " |
| "command buffer fence before this call.", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| 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(commandBuffer, "VUID-vkBeginCommandBuffer-commandBuffer-02840", |
| "vkBeginCommandBuffer(): Primary %s can't have both VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT and " |
| "VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set.", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| } else { |
| // Secondary Command Buffer |
| const VkCommandBufferInheritanceInfo *info = pBeginInfo->pInheritanceInfo; |
| if (!info) { |
| skip |= LogError(commandBuffer, "VUID-vkBeginCommandBuffer-commandBuffer-00051", |
| "vkBeginCommandBuffer(): Secondary %s must have inheritance info.", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } else { |
| auto p_inherited_rendering_info = LvlFindInChain<VkCommandBufferInheritanceRenderingInfoKHR>(info->pNext); |
| |
| if ((pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) && |
| ((api_version >= VK_API_VERSION_1_3) || enabled_features.core13.dynamicRendering)) |
| { |
| 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(), "vkBeginCommandBuffer()", |
| "VUID-VkCommandBufferBeginInfo-flags-00055"); |
| } |
| } |
| |
| if (info->renderPass != VK_NULL_HANDLE) { |
| auto render_pass = Get<RENDER_PASS_STATE>(info->renderPass); |
| if (!render_pass) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferBeginInfo-flags-06000", |
| "vkBeginCommandBuffer(): Renderpass must be a valid VkRenderPass"); |
| } else { |
| if (info->subpass >= render_pass->createInfo.subpassCount) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferBeginInfo-flags-06001", |
| "vkBeginCommandBuffer(): Subpass member of pInheritanceInfo must be a valid subpass " |
| "index within pInheritanceInfo->renderPass"); |
| } |
| } |
| } else { |
| if (!p_inherited_rendering_info) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferBeginInfo-flags-06002", |
| "vkBeginCommandBuffer():The pNext chain of pInheritanceInfo must include a " |
| "VkCommandBufferInheritanceRenderingInfoKHR structure"); |
| } |
| } |
| } |
| |
| if (p_inherited_rendering_info) { |
| auto p_attachment_sample_count_info_amd = LvlFindInChain<VkAttachmentSampleCountInfoAMD>(info->pNext); |
| if (p_attachment_sample_count_info_amd && |
| p_attachment_sample_count_info_amd->colorAttachmentCount != p_inherited_rendering_info->colorAttachmentCount) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferBeginInfo-flags-06003", |
| "vkBeginCommandBuffer(): VkAttachmentSampleCountInfo{AMD,NV}->colorAttachmentCount[%u] must equal VkCommandBufferInheritanceRenderingInfoKHR->colorAttachmentCount[%u]", |
| p_attachment_sample_count_info_amd->colorAttachmentCount, p_inherited_rendering_info->colorAttachmentCount); |
| } |
| |
| const VkSampleCountFlags AllVkSampleCountFlagBits = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_2_BIT | VK_SAMPLE_COUNT_4_BIT | VK_SAMPLE_COUNT_8_BIT | VK_SAMPLE_COUNT_16_BIT | VK_SAMPLE_COUNT_32_BIT | VK_SAMPLE_COUNT_64_BIT; |
| |
| if ((p_inherited_rendering_info->colorAttachmentCount != 0) && |
| (p_inherited_rendering_info->rasterizationSamples & AllVkSampleCountFlagBits) == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-colorAttachmentCount-06004", |
| "vkBeginCommandBuffer(): VkCommandBufferInheritanceRenderingInfo->colorAttachmentCount (%" PRIu32 ") is not 0, rasterizationSamples (%s) must be valid VkSampleCountFlagBits value", |
| p_inherited_rendering_info->colorAttachmentCount, string_VkSampleCountFlagBits(p_inherited_rendering_info->rasterizationSamples)); |
| } |
| |
| if ((enabled_features.core.variableMultisampleRate == false) && |
| (p_inherited_rendering_info->rasterizationSamples & AllVkSampleCountFlagBits) == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-variableMultisampleRate-06005", |
| "vkBeginCommandBuffer(): If the variableMultisampleRate feature is not enabled, rasterizationSamples (%s) must be a valid VkSampleCountFlagBits", |
| string_VkSampleCountFlagBits(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 VkFormatFeatureFlags2KHR potential_format_features = GetPotentialFormatFeatures(attachment_format); |
| if ((potential_format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR) == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-pColorAttachmentFormats-06006", |
| "vkBeginCommandBuffer(): VkCommandBufferInheritanceRenderingInfo->pColorAttachmentFormats[%u] (%s) must be a format with potential format features that include VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", |
| i, string_VkFormat(attachment_format)); |
| } |
| } |
| } |
| } |
| |
| const VkFormatFeatureFlags2KHR 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 VkFormatFeatureFlags2KHR potential_format_features = GetPotentialFormatFeatures(depth_format); |
| if ((potential_format_features & valid_depth_stencil_format) == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06007", |
| "vkBeginCommandBuffer(): VkCommandBufferInheritanceRenderingInfo->depthAttachmentFormat (%s) must be a format with potential format features that include VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", |
| string_VkFormat(depth_format)); |
| } |
| if (!FormatHasDepth(depth_format)) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06540", |
| "vkBeginCommandBuffer(): VkCommandBufferInheritanceRenderingInfo->depthAttachmentFormat (%s) must be a " |
| "format with a depth aspect.", |
| string_VkFormat(depth_format)); |
| } |
| } |
| |
| const VkFormat stencil_format = p_inherited_rendering_info->stencilAttachmentFormat; |
| if (stencil_format != VK_FORMAT_UNDEFINED) { |
| const VkFormatFeatureFlags2KHR potential_format_features = GetPotentialFormatFeatures(stencil_format); |
| if ((potential_format_features & valid_depth_stencil_format) == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-06199", |
| "vkBeginCommandBuffer(): VkCommandBufferInheritanceRenderingInfo->stencilAttachmentFormat (%s) must be a format with potential format features that include VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", |
| string_VkFormat(stencil_format)); |
| } |
| } |
| |
| if ((depth_format != VK_FORMAT_UNDEFINED && stencil_format != VK_FORMAT_UNDEFINED) && (depth_format != stencil_format)) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06200", |
| "vkBeginCommandBuffer(): VkCommandBufferInheritanceRenderingInfoKHR->depthAttachmentFormat (%s) must equal VkCommandBufferInheritanceRenderingInfoKHR->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(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-multiview-06008", |
| "vkBeginCommandBuffer(): If the multiview feature is not enabled, viewMask must be 0 (%u)", |
| p_inherited_rendering_info->viewMask); |
| } |
| |
| if (MostSignificantBit(p_inherited_rendering_info->viewMask) >= |
| static_cast<int32_t>(phys_dev_props_core11.maxMultiviewViewCount)) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceRenderingInfo-viewMask-06009", |
| "vkBeginCommandBuffer(): Most significant bit VkCommandBufferInheritanceRenderingInfoKHR->viewMask(%u) must be less maxMultiviewViewCount(%u)", |
| 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(commandBuffer, "VUID-vkBeginCommandBuffer-commandBuffer-00052", |
| "vkBeginCommandBuffer(): Secondary %s must not have VK_QUERY_CONTROL_PRECISE_BIT if " |
| "occulusionQuery is disabled or the device does not support precise occlusion queries.", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| auto p_inherited_viewport_scissor_info = LvlFindInChain<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(commandBuffer, "VUID-VkCommandBufferInheritanceViewportScissorInfoNV-viewportScissor2D-04782", |
| "vkBeginCommandBuffer(): inheritedViewportScissor2D feature not enabled."); |
| } |
| if (!(pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceViewportScissorInfoNV-viewportScissor2D-04786", |
| "vkBeginCommandBuffer(): Secondary %s must be recorded with the" |
| "VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT if viewportScissor2D is VK_TRUE.", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| if (p_inherited_viewport_scissor_info->viewportDepthCount == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferInheritanceViewportScissorInfoNV-viewportScissor2D-04784", |
| "vkBeginCommandBuffer(): " |
| "If viewportScissor2D is VK_TRUE, then viewportDepthCount must be greater than 0."); |
| } |
| } |
| |
| // 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) { |
| auto render_pass = Get<RENDER_PASS_STATE>(info->renderPass); |
| if (render_pass) { |
| if (info->subpass >= render_pass->createInfo.subpassCount) { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferBeginInfo-flags-00054", |
| "vkBeginCommandBuffer(): Secondary %s must have a subpass index (%d) that is " |
| "less than the number of subpasses (%d).", |
| report_data->FormatHandle(commandBuffer).c_str(), info->subpass, |
| render_pass->createInfo.subpassCount); |
| } |
| } |
| } else { |
| skip |= LogError(commandBuffer, "VUID-VkCommandBufferBeginInfo-flags-00053", |
| "vkBeginCommandBuffer(): Flags contains VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, " |
| "the renderpass member of pInheritanceInfo must be a valid VkRenderPass."); |
| } |
| } |
| } |
| } |
| } |
| if (CB_RECORDING == cb_state->state) { |
| skip |= LogError(commandBuffer, "VUID-vkBeginCommandBuffer-commandBuffer-00049", |
| "vkBeginCommandBuffer(): Cannot call Begin on %s in the RECORDING state. Must first call " |
| "vkEndCommandBuffer().", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } else if (CB_RECORDED == cb_state->state || CB_INVALID_COMPLETE == 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)) { |
| LogObjectList objlist(commandBuffer); |
| objlist.add(cmd_pool); |
| skip |= LogError(objlist, "VUID-vkBeginCommandBuffer-commandBuffer-00050", |
| "Call to vkBeginCommandBuffer() on %s attempts to implicitly reset cmdBuffer created from " |
| "%s that does NOT have the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit set.", |
| report_data->FormatHandle(commandBuffer).c_str(), report_data->FormatHandle(cmd_pool).c_str()); |
| } |
| } |
| auto chained_device_group_struct = LvlFindInChain<VkDeviceGroupCommandBufferBeginInfo>(pBeginInfo->pNext); |
| if (chained_device_group_struct) { |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(chained_device_group_struct->deviceMask, commandBuffer, |
| "VUID-VkDeviceGroupCommandBufferBeginInfo-deviceMask-00106"); |
| skip |= ValidateDeviceMaskToZero(chained_device_group_struct->deviceMask, commandBuffer, |
| "VUID-VkDeviceGroupCommandBufferBeginInfo-deviceMask-00107"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateEndCommandBuffer(VkCommandBuffer commandBuffer) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (!cb_state) return false; |
| bool skip = false; |
| 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.get(), "vkEndCommandBuffer()", "VUID-vkEndCommandBuffer-commandBuffer-00060"); |
| } |
| |
| if (cb_state->state == CB_INVALID_COMPLETE || cb_state->state == CB_INVALID_INCOMPLETE) { |
| skip |= ReportInvalidCommandBuffer(cb_state.get(), "vkEndCommandBuffer()"); |
| } else if (CB_RECORDING != cb_state->state) { |
| skip |= LogError( |
| commandBuffer, "VUID-vkEndCommandBuffer-commandBuffer-00059", |
| "vkEndCommandBuffer(): Cannot call End on %s when not in the RECORDING state. Must first call vkBeginCommandBuffer().", |
| report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| |
| for (const auto &query : cb_state->activeQueries) { |
| skip |= LogError(commandBuffer, "VUID-vkEndCommandBuffer-commandBuffer-00061", |
| "vkEndCommandBuffer(): Ending command buffer with in progress query: %s, query %d.", |
| report_data->FormatHandle(query.pool).c_str(), query.query); |
| } |
| if (cb_state->conditional_rendering_active) { |
| skip |= LogError(commandBuffer, "VUID-vkEndCommandBuffer-None-01978", |
| "vkEndCommandBuffer(): Ending command buffer with active conditional rendering."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateResetCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) 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)) { |
| LogObjectList objlist(commandBuffer); |
| objlist.add(cmd_pool); |
| skip |= LogError(objlist, "VUID-vkResetCommandBuffer-commandBuffer-00046", |
| "vkResetCommandBuffer(): Attempt to reset %s created from %s that does NOT have the " |
| "VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit set.", |
| report_data->FormatHandle(commandBuffer).c_str(), report_data->FormatHandle(cmd_pool).c_str()); |
| } |
| skip |= CheckCommandBufferInFlight(cb_state.get(), "reset", "VUID-vkResetCommandBuffer-commandBuffer-00045"); |
| |
| return skip; |
| } |
| |
| static const char *GetPipelineTypeName(VkPipelineBindPoint pipelineBindPoint) { |
| switch (pipelineBindPoint) { |
| case VK_PIPELINE_BIND_POINT_GRAPHICS: |
| return "graphics"; |
| case VK_PIPELINE_BIND_POINT_COMPUTE: |
| return "compute"; |
| case VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR: |
| return "ray-tracing"; |
| case VK_PIPELINE_BIND_POINT_SUBPASS_SHADING_HUAWEI: |
| return "subpass-shading"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineBindPoint(const CMD_BUFFER_STATE *cb_state, const PIPELINE_STATE *pipeline_state) const { |
| bool skip = false; |
| |
| if (cb_state->inheritedViewportDepths.size() != 0) { |
| bool dyn_viewport = IsDynamic(pipeline_state, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT) |
| || IsDynamic(pipeline_state, VK_DYNAMIC_STATE_VIEWPORT); |
| bool dyn_scissor = IsDynamic(pipeline_state, VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT) |
| || IsDynamic(pipeline_state, VK_DYNAMIC_STATE_SCISSOR); |
| if (!dyn_viewport || !dyn_scissor) { |
| skip |= LogError(device, "VUID-vkCmdBindPipeline-commandBuffer-04808", |
| "Graphics pipeline incompatible with viewport/scissor inheritance."); |
| } |
| const auto *discard_rectangle_state = LvlFindInChain<VkPipelineDiscardRectangleStateCreateInfoEXT>(pipeline_state->PNext()); |
| if (discard_rectangle_state && discard_rectangle_state->discardRectangleCount != 0) { |
| if (!IsDynamic(pipeline_state, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT)) { |
| skip |= LogError(device, "VUID-vkCmdBindPipeline-commandBuffer-04809", |
| "vkCmdBindPipeline(): commandBuffer is a secondary command buffer with " |
| "VkCommandBufferInheritanceViewportScissorInfoNV::viewportScissor2D enabled, pipelineBindPoint is " |
| "VK_PIPELINE_BIND_POINT_GRAPHICS and pipeline was created with " |
| "VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleCount = %" PRIu32 |
| ", but without VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT.", |
| discard_rectangle_state->discardRectangleCount); |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBindPipeline(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, |
| VkPipeline pipeline) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_BINDPIPELINE); |
| static const std::map<VkPipelineBindPoint, std::string> bindpoint_errors = { |
| std::make_pair(VK_PIPELINE_BIND_POINT_GRAPHICS, "VUID-vkCmdBindPipeline-pipelineBindPoint-00777"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_COMPUTE, "VUID-vkCmdBindPipeline-pipelineBindPoint-00778"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, "VUID-vkCmdBindPipeline-pipelineBindPoint-02391")}; |
| |
| skip |= ValidatePipelineBindPoint(cb_state.get(), pipelineBindPoint, "vkCmdBindPipeline()", bindpoint_errors); |
| |
| auto pipeline_state = Get<PIPELINE_STATE>(pipeline); |
| assert(pipeline_state); |
| |
| const auto pipeline_state_bind_point = pipeline_state->GetPipelineType(); |
| |
| if (pipelineBindPoint != pipeline_state_bind_point) { |
| if (pipelineBindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBindPipeline-pipelineBindPoint-00779", |
| "Cannot bind a pipeline of type %s to the graphics pipeline bind point", |
| GetPipelineTypeName(pipeline_state_bind_point)); |
| } else if (pipelineBindPoint == VK_PIPELINE_BIND_POINT_COMPUTE) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBindPipeline-pipelineBindPoint-00780", |
| "Cannot bind a pipeline of type %s to the compute pipeline bind point", |
| GetPipelineTypeName(pipeline_state_bind_point)); |
| } else if (pipelineBindPoint == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBindPipeline-pipelineBindPoint-02392", |
| "Cannot bind a pipeline of type %s to the ray-tracing pipeline bind point", |
| GetPipelineTypeName(pipeline_state_bind_point)); |
| } |
| } else { |
| if (pipelineBindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) { |
| skip |= ValidateGraphicsPipelineBindPoint(cb_state.get(), pipeline_state.get()); |
| |
| if (cb_state->activeRenderPass && |
| phys_dev_ext_props.provoking_vertex_props.provokingVertexModePerPipeline == VK_FALSE) { |
| const auto lvl_bind_point = ConvertToLvlBindPoint(pipelineBindPoint); |
| const auto &last_bound_it = cb_state->lastBound[lvl_bind_point]; |
| if (last_bound_it.pipeline_state) { |
| auto last_bound_provoking_vertex_state_ci = |
| LvlFindInChain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>( |
| last_bound_it.pipeline_state->RasterizationState()->pNext); |
| |
| auto current_provoking_vertex_state_ci = |
| LvlFindInChain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>( |
| pipeline_state->RasterizationState()->pNext); |
| |
| if (last_bound_provoking_vertex_state_ci && !current_provoking_vertex_state_ci) { |
| skip |= LogError(pipeline, "VUID-vkCmdBindPipeline-pipelineBindPoint-04881", |
| "Previous %s's provokingVertexMode is %s, but %s doesn't chain " |
| "VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.", |
| report_data->FormatHandle(last_bound_it.pipeline_state->pipeline()).c_str(), |
| string_VkProvokingVertexModeEXT(last_bound_provoking_vertex_state_ci->provokingVertexMode), |
| report_data->FormatHandle(pipeline).c_str()); |
| } else if (!last_bound_provoking_vertex_state_ci && current_provoking_vertex_state_ci) { |
| skip |= LogError(pipeline, "VUID-vkCmdBindPipeline-pipelineBindPoint-04881", |
| " %s's provokingVertexMode is %s, but previous %s doesn't chain " |
| "VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.", |
| report_data->FormatHandle(pipeline).c_str(), |
| string_VkProvokingVertexModeEXT(current_provoking_vertex_state_ci->provokingVertexMode), |
| report_data->FormatHandle(last_bound_it.pipeline_state->pipeline()).c_str()); |
| } else if (last_bound_provoking_vertex_state_ci && current_provoking_vertex_state_ci && |
| last_bound_provoking_vertex_state_ci->provokingVertexMode != |
| current_provoking_vertex_state_ci->provokingVertexMode) { |
| skip |= |
| LogError(pipeline, "VUID-vkCmdBindPipeline-pipelineBindPoint-04881", |
| "%s's provokingVertexMode is %s, but previous %s's provokingVertexMode is %s.", |
| report_data->FormatHandle(pipeline).c_str(), |
| string_VkProvokingVertexModeEXT(current_provoking_vertex_state_ci->provokingVertexMode), |
| report_data->FormatHandle(last_bound_it.pipeline_state->pipeline()).c_str(), |
| string_VkProvokingVertexModeEXT(last_bound_provoking_vertex_state_ci->provokingVertexMode)); |
| } |
| } |
| } |
| |
| if (cb_state->activeRenderPass && phys_dev_ext_props.sample_locations_props.variableSampleLocations == VK_FALSE) { |
| const auto *sample_locations = LvlFindInChain<VkPipelineSampleLocationsStateCreateInfoEXT>(pipeline_state->PNext()); |
| if (sample_locations && sample_locations->sampleLocationsEnable == VK_TRUE && |
| !IsDynamic(pipeline_state.get(), VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT)) { |
| const VkRenderPassSampleLocationsBeginInfoEXT *sample_locations_begin_info = |
| LvlFindInChain<VkRenderPassSampleLocationsBeginInfoEXT>(cb_state->activeRenderPassBeginInfo.pNext); |
| bool found = false; |
| if (sample_locations_begin_info) { |
| for (uint32_t i = 0; i < sample_locations_begin_info->postSubpassSampleLocationsCount; ++i) { |
| if (sample_locations_begin_info->pPostSubpassSampleLocations[i].subpassIndex == |
| cb_state->activeSubpass) { |
| if (MatchSampleLocationsInfo( |
| &sample_locations_begin_info->pPostSubpassSampleLocations[i].sampleLocationsInfo, |
| &sample_locations->sampleLocationsInfo)) { |
| found = true; |
| } |
| } |
| } |
| } |
| if (!found) { |
| skip |= |
| LogError(pipeline, "VUID-vkCmdBindPipeline-variableSampleLocations-01525", |
| "vkCmdBindPipeline(): VkPhysicalDeviceSampleLocationsPropertiesEXT::variableSampleLocations " |
| "is false, pipeline is a graphics pipeline with " |
| "VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsEnable equal to true and without " |
| "VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT, but the current render pass (%" PRIu32 |
| ") was not begun with any element of " |
| "VkRenderPassSampleLocationsBeginInfoEXT::pPostSubpassSampleLocations subpassIndex " |
| "matching the current subpass index and sampleLocationsInfo matching sampleLocationsInfo of " |
| "VkPipelineSampleLocationsStateCreateInfoEXT the pipeline was created with.", |
| cb_state->activeSubpass); |
| } |
| } |
| } |
| } |
| if (pipeline_state->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) { |
| skip |= LogError( |
| pipeline, "VUID-vkCmdBindPipeline-pipeline-03382", |
| "vkCmdBindPipeline(): Cannot bind a pipeline that was created with the VK_PIPELINE_CREATE_LIBRARY_BIT_KHR flag."); |
| } |
| if (cb_state->transform_feedback_active) { |
| skip |= LogError(pipeline, "VUID-vkCmdBindPipeline-None-02323", "vkCmdBindPipeline(): transform feedback is active."); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ForbidInheritedViewportScissor(VkCommandBuffer commandBuffer, const CMD_BUFFER_STATE *cb_state, const char *vuid, |
| const CMD_TYPE cmd_type) const { |
| bool skip = false; |
| if (cb_state->inheritedViewportDepths.size() != 0) { |
| skip |= |
| LogError(commandBuffer, vuid, |
| "%s: commandBuffer must not have VkCommandBufferInheritanceViewportScissorInfoNV::viewportScissor2D enabled.", |
| CommandTypeString(cmd_type)); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetViewport(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, |
| const VkViewport *pViewports) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETVIEWPORT, VK_TRUE, nullptr, nullptr); |
| skip |= ForbidInheritedViewportScissor(commandBuffer, cb_state.get(), "VUID-vkCmdSetViewport-commandBuffer-04821", CMD_SETVIEWPORT); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetScissor(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, |
| const VkRect2D *pScissors) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETSCISSOR, VK_TRUE, nullptr, nullptr); |
| skip |= ForbidInheritedViewportScissor(commandBuffer, cb_state.get(), "VUID-vkCmdSetScissor-viewportScissor2D-04789", |
| CMD_SETSCISSOR); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetExclusiveScissorNV(VkCommandBuffer commandBuffer, uint32_t firstExclusiveScissor, |
| uint32_t exclusiveScissorCount, const VkRect2D *pExclusiveScissors) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETEXCLUSIVESCISSORNV, |
| enabled_features.exclusive_scissor_features.exclusiveScissor, |
| "VUID-vkCmdSetExclusiveScissorNV-None-02031", "exclusiveScissor"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBindShadingRateImageNV(VkCommandBuffer commandBuffer, VkImageView imageView, |
| VkImageLayout imageLayout) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| |
| skip |= ValidateCmd(cb_state.get(), CMD_BINDSHADINGRATEIMAGENV); |
| |
| if (!enabled_features.shading_rate_image_features.shadingRateImage) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindShadingRateImageNV-None-02058", |
| "vkCmdBindShadingRateImageNV: The shadingRateImage feature is disabled."); |
| } |
| |
| if (imageView == VK_NULL_HANDLE) { |
| return skip; |
| } |
| auto view_state = Get<IMAGE_VIEW_STATE>(imageView); |
| if (!view_state) { |
| skip |= LogError(imageView, "VUID-vkCmdBindShadingRateImageNV-imageView-02059", |
| "vkCmdBindShadingRateImageNV: 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) { |
| skip |= LogError(imageView, "VUID-vkCmdBindShadingRateImageNV-imageView-02059", |
| "vkCmdBindShadingRateImageNV: 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) { |
| skip |= LogError( |
| imageView, "VUID-vkCmdBindShadingRateImageNV-imageView-02060", |
| "vkCmdBindShadingRateImageNV: If imageView is not VK_NULL_HANDLE, it must have a format of VK_FORMAT_R8_UINT."); |
| } |
| |
| 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)) { |
| skip |= LogError(imageView, "VUID-vkCmdBindShadingRateImageNV-imageView-02061", |
| "vkCmdBindShadingRateImageNV: If imageView is not VK_NULL_HANDLE, the image must have been " |
| "created with VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV set."); |
| } |
| |
| bool hit_error = false; |
| |
| // 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 |= VerifyImageLayout(cb_state.get(), image_state, subresource, imageLayout, VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV, |
| "vkCmdCopyImage()", "VUID-vkCmdBindShadingRateImageNV-imageLayout-02063", |
| "VUID-vkCmdBindShadingRateImageNV-imageView-02062", &hit_error); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetViewportShadingRatePaletteNV(VkCommandBuffer commandBuffer, uint32_t firstViewport, |
| uint32_t viewportCount, |
| const VkShadingRatePaletteNV *pShadingRatePalettes) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETVIEWPORTSHADINGRATEPALETTENV, |
| enabled_features.shading_rate_image_features.shadingRateImage, |
| "VUID-vkCmdSetViewportShadingRatePaletteNV-None-02064", "shadingRateImage"); |
| |
| for (uint32_t i = 0; i < viewportCount; ++i) { |
| auto *palette = &pShadingRatePalettes[i]; |
| if (palette->shadingRatePaletteEntryCount == 0 || |
| palette->shadingRatePaletteEntryCount > phys_dev_ext_props.shading_rate_image_props.shadingRatePaletteSize) { |
| skip |= LogError( |
| commandBuffer, "VUID-VkShadingRatePaletteNV-shadingRatePaletteEntryCount-02071", |
| "vkCmdSetViewportShadingRatePaletteNV: shadingRatePaletteEntryCount must be between 1 and shadingRatePaletteSize."); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGeometryTrianglesNV(const VkGeometryTrianglesNV &triangles, const char *func_name) const { |
| bool skip = false; |
| |
| auto vb_state = Get<BUFFER_STATE>(triangles.vertexData); |
| if (vb_state != nullptr && vb_state->createInfo.size <= triangles.vertexOffset) { |
| skip |= LogError(device, "VUID-VkGeometryTrianglesNV-vertexOffset-02428", "%s", func_name); |
| } |
| |
| auto ib_state = Get<BUFFER_STATE>(triangles.indexData); |
| if (ib_state != nullptr && ib_state->createInfo.size <= triangles.indexOffset) { |
| skip |= LogError(device, "VUID-VkGeometryTrianglesNV-indexOffset-02431", "%s", func_name); |
| } |
| |
| auto td_state = Get<BUFFER_STATE>(triangles.transformData); |
| if (td_state != nullptr && td_state->createInfo.size <= triangles.transformOffset) { |
| skip |= LogError(device, "VUID-VkGeometryTrianglesNV-transformOffset-02437", "%s", func_name); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGeometryAABBNV(const VkGeometryAABBNV &aabbs, const char *func_name) const { |
| bool skip = false; |
| |
| auto aabb_state = Get<BUFFER_STATE>(aabbs.aabbData); |
| if (aabb_state != nullptr && aabb_state->createInfo.size > 0 && aabb_state->createInfo.size <= aabbs.offset) { |
| skip |= LogError(device, "VUID-VkGeometryAABBNV-offset-02439", "%s", func_name); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGeometryNV(const VkGeometryNV &geometry, const char *func_name) const { |
| bool skip = false; |
| if (geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_NV) { |
| skip = ValidateGeometryTrianglesNV(geometry.geometry.triangles, func_name); |
| } else if (geometry.geometryType == VK_GEOMETRY_TYPE_AABBS_NV) { |
| skip = ValidateGeometryAABBNV(geometry.geometry.aabbs, func_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateAccelerationStructureNV(VkDevice device, |
| const VkAccelerationStructureCreateInfoNV *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkAccelerationStructureNV *pAccelerationStructure) const { |
| bool skip = false; |
| if (pCreateInfo != nullptr && pCreateInfo->info.type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV) { |
| for (uint32_t i = 0; i < pCreateInfo->info.geometryCount; i++) { |
| skip |= ValidateGeometryNV(pCreateInfo->info.pGeometries[i], "vkCreateAccelerationStructureNV():"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateAccelerationStructureKHR(VkDevice device, |
| const VkAccelerationStructureCreateInfoKHR *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkAccelerationStructureKHR *pAccelerationStructure) const { |
| bool skip = false; |
| if (pCreateInfo) { |
| auto buffer_state = Get<BUFFER_STATE>(pCreateInfo->buffer); |
| if (buffer_state) { |
| if (!(buffer_state->createInfo.usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR)) { |
| skip |= |
| LogError(device, "VUID-VkAccelerationStructureCreateInfoKHR-buffer-03614", |
| "VkAccelerationStructureCreateInfoKHR(): buffer must have been created with a usage value containing " |
| "VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR."); |
| } |
| if (buffer_state->createInfo.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT) { |
| skip |= LogError(device, "VUID-VkAccelerationStructureCreateInfoKHR-buffer-03615", |
| "VkAccelerationStructureCreateInfoKHR(): buffer must not have been created with " |
| "VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT."); |
| } |
| if (pCreateInfo->offset + pCreateInfo->size > buffer_state->createInfo.size) { |
| skip |= LogError( |
| device, "VUID-VkAccelerationStructureCreateInfoKHR-offset-03616", |
| "VkAccelerationStructureCreateInfoKHR(): The sum of offset and size must be less than the size of buffer."); |
| } |
| } |
| } |
| return skip; |
| } |
| bool CoreChecks::ValidateBindAccelerationStructureMemory(VkDevice device, |
| const VkBindAccelerationStructureMemoryInfoNV &info) const { |
| bool skip = false; |
| |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE>(info.accelerationStructure); |
| if (!as_state) { |
| return skip; |
| } |
| if (!as_state->GetBoundMemory().empty()) { |
| skip |= |
| LogError(info.accelerationStructure, "VUID-VkBindAccelerationStructureMemoryInfoNV-accelerationStructure-03620", |
| "vkBindAccelerationStructureMemoryNV(): accelerationStructure must not already be backed by a memory object."); |
| } |
| |
| // Validate bound memory range information |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(info.memory); |
| if (mem_info) { |
| skip |= ValidateInsertAccelerationStructureMemoryRange(info.accelerationStructure, mem_info.get(), info.memoryOffset, |
| "vkBindAccelerationStructureMemoryNV()"); |
| skip |= ValidateMemoryTypes(mem_info.get(), as_state->memory_requirements.memoryTypeBits, |
| "vkBindAccelerationStructureMemoryNV()", |
| "VUID-VkBindAccelerationStructureMemoryInfoNV-memory-03622"); |
| } |
| |
| // Validate memory requirements alignment |
| if (SafeModulo(info.memoryOffset, as_state->memory_requirements.alignment) != 0) { |
| skip |= LogError(info.accelerationStructure, "VUID-VkBindAccelerationStructureMemoryInfoNV-memoryOffset-03623", |
| "vkBindAccelerationStructureMemoryNV(): memoryOffset 0x%" PRIxLEAST64 |
| " must be an integer multiple of the alignment 0x%" PRIxLEAST64 |
| " member of the VkMemoryRequirements structure returned from " |
| "a call to vkGetAccelerationStructureMemoryRequirementsNV with accelerationStructure and type of " |
| "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV", |
| info.memoryOffset, as_state->memory_requirements.alignment); |
| } |
| |
| if (mem_info) { |
| // Validate memory requirements size |
| if (as_state->memory_requirements.size > (mem_info->alloc_info.allocationSize - info.memoryOffset)) { |
| skip |= LogError(info.accelerationStructure, "VUID-VkBindAccelerationStructureMemoryInfoNV-size-03624", |
| "vkBindAccelerationStructureMemoryNV(): The size 0x%" PRIxLEAST64 |
| " member of the VkMemoryRequirements structure returned from a call to " |
| "vkGetAccelerationStructureMemoryRequirementsNV with accelerationStructure and type of " |
| "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV must be less than or equal to the size " |
| "of memory minus memoryOffset 0x%" PRIxLEAST64 ".", |
| as_state->memory_requirements.size, mem_info->alloc_info.allocationSize - info.memoryOffset); |
| } |
| } |
| |
| return skip; |
| } |
| bool CoreChecks::PreCallValidateBindAccelerationStructureMemoryNV(VkDevice device, uint32_t bindInfoCount, |
| const VkBindAccelerationStructureMemoryInfoNV *pBindInfos) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| skip |= ValidateBindAccelerationStructureMemory(device, pBindInfos[i]); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetAccelerationStructureHandleNV(VkDevice device, VkAccelerationStructureNV accelerationStructure, |
| size_t dataSize, void *pData) const { |
| bool skip = false; |
| |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE>(accelerationStructure); |
| if (as_state != nullptr) { |
| // TODO: update the fake VUID below once the real one is generated. |
| skip = ValidateMemoryIsBoundToAccelerationStructure( |
| as_state.get(), "vkGetAccelerationStructureHandleNV", |
| "UNASSIGNED-vkGetAccelerationStructureHandleNV-accelerationStructure-XXXX"); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBuildAccelerationStructuresKHR( |
| VkCommandBuffer commandBuffer, uint32_t infoCount, const VkAccelerationStructureBuildGeometryInfoKHR *pInfos, |
| const VkAccelerationStructureBuildRangeInfoKHR *const *ppBuildRangeInfos) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| skip |= ValidateCmd(cb_state.get(), CMD_BUILDACCELERATIONSTRUCTURESKHR); |
| if (pInfos != nullptr) { |
| for (uint32_t info_index = 0; info_index < infoCount; ++info_index) { |
| auto src_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfos[info_index].srcAccelerationStructure); |
| auto dst_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfos[info_index].dstAccelerationStructure); |
| skip |= ValidateMemoryIsBoundToBuffer(dst_as_state->buffer_state.get(), "vkCmdBuildAccelerationStructuresKHR", |
| "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03707"); |
| if (pInfos[info_index].mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) { |
| skip |= ValidateMemoryIsBoundToBuffer(src_as_state->buffer_state.get(), "vkCmdBuildAccelerationStructuresKHR", |
| "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03708"); |
| if (src_as_state == nullptr || !src_as_state->built || |
| !(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03667", |
| "vkCmdBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is " |
| "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its srcAccelerationStructure member must " |
| "have been built before with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR set in " |
| "VkAccelerationStructureBuildGeometryInfoKHR::flags."); |
| } |
| if (pInfos[info_index].geometryCount != src_as_state->build_info_khr.geometryCount) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03758", |
| "vkCmdBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is " |
| "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR," |
| " its geometryCount member must have the same value which was specified when " |
| "srcAccelerationStructure was last built."); |
| } |
| if (pInfos[info_index].flags != src_as_state->build_info_khr.flags) { |
| skip |= |
| LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03759", |
| "vkCmdBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is" |
| " VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its flags member must have the same value which" |
| " was specified when srcAccelerationStructure was last built."); |
| } |
| if (pInfos[info_index].type != src_as_state->build_info_khr.type) { |
| skip |= |
| LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03760", |
| "vkCmdBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is" |
| " VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its type member must have the same value which" |
| " was specified when srcAccelerationStructure was last built."); |
| } |
| } |
| if (pInfos[info_index].type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) { |
| if (!dst_as_state || |
| (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR && |
| dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) { |
| skip |= |
| LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03700", |
| "vkCmdBuildAccelerationStructuresKHR(): For each element of pInfos, if its type member is " |
| "VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, its dstAccelerationStructure member must have " |
| "been created with a value of VkAccelerationStructureCreateInfoKHR::type equal to either " |
| "VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR or VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR."); |
| } |
| } |
| if (pInfos[info_index].type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) { |
| if (!dst_as_state || |
| (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR && |
| dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) { |
| skip |= |
| LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03699", |
| "vkCmdBuildAccelerationStructuresKHR(): For each element of pInfos, if its type member is " |
| "VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, its dstAccelerationStructure member must have been " |
| "created with a value of VkAccelerationStructureCreateInfoKHR::type equal to either " |
| "VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR or VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR."); |
| } |
| } |
| |
| skip |= ValidateAccelerationBuffers(info_index, pInfos[info_index], "vkCmdBuildAccelerationStructuresKHR"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateAccelerationBuffers(uint32_t info_index, const VkAccelerationStructureBuildGeometryInfoKHR &info, |
| const char *func_name) const { |
| bool skip = false; |
| const auto geometry_count = info.geometryCount; |
| const auto *p_geometries = info.pGeometries; |
| const auto *const *const pp_geometries = info.ppGeometries; |
| |
| auto buffer_check = [this, info_index, func_name](uint32_t gi, const VkDeviceOrHostAddressConstKHR address, |
| const char *field) -> bool { |
| // being inside the lambda confuses the compiler about our constness |
| const auto buffer_state = const_cast<const CoreChecks *>(this)->GetBufferByAddress(address.deviceAddress); |
| if (buffer_state && |
| !(buffer_state->createInfo.usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR)) { |
| LogObjectList objlist(device); |
| objlist.add(buffer_state->Handle()); |
| return LogError(objlist, "VUID-vkCmdBuildAccelerationStructuresKHR-geometry-03673", |
| "%s(): The buffer associated with pInfos[%" PRIu32 "].pGeometries[%" PRIu32 |
| "].%s was not created with VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR.", |
| func_name, info_index, gi, field); |
| } |
| return false; |
| }; |
| |
| // Parameter validation has already checked VUID-VkAccelerationStructureBuildGeometryInfoKHR-pGeometries-03788 |
| // !(pGeometries && ppGeometries) |
| std::function<const VkAccelerationStructureGeometryKHR &(uint32_t)> geom_accessor; |
| if (p_geometries) { |
| geom_accessor = [p_geometries](uint32_t i) -> const VkAccelerationStructureGeometryKHR & { return p_geometries[i]; }; |
| } else if (pp_geometries) { |
| geom_accessor = [pp_geometries](uint32_t i) -> const VkAccelerationStructureGeometryKHR & { |
| // pp_geometries[i] is assumed to be a valid pointer |
| return *pp_geometries[i]; |
| }; |
| } |
| |
| if (geom_accessor) { |
| for (uint32_t geom_index = 0; geom_index < geometry_count; ++geom_index) { |
| const auto &geom_data = geom_accessor(geom_index); |
| switch (geom_data.geometryType) { |
| case VK_GEOMETRY_TYPE_TRIANGLES_KHR: // == VK_GEOMETRY_TYPE_TRIANGLES_NV |
| skip |= buffer_check(geom_index, geom_data.geometry.triangles.vertexData, "geometry.triangles.vertexData"); |
| skip |= buffer_check(geom_index, geom_data.geometry.triangles.indexData, "geometry.triangles.indexData"); |
| skip |= |
| buffer_check(geom_index, geom_data.geometry.triangles.transformData, "geometry.triangles.transformData"); |
| break; |
| case VK_GEOMETRY_TYPE_INSTANCES_KHR: |
| skip |= buffer_check(geom_index, geom_data.geometry.instances.data, "geometry.instances.data"); |
| break; |
| case VK_GEOMETRY_TYPE_AABBS_KHR: // == VK_GEOMETRY_TYPE_AABBS_NV |
| skip |= buffer_check(geom_index, geom_data.geometry.aabbs.data, "geometry.aabbs.data"); |
| break; |
| default: |
| // no-op |
| break; |
| } |
| } |
| } |
| |
| const auto buffer_state = GetBufferByAddress(info.scratchData.deviceAddress); |
| if (!buffer_state) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03802", |
| "vkBuildAccelerationStructuresKHR(): The buffer associated with pInfos[%" PRIu32 |
| "].scratchData.deviceAddress %" PRIx64 " is not a valid device address.", |
| info_index, info.scratchData.deviceAddress); |
| } else { |
| if (!(buffer_state->createInfo.usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03674", |
| "vkBuildAccelerationStructuresKHR(): The buffer associated with pInfos[%" PRIu32 |
| "].scratchData.deviceAddress was not created with VK_BUFFER_USAGE_STORAGE_BUFFER_BIT bit.", |
| info_index); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateBuildAccelerationStructuresKHR( |
| VkDevice device, VkDeferredOperationKHR deferredOperation, uint32_t infoCount, |
| const VkAccelerationStructureBuildGeometryInfoKHR *pInfos, |
| const VkAccelerationStructureBuildRangeInfoKHR *const *ppBuildRangeInfos) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < infoCount; ++i) { |
| auto src_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfos[i].srcAccelerationStructure); |
| auto dst_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfos[i].dstAccelerationStructure); |
| if (dst_as_state) { |
| skip |= ValidateHostVisibleMemoryIsBoundToBuffer(dst_as_state->buffer_state.get(), "vkBuildAccelerationStructuresKHR", |
| "VUID-vkBuildAccelerationStructuresKHR-pInfos-03722"); |
| } |
| if (pInfos[i].mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) { |
| if (src_as_state) { |
| skip |= |
| ValidateHostVisibleMemoryIsBoundToBuffer(src_as_state->buffer_state.get(), "vkBuildAccelerationStructuresKHR", |
| "VUID-vkBuildAccelerationStructuresKHR-pInfos-03723"); |
| } |
| if (src_as_state == nullptr || !src_as_state->built || |
| !(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) { |
| skip |= LogError(device, "VUID-vkBuildAccelerationStructuresKHR-pInfos-03667", |
| "vkBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is " |
| "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its srcAccelerationStructure member must have " |
| "been built before with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR set in " |
| "VkAccelerationStructureBuildGeometryInfoKHR::flags."); |
| } |
| if (pInfos[i].geometryCount != src_as_state->build_info_khr.geometryCount) { |
| skip |= LogError(device, "VUID-vkBuildAccelerationStructuresKHR-pInfos-03758", |
| "vkBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is " |
| "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR," |
| " its geometryCount member must have the same value which was specified when " |
| "srcAccelerationStructure was last built."); |
| } |
| if (pInfos[i].flags != src_as_state->build_info_khr.flags) { |
| skip |= LogError(device, "VUID-vkBuildAccelerationStructuresKHR-pInfos-03759", |
| "vkBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is" |
| " VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its flags member must have the same value which" |
| " was specified when srcAccelerationStructure was last built."); |
| } |
| if (pInfos[i].type != src_as_state->build_info_khr.type) { |
| skip |= LogError(device, "VUID-vkBuildAccelerationStructuresKHR-pInfos-03760", |
| "vkBuildAccelerationStructuresKHR(): For each element of pInfos, if its mode member is" |
| " VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its type member must have the same value which" |
| " was specified when srcAccelerationStructure was last built."); |
| } |
| } |
| if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) { |
| if (!dst_as_state || |
| (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR && |
| dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) { |
| skip |= LogError(device, "VUID-vkBuildAccelerationStructuresKHR-pInfos-03700", |
| "vkBuildAccelerationStructuresKHR(): For each element of pInfos, if its type member is " |
| "VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, its dstAccelerationStructure member must have " |
| "been created with a value of VkAccelerationStructureCreateInfoKHR::type equal to either " |
| "VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR or VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR."); |
| } |
| } |
| if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) { |
| if (!dst_as_state || |
| (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR && |
| dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) { |
| skip |= LogError(device, "VUID-vkBuildAccelerationStructuresKHR-pInfos-03699", |
| "vkBuildAccelerationStructuresKHR(): For each element of pInfos, if its type member is " |
| "VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, its dstAccelerationStructure member must have been " |
| "created with a value of VkAccelerationStructureCreateInfoKHR::type equal to either " |
| "VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR or VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR."); |
| } |
| } |
| } |
| return skip; |
| } |
| bool CoreChecks::PreCallValidateCmdBuildAccelerationStructureNV(VkCommandBuffer commandBuffer, |
| const VkAccelerationStructureInfoNV *pInfo, VkBuffer instanceData, |
| VkDeviceSize instanceOffset, VkBool32 update, |
| VkAccelerationStructureNV dst, VkAccelerationStructureNV src, |
| VkBuffer scratch, VkDeviceSize scratchOffset) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| |
| skip |= ValidateCmd(cb_state.get(), CMD_BUILDACCELERATIONSTRUCTURENV); |
| |
| if (pInfo != nullptr && pInfo->type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV) { |
| for (uint32_t i = 0; i < pInfo->geometryCount; i++) { |
| skip |= ValidateGeometryNV(pInfo->pGeometries[i], "vkCmdBuildAccelerationStructureNV():"); |
| } |
| } |
| |
| if (pInfo != nullptr && pInfo->geometryCount > phys_dev_ext_props.ray_tracing_propsNV.maxGeometryCount) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-geometryCount-02241", |
| "vkCmdBuildAccelerationStructureNV(): geometryCount [%d] must be less than or equal to " |
| "VkPhysicalDeviceRayTracingPropertiesNV::maxGeometryCount.", |
| pInfo->geometryCount); |
| } |
| |
| auto dst_as_state = Get<ACCELERATION_STRUCTURE_STATE>(dst); |
| auto src_as_state = Get<ACCELERATION_STRUCTURE_STATE>(src); |
| auto scratch_buffer_state = Get<BUFFER_STATE>(scratch); |
| |
| if (dst_as_state != nullptr && pInfo != nullptr) { |
| if (dst_as_state->create_infoNV.info.type != pInfo->type) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info VkAccelerationStructureInfoNV::type" |
| "[%s] must be identical to build info VkAccelerationStructureInfoNV::type [%s].", |
| string_VkAccelerationStructureTypeNV(dst_as_state->create_infoNV.info.type), |
| string_VkAccelerationStructureTypeNV(pInfo->type)); |
| } |
| if (dst_as_state->create_infoNV.info.flags != pInfo->flags) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info VkAccelerationStructureInfoNV::flags" |
| "[0x%X] must be identical to build info VkAccelerationStructureInfoNV::flags [0x%X].", |
| dst_as_state->create_infoNV.info.flags, pInfo->flags); |
| } |
| if (dst_as_state->create_infoNV.info.instanceCount < pInfo->instanceCount) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info VkAccelerationStructureInfoNV::instanceCount " |
| "[%d] must be greater than or equal to build info VkAccelerationStructureInfoNV::instanceCount [%d].", |
| dst_as_state->create_infoNV.info.instanceCount, pInfo->instanceCount); |
| } |
| if (dst_as_state->create_infoNV.info.geometryCount < pInfo->geometryCount) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info VkAccelerationStructureInfoNV::geometryCount" |
| "[%d] must be greater than or equal to build info VkAccelerationStructureInfoNV::geometryCount [%d].", |
| dst_as_state->create_infoNV.info.geometryCount, pInfo->geometryCount); |
| } else { |
| for (uint32_t i = 0; i < pInfo->geometryCount; i++) { |
| const VkGeometryDataNV &create_geometry_data = dst_as_state->create_infoNV.info.pGeometries[i].geometry; |
| const VkGeometryDataNV &build_geometry_data = pInfo->pGeometries[i].geometry; |
| if (create_geometry_data.triangles.vertexCount < build_geometry_data.triangles.vertexCount) { |
| skip |= LogError( |
| commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info pGeometries[%d].geometry.triangles.vertexCount [%d]" |
| "must be greater than or equal to build info pGeometries[%d].geometry.triangles.vertexCount [%d].", |
| i, create_geometry_data.triangles.vertexCount, i, build_geometry_data.triangles.vertexCount); |
| break; |
| } |
| if (create_geometry_data.triangles.indexCount < build_geometry_data.triangles.indexCount) { |
| skip |= LogError( |
| commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info pGeometries[%d].geometry.triangles.indexCount [%d]" |
| "must be greater than or equal to build info pGeometries[%d].geometry.triangles.indexCount [%d].", |
| i, create_geometry_data.triangles.indexCount, i, build_geometry_data.triangles.indexCount); |
| break; |
| } |
| if (create_geometry_data.aabbs.numAABBs < build_geometry_data.aabbs.numAABBs) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-dst-02488", |
| "vkCmdBuildAccelerationStructureNV(): create info pGeometries[%d].geometry.aabbs.numAABBs [%d]" |
| "must be greater than or equal to build info pGeometries[%d].geometry.aabbs.numAABBs [%d].", |
| i, create_geometry_data.aabbs.numAABBs, i, build_geometry_data.aabbs.numAABBs); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (dst_as_state != nullptr) { |
| skip |= ValidateMemoryIsBoundToAccelerationStructure( |
| dst_as_state.get(), "vkCmdBuildAccelerationStructureNV()", |
| "UNASSIGNED-CoreValidation-DrawState-InvalidCommandBuffer-VkAccelerationStructureNV"); |
| } |
| |
| if (update == VK_TRUE) { |
| if (src == VK_NULL_HANDLE) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-update-02489", |
| "vkCmdBuildAccelerationStructureNV(): If update is VK_TRUE, src must not be VK_NULL_HANDLE."); |
| } else { |
| if (src_as_state == nullptr || !src_as_state->built || |
| !(src_as_state->build_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-update-02490", |
| "vkCmdBuildAccelerationStructureNV(): If update is VK_TRUE, src must have been built before " |
| "with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV set in " |
| "VkAccelerationStructureInfoNV::flags."); |
| } |
| } |
| if (dst_as_state != nullptr && !dst_as_state->update_scratch_memory_requirements_checked) { |
| skip |= |
| LogWarning(dst, kVUID_Core_CmdBuildAccelNV_NoUpdateMemReqQuery, |
| "vkCmdBuildAccelerationStructureNV(): Updating %s but vkGetAccelerationStructureMemoryRequirementsNV() " |
| "has not been called for update scratch memory.", |
| report_data->FormatHandle(dst_as_state->acceleration_structure()).c_str()); |
| // Use requirements fetched at create time |
| } |
| if (scratch_buffer_state != nullptr && dst_as_state != nullptr && |
| dst_as_state->update_scratch_memory_requirements.size > (scratch_buffer_state->createInfo.size - scratchOffset)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-update-02492", |
| "vkCmdBuildAccelerationStructureNV(): If update is VK_TRUE, The size member of the " |
| "VkMemoryRequirements structure returned from a call to " |
| "vkGetAccelerationStructureMemoryRequirementsNV with " |
| "VkAccelerationStructureMemoryRequirementsInfoNV::accelerationStructure set to dst and " |
| "VkAccelerationStructureMemoryRequirementsInfoNV::type set to " |
| "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV must be less than " |
| "or equal to the size of scratch minus scratchOffset"); |
| } |
| } else { |
| if (dst_as_state != nullptr && !dst_as_state->build_scratch_memory_requirements_checked) { |
| skip |= LogWarning(dst, kVUID_Core_CmdBuildAccelNV_NoScratchMemReqQuery, |
| "vkCmdBuildAccelerationStructureNV(): Assigning scratch buffer to %s but " |
| "vkGetAccelerationStructureMemoryRequirementsNV() has not been called for scratch memory.", |
| report_data->FormatHandle(dst_as_state->acceleration_structure()).c_str()); |
| // Use requirements fetched at create time |
| } |
| if (scratch_buffer_state != nullptr && dst_as_state != nullptr && |
| dst_as_state->build_scratch_memory_requirements.size > (scratch_buffer_state->createInfo.size - scratchOffset)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBuildAccelerationStructureNV-update-02491", |
| "vkCmdBuildAccelerationStructureNV(): If update is VK_FALSE, The size member of the " |
| "VkMemoryRequirements structure returned from a call to " |
| "vkGetAccelerationStructureMemoryRequirementsNV with " |
| "VkAccelerationStructureMemoryRequirementsInfoNV::accelerationStructure set to dst and " |
| "VkAccelerationStructureMemoryRequirementsInfoNV::type set to " |
| "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV must be less than " |
| "or equal to the size of scratch minus scratchOffset"); |
| } |
| } |
| if (instanceData != VK_NULL_HANDLE) { |
| auto buffer_state = Get<BUFFER_STATE>(instanceData); |
| if (buffer_state) { |
| skip |= ValidateBufferUsageFlags(buffer_state.get(), VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, true, |
| "VUID-VkAccelerationStructureInfoNV-instanceData-02782", |
| "vkCmdBuildAccelerationStructureNV()", "VK_BUFFER_USAGE_RAY_TRACING_BIT_NV"); |
| } |
| } |
| if (scratch_buffer_state) { |
| skip |= ValidateBufferUsageFlags(scratch_buffer_state.get(), VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, true, |
| "VUID-VkAccelerationStructureInfoNV-scratch-02781", "vkCmdBuildAccelerationStructureNV()", |
| "VK_BUFFER_USAGE_RAY_TRACING_BIT_NV"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyAccelerationStructureNV(VkCommandBuffer commandBuffer, VkAccelerationStructureNV dst, |
| VkAccelerationStructureNV src, |
| VkCopyAccelerationStructureModeNV mode) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| |
| skip |= ValidateCmd(cb_state.get(), CMD_COPYACCELERATIONSTRUCTURENV); |
| auto dst_as_state = Get<ACCELERATION_STRUCTURE_STATE>(dst); |
| auto src_as_state = Get<ACCELERATION_STRUCTURE_STATE>(src); |
| |
| if (dst_as_state != nullptr) { |
| skip |= ValidateMemoryIsBoundToAccelerationStructure( |
| dst_as_state.get(), "vkCmdBuildAccelerationStructureNV()", |
| "UNASSIGNED-CoreValidation-DrawState-InvalidCommandBuffer-VkAccelerationStructureNV"); |
| } |
| |
| if (mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV) { |
| if (src_as_state != nullptr && |
| (!src_as_state->built || !(src_as_state->build_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV))) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdCopyAccelerationStructureNV-src-03411", |
| "vkCmdCopyAccelerationStructureNV(): src must have been built with " |
| "VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV if mode is " |
| "VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV."); |
| } |
| } |
| if (!(mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV || mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdCopyAccelerationStructureNV-mode-03410", |
| "vkCmdCopyAccelerationStructureNV():mode must be VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR" |
| "or VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyAccelerationStructureNV(VkDevice device, VkAccelerationStructureNV accelerationStructure, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE>(accelerationStructure); |
| bool skip = false; |
| if (as_state) { |
| skip |= ValidateObjectNotInUse(as_state.get(), "vkDestroyAccelerationStructureNV", |
| "VUID-vkDestroyAccelerationStructureNV-accelerationStructure-03752"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroyAccelerationStructureKHR(VkDevice device, VkAccelerationStructureKHR accelerationStructure, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(accelerationStructure); |
| bool skip = false; |
| if (as_state) { |
| skip |= ValidateObjectNotInUse(as_state.get(), "vkDestroyAccelerationStructureKHR", |
| "VUID-vkDestroyAccelerationStructureKHR-accelerationStructure-02442"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetViewportWScalingNV(VkCommandBuffer commandBuffer, uint32_t firstViewport, |
| uint32_t viewportCount, |
| const VkViewportWScalingNV *pViewportWScalings) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETVIEWPORTWSCALINGNV, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETLINEWIDTH, VK_TRUE, nullptr, nullptr); |
| ; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetLineStippleEXT(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, |
| uint16_t lineStipplePattern) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETLINESTIPPLEEXT, VK_TRUE, nullptr, nullptr); |
| ; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthBias(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, |
| float depthBiasSlopeFactor) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHBIAS, VK_TRUE, nullptr, nullptr); |
| if ((depthBiasClamp != 0.0) && (!enabled_features.core.depthBiasClamp)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdSetDepthBias-depthBiasClamp-00790", |
| "vkCmdSetDepthBias(): the depthBiasClamp device feature is disabled: the depthBiasClamp parameter must " |
| "be set to 0.0."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetBlendConstants(VkCommandBuffer commandBuffer, const float blendConstants[4]) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETBLENDCONSTANTS, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthBounds(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHBOUNDS, VK_TRUE, nullptr, nullptr); |
| |
| // The extension was not created with a feature bit whichs prevents displaying the 2 variations of the VUIDs |
| if (!IsExtEnabled(device_extensions.vk_ext_depth_range_unrestricted)) { |
| if (!(minDepthBounds >= 0.0) || !(minDepthBounds <= 1.0)) { |
| // Also VUID-vkCmdSetDepthBounds-minDepthBounds-00600 |
| skip |= LogError(commandBuffer, "VUID-vkCmdSetDepthBounds-minDepthBounds-02508", |
| "vkCmdSetDepthBounds(): VK_EXT_depth_range_unrestricted extension is not enabled and minDepthBounds " |
| "(=%f) is not within the [0.0, 1.0] range.", |
| minDepthBounds); |
| } |
| |
| if (!(maxDepthBounds >= 0.0) || !(maxDepthBounds <= 1.0)) { |
| // Also VUID-vkCmdSetDepthBounds-maxDepthBounds-00601 |
| skip |= LogError(commandBuffer, "VUID-vkCmdSetDepthBounds-maxDepthBounds-02509", |
| "vkCmdSetDepthBounds(): VK_EXT_depth_range_unrestricted extension is not enabled and maxDepthBounds " |
| "(=%f) is not within the [0.0, 1.0] range.", |
| maxDepthBounds); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilCompareMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, |
| uint32_t compareMask) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILCOMPAREMASK, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilWriteMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, |
| uint32_t writeMask) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILWRITEMASK, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilReference(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, |
| uint32_t reference) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILREFERENCE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBindDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, |
| VkPipelineLayout layout, uint32_t firstSet, uint32_t setCount, |
| const VkDescriptorSet *pDescriptorSets, uint32_t dynamicOffsetCount, |
| const uint32_t *pDynamicOffsets) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_BINDDESCRIPTORSETS); |
| // Track total count of dynamic descriptor types to make sure we have an offset for each one |
| uint32_t total_dynamic_descriptors = 0; |
| string error_string = ""; |
| |
| auto pipeline_layout = Get<PIPELINE_LAYOUT_STATE>(layout); |
| for (uint32_t set_idx = 0; set_idx < setCount; set_idx++) { |
| auto descriptor_set = Get<cvdescriptorset::DescriptorSet>(pDescriptorSets[set_idx]); |
| if (descriptor_set) { |
| // Verify that set being bound is compatible with overlapping setLayout of pipelineLayout |
| if (!VerifySetLayoutCompatibility(report_data, descriptor_set.get(), pipeline_layout.get(), set_idx + firstSet, |
| error_string)) { |
| skip |= LogError(pDescriptorSets[set_idx], "VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358", |
| "vkCmdBindDescriptorSets(): descriptorSet #%u being bound is not compatible with overlapping " |
| "descriptorSetLayout at index %u of " |
| "%s due to: %s.", |
| set_idx, set_idx + firstSet, report_data->FormatHandle(layout).c_str(), error_string.c_str()); |
| } |
| |
| auto set_dynamic_descriptor_count = descriptor_set->GetDynamicDescriptorCount(); |
| if (set_dynamic_descriptor_count) { |
| // First make sure we won't overstep bounds of pDynamicOffsets array |
| if ((total_dynamic_descriptors + set_dynamic_descriptor_count) > dynamicOffsetCount) { |
| // Test/report this here, such that we don't run past the end of pDynamicOffsets in the else clause |
| skip |= |
| LogError(pDescriptorSets[set_idx], "VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359", |
| "vkCmdBindDescriptorSets(): descriptorSet #%u (%s) requires %u dynamicOffsets, but only %u " |
| "dynamicOffsets are left in " |
| "pDynamicOffsets array. There must be one dynamic offset for each dynamic descriptor being bound.", |
| set_idx, report_data->FormatHandle(pDescriptorSets[set_idx]).c_str(), |
| descriptor_set->GetDynamicDescriptorCount(), (dynamicOffsetCount - total_dynamic_descriptors)); |
| // Set the number found to the maximum to prevent duplicate messages, or subsquent descriptor sets from |
| // testing against the "short tail" we're skipping below. |
| total_dynamic_descriptors = dynamicOffsetCount; |
| } else { // Validate dynamic offsets and Dynamic Offset Minimums |
| // offset for all sets (pDynamicOffsets) |
| uint32_t cur_dyn_offset = total_dynamic_descriptors; |
| // offset into this descriptor set |
| uint32_t set_dyn_offset = 0; |
| const auto &dsl = descriptor_set->GetLayout(); |
| const auto binding_count = dsl->GetBindingCount(); |
| const auto &limits = phys_dev_props.limits; |
| for (uint32_t i = 0; i < binding_count; i++) { |
| const auto *binding = dsl->GetDescriptorSetLayoutBindingPtrFromIndex(i); |
| // skip checking binding if not needed |
| if (cvdescriptorset::IsDynamicDescriptor(binding->descriptorType) == false) { |
| continue; |
| } |
| |
| // If a descriptor set has only binding 0 and 2 the binding_index will be 0 and 2 |
| const uint32_t binding_index = binding->binding; |
| const uint32_t descriptorCount = binding->descriptorCount; |
| |
| // Need to loop through each descriptor count inside the binding |
| // if descriptorCount is zero the binding with a dynamic descriptor type does not count |
| for (uint32_t j = 0; j < descriptorCount; j++) { |
| const uint32_t offset = pDynamicOffsets[cur_dyn_offset]; |
| if (offset == 0) { |
| // offset of zero is equivalent of not having the dynamic offset |
| cur_dyn_offset++; |
| set_dyn_offset++; |
| continue; |
| } |
| |
| // Validate alignment with limit |
| if ((binding->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) && |
| (SafeModulo(offset, limits.minUniformBufferOffsetAlignment) != 0)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971", |
| "vkCmdBindDescriptorSets(): pDynamicOffsets[%u] is %u, but must be a multiple of " |
| "device limit minUniformBufferOffsetAlignment 0x%" PRIxLEAST64 ".", |
| cur_dyn_offset, offset, limits.minUniformBufferOffsetAlignment); |
| } |
| if ((binding->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) && |
| (SafeModulo(offset, limits.minStorageBufferOffsetAlignment) != 0)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972", |
| "vkCmdBindDescriptorSets(): pDynamicOffsets[%u] is %u, but must be a multiple of " |
| "device limit minStorageBufferOffsetAlignment 0x%" PRIxLEAST64 ".", |
| cur_dyn_offset, offset, limits.minStorageBufferOffsetAlignment); |
| } |
| |
| auto *descriptor = descriptor_set->GetDescriptorFromDynamicOffsetIndex(set_dyn_offset); |
| assert(descriptor != nullptr); |
| // Currently only GeneralBuffer are dynamic and need to be checked |
| if (descriptor->GetClass() == cvdescriptorset::DescriptorClass::GeneralBuffer) { |
| const auto *buffer_descriptor = static_cast<const cvdescriptorset::BufferDescriptor *>(descriptor); |
| const VkDeviceSize bound_range = buffer_descriptor->GetRange(); |
| const VkDeviceSize bound_offset = buffer_descriptor->GetOffset(); |
| //NOTE: null / invalid buffers may show up here, errors are raised elsewhere for this. |
| auto buffer_state = buffer_descriptor->GetBufferState(); |
| |
| // Validate offset didn't go over buffer |
| if ((bound_range == VK_WHOLE_SIZE) && (offset > 0)) { |
| LogObjectList objlist(commandBuffer); |
| objlist.add(pDescriptorSets[set_idx]); |
| objlist.add(buffer_descriptor->GetBuffer()); |
| skip |= |
| LogError(objlist, "VUID-vkCmdBindDescriptorSets-pDescriptorSets-06715", |
| "vkCmdBindDescriptorSets(): pDynamicOffsets[%u] is 0x%x, but must be zero since " |
| "the buffer descriptor's range is VK_WHOLE_SIZE in descriptorSet #%u binding #%u " |
| "descriptor[%u].", |
| cur_dyn_offset, offset, set_idx, binding_index, j); |
| |
| } else if (buffer_state && (bound_range != VK_WHOLE_SIZE) && |
| ((offset + bound_range + bound_offset) > buffer_state->createInfo.size)) { |
| LogObjectList objlist(commandBuffer); |
| objlist.add(pDescriptorSets[set_idx]); |
| objlist.add(buffer_descriptor->GetBuffer()); |
| skip |= |
| LogError(objlist, "VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979", |
| "vkCmdBindDescriptorSets(): pDynamicOffsets[%u] is 0x%x which when added to the " |
| "buffer descriptor's range (0x%" PRIxLEAST64 |
| ") is greater than the size of the buffer (0x%" PRIxLEAST64 |
| ") in descriptorSet #%u binding #%u descriptor[%u].", |
| cur_dyn_offset, offset, bound_range, buffer_state->createInfo.size, set_idx, |
| binding_index, j); |
| } |
| } |
| cur_dyn_offset++; |
| set_dyn_offset++; |
| } // descriptorCount loop |
| } // bindingCount loop |
| // Keep running total of dynamic descriptor count to verify at the end |
| total_dynamic_descriptors += set_dynamic_descriptor_count; |
| } |
| } |
| if (descriptor_set->GetPoolState()->createInfo.flags & VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_VALVE) { |
| skip |= LogError(pDescriptorSets[set_idx], "VUID-vkCmdBindDescriptorSets-pDescriptorSets-04616", |
| "vkCmdBindDescriptorSets(): pDescriptorSets[%" PRIu32 "] was allocated from a pool that was created with VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_VALVE.", set_idx); |
| } |
| } else { |
| if (!IsExtEnabled(device_extensions.vk_ext_graphics_pipeline_library)) { |
| skip |= LogError(pDescriptorSets[set_idx], "VUID-vkCmdBindDescriptorSets-pDescriptorSets-06563", |
| "vkCmdBindDescriptorSets(): Attempt to bind pDescriptorSets[%" PRIu32 |
| "] (%s) that does not exist, and %s is not enabled.", |
| set_idx, report_data->FormatHandle(pDescriptorSets[set_idx]).c_str(), |
| VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| } else { |
| const auto layout_flags = pipeline_layout->CreateFlags(); |
| if ((layout_flags & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT) == 0) { |
| skip |= LogError(pDescriptorSets[set_idx], "VUID-vkCmdBindDescriptorSets-layout-06564", |
| "vkCmdBindDescriptorSets(): Attempt to bind pDescriptorSets[%" PRIu32 |
| "] (%s) that does not exist, and the layout was not created " |
| "VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT.", |
| set_idx, report_data->FormatHandle(pDescriptorSets[set_idx]).c_str()); |
| } |
| } |
| } |
| } |
| // dynamicOffsetCount must equal the total number of dynamic descriptors in the sets being bound |
| if (total_dynamic_descriptors != dynamicOffsetCount) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359", |
| "vkCmdBindDescriptorSets(): Attempting to bind %u descriptorSets with %u dynamic descriptors, but " |
| "dynamicOffsetCount is %u. It should " |
| "exactly match the number of dynamic descriptors.", |
| setCount, total_dynamic_descriptors, dynamicOffsetCount); |
| } |
| // firstSet and descriptorSetCount sum must be less than setLayoutCount |
| if ((firstSet + setCount) > static_cast<uint32_t>(pipeline_layout->set_layouts.size())) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBindDescriptorSets-firstSet-00360", |
| "vkCmdBindDescriptorSets(): Sum of firstSet (%u) and descriptorSetCount (%u) is greater than " |
| "VkPipelineLayoutCreateInfo::setLayoutCount " |
| "(%zu) when pipeline layout was created", |
| firstSet, setCount, pipeline_layout->set_layouts.size()); |
| } |
| |
| static const std::map<VkPipelineBindPoint, std::string> bindpoint_errors = { |
| std::make_pair(VK_PIPELINE_BIND_POINT_GRAPHICS, "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_COMPUTE, "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361")}; |
| skip |= ValidatePipelineBindPoint(cb_state.get(), pipelineBindPoint, "vkCmdBindPipeline()", bindpoint_errors); |
| |
| return skip; |
| } |
| |
| // Validates that the supplied bind point is supported for the command buffer (vis. the command pool) |
| // Takes array of error codes as some of the VUID's (e.g. vkCmdBindPipeline) are written per bindpoint |
| // TODO add vkCmdBindPipeline bind_point validation using this call. |
| bool CoreChecks::ValidatePipelineBindPoint(const CMD_BUFFER_STATE *cb_state, VkPipelineBindPoint bind_point, const char *func_name, |
| const std::map<VkPipelineBindPoint, std::string> &bind_errors) const { |
| bool skip = false; |
| auto pool = cb_state->command_pool; |
| if (pool) { // The loss of a pool in a recording cmd is reported in DestroyCommandPool |
| static const std::map<VkPipelineBindPoint, VkQueueFlags> flag_mask = { |
| std::make_pair(VK_PIPELINE_BIND_POINT_GRAPHICS, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)), |
| std::make_pair(VK_PIPELINE_BIND_POINT_COMPUTE, static_cast<VkQueueFlags>(VK_QUEUE_COMPUTE_BIT)), |
| std::make_pair(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, |
| static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)), |
| }; |
| const auto &qfp = physical_device_state->queue_family_properties[pool->queueFamilyIndex]; |
| if (0 == (qfp.queueFlags & flag_mask.at(bind_point))) { |
| const std::string &error = bind_errors.at(bind_point); |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(cb_state->createInfo.commandPool); |
| skip |= LogError(objlist, error, "%s: %s was allocated from %s that does not support bindpoint %s.", func_name, |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(cb_state->createInfo.commandPool).c_str(), |
| string_VkPipelineBindPoint(bind_point)); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdPushDescriptorSetKHR(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, |
| VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, |
| const VkWriteDescriptorSet *pDescriptorWrites) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| const char *func_name = "vkCmdPushDescriptorSetKHR()"; |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_PUSHDESCRIPTORSETKHR); |
| |
| static const std::map<VkPipelineBindPoint, std::string> bind_errors = { |
| std::make_pair(VK_PIPELINE_BIND_POINT_GRAPHICS, "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_COMPUTE, "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363")}; |
| |
| skip |= ValidatePipelineBindPoint(cb_state.get(), pipelineBindPoint, func_name, bind_errors); |
| auto layout_data = Get<PIPELINE_LAYOUT_STATE>(layout); |
| |
| // Validate the set index points to a push descriptor set and is in range |
| if (layout_data) { |
| const auto &set_layouts = layout_data->set_layouts; |
| if (set < set_layouts.size()) { |
| const auto &dsl = set_layouts[set]; |
| if (dsl) { |
| if (!dsl->IsPushDescriptor()) { |
| skip = LogError(layout, "VUID-vkCmdPushDescriptorSetKHR-set-00365", |
| "%s: Set index %" PRIu32 " does not match push descriptor set layout index for %s.", func_name, |
| set, report_data->FormatHandle(layout).c_str()); |
| } else { |
| // Create an empty proxy in order to use the existing descriptor set update validation |
| // TODO move the validation (like this) that doesn't need descriptor set state to the DSL object so we |
| // don't have to do this. |
| cvdescriptorset::DescriptorSet proxy_ds(VK_NULL_HANDLE, nullptr, dsl, 0, this); |
| skip |= ValidatePushDescriptorsUpdate(&proxy_ds, descriptorWriteCount, pDescriptorWrites, func_name); |
| } |
| } |
| } else { |
| skip = LogError(layout, "VUID-vkCmdPushDescriptorSetKHR-set-00364", |
| "%s: Set index %" PRIu32 " is outside of range for %s (set < %" PRIu32 ").", func_name, set, |
| report_data->FormatHandle(layout).c_str(), static_cast<uint32_t>(set_layouts.size())); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, |
| VkIndexType indexType) const { |
| auto buffer_state = Get<BUFFER_STATE>(buffer); |
| auto cb_node = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(buffer_state); |
| assert(cb_node); |
| |
| bool skip = ValidateBufferUsageFlags(buffer_state.get(), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, true, |
| "VUID-vkCmdBindIndexBuffer-buffer-00433", "vkCmdBindIndexBuffer()", |
| "VK_BUFFER_USAGE_INDEX_BUFFER_BIT"); |
| skip |= ValidateCmd(cb_node.get(), CMD_BINDINDEXBUFFER); |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), "vkCmdBindIndexBuffer()", "VUID-vkCmdBindIndexBuffer-buffer-00434"); |
| const auto offset_align = GetIndexAlignment(indexType); |
| if (offset % offset_align) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindIndexBuffer-offset-00432", |
| "vkCmdBindIndexBuffer() offset (0x%" PRIxLEAST64 ") does not fall on alignment (%s) boundary.", offset, |
| string_VkIndexType(indexType)); |
| } |
| if (offset >= buffer_state->requirements.size) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindIndexBuffer-offset-00431", |
| "vkCmdBindIndexBuffer() offset (0x%" PRIxLEAST64 ") is not less than the size (0x%" PRIxLEAST64 |
| ") of buffer (%s).", |
| offset, buffer_state->requirements.size, report_data->FormatHandle(buffer_state->buffer()).c_str()); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, |
| const VkBuffer *pBuffers, const VkDeviceSize *pOffsets) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_BINDVERTEXBUFFERS); |
| for (uint32_t i = 0; i < bindingCount; ++i) { |
| auto buffer_state = Get<BUFFER_STATE>(pBuffers[i]); |
| if (buffer_state) { |
| skip |= ValidateBufferUsageFlags(buffer_state.get(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, true, |
| "VUID-vkCmdBindVertexBuffers-pBuffers-00627", "vkCmdBindVertexBuffers()", |
| "VK_BUFFER_USAGE_VERTEX_BUFFER_BIT"); |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), "vkCmdBindVertexBuffers()", |
| "VUID-vkCmdBindVertexBuffers-pBuffers-00628"); |
| if (pOffsets[i] >= buffer_state->createInfo.size) { |
| skip |= |
| LogError(buffer_state->buffer(), "VUID-vkCmdBindVertexBuffers-pOffsets-00626", |
| "vkCmdBindVertexBuffers() offset (0x%" PRIxLEAST64 ") is beyond the end of the buffer.", pOffsets[i]); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| // Validate that an image's sampleCount matches the requirement for a specific API call |
| bool CoreChecks::ValidateImageSampleCount(const IMAGE_STATE *image_state, VkSampleCountFlagBits sample_count, const char *location, |
| const std::string &msgCode) const { |
| bool skip = false; |
| if (image_state->createInfo.samples != sample_count) { |
| skip = LogError(image_state->image(), msgCode, "%s for %s was created with a sample count of %s but must be %s.", location, |
| report_data->FormatHandle(image_state->image()).c_str(), |
| string_VkSampleCountFlagBits(image_state->createInfo.samples), string_VkSampleCountFlagBits(sample_count)); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, |
| VkDeviceSize dataSize, const void *pData) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| auto dst_buffer_state = Get<BUFFER_STATE>(dstBuffer); |
| assert(dst_buffer_state); |
| |
| bool skip = false; |
| skip |= ValidateMemoryIsBoundToBuffer(dst_buffer_state.get(), "vkCmdUpdateBuffer()", "VUID-vkCmdUpdateBuffer-dstBuffer-00035"); |
| // Validate that DST buffer has correct usage flags set |
| skip |= ValidateBufferUsageFlags(dst_buffer_state.get(), VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, |
| "VUID-vkCmdUpdateBuffer-dstBuffer-00034", "vkCmdUpdateBuffer()", |
| "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); |
| skip |= ValidateCmd(cb_state.get(), CMD_UPDATEBUFFER); |
| skip |= ValidateProtectedBuffer(cb_state.get(), dst_buffer_state.get(), "vkCmdUpdateBuffer()", |
| "VUID-vkCmdUpdateBuffer-commandBuffer-01813"); |
| skip |= ValidateUnprotectedBuffer(cb_state.get(), dst_buffer_state.get(), "vkCmdUpdateBuffer()", |
| "VUID-vkCmdUpdateBuffer-commandBuffer-01814"); |
| if (dstOffset >= dst_buffer_state->createInfo.size) { |
| skip |= LogError( |
| commandBuffer, "VUID-vkCmdUpdateBuffer-dstOffset-00032", |
| "vkCmdUpdateBuffer() dstOffset (0x%" PRIxLEAST64 ") is not less than the size (0x%" PRIxLEAST64 ") of buffer (%s).", |
| dstOffset, dst_buffer_state->createInfo.size, report_data->FormatHandle(dst_buffer_state->buffer()).c_str()); |
| } else if (dataSize > dst_buffer_state->createInfo.size - dstOffset) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdUpdateBuffer-dataSize-00033", |
| "vkCmdUpdateBuffer() dataSize (0x%" PRIxLEAST64 ") is not less than the size (0x%" PRIxLEAST64 |
| ") of buffer (%s) minus dstOffset (0x%" PRIxLEAST64 ").", |
| dataSize, dst_buffer_state->createInfo.size, report_data->FormatHandle(dst_buffer_state->buffer()).c_str(), |
| dstOffset); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETEVENT, VK_TRUE, nullptr, nullptr); |
| Location loc(Func::vkCmdSetEvent, Field::stageMask); |
| LogObjectList objects(commandBuffer); |
| skip |= ValidatePipelineStage(objects, loc, cb_state->GetQueueFlags(), stageMask); |
| skip |= ValidateStageMaskHost(loc, stageMask); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo *pDependencyInfo, |
| CMD_TYPE cmd_type) const { |
| LogObjectList objects(commandBuffer); |
| objects.add(event); |
| |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= ValidateExtendedDynamicState(*cb_state, cmd_type, enabled_features.core13.synchronization2, |
| "VUID-vkCmdSetEvent2-synchronization2-03824", "synchronization2"); |
| Location loc(Func::vkCmdSetEvent2, Field::pDependencyInfo); |
| if (pDependencyInfo->dependencyFlags != 0) { |
| skip |= LogError(objects, "VUID-vkCmdSetEvent2-dependencyFlags-03825", "%s (%s) must be 0", |
| loc.dot(Field::dependencyFlags).Message().c_str(), |
| string_VkDependencyFlags(pDependencyInfo->dependencyFlags).c_str()); |
| } |
| skip |= ValidateDependencyInfo(objects, loc, cb_state.get(), pDependencyInfo); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event, |
| const VkDependencyInfoKHR *pDependencyInfo) const { |
| return ValidateCmdSetEvent2(commandBuffer, event, pDependencyInfo, CMD_SETEVENT2KHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, |
| const VkDependencyInfo *pDependencyInfo) const { |
| return ValidateCmdSetEvent2(commandBuffer, event, pDependencyInfo, CMD_SETEVENT2); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| LogObjectList objects(commandBuffer); |
| Location loc(Func::vkCmdResetEvent, Field::stageMask); |
| |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_RESETEVENT); |
| skip |= ValidatePipelineStage(objects, loc, cb_state->GetQueueFlags(), stageMask); |
| skip |= ValidateStageMaskHost(loc, stageMask); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask, |
| CMD_TYPE cmd_type) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| LogObjectList objects(commandBuffer); |
| Location loc(Func::vkCmdResetEvent2, Field::stageMask); |
| |
| bool skip = false; |
| if (!enabled_features.core13.synchronization2) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdResetEvent2-synchronization2-03829", |
| "vkCmdResetEvent2KHR(): Synchronization2 feature is not enabled"); |
| } |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| skip |= ValidatePipelineStage(objects, loc, cb_state->GetQueueFlags(), stageMask); |
| skip |= ValidateStageMaskHost(loc, stageMask); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResetEvent2KHR(VkCommandBuffer commandBuffer, VkEvent event, |
| VkPipelineStageFlags2KHR stageMask) const { |
| return ValidateCmdResetEvent2(commandBuffer, event, stageMask, CMD_RESETEVENT2KHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, |
| VkPipelineStageFlags2 stageMask) const { |
| return ValidateCmdResetEvent2(commandBuffer, event, stageMask, CMD_RESETEVENT2); |
| } |
| |
| static bool HasNonFramebufferStagePipelineStageFlags(VkPipelineStageFlags2KHR inflags) { |
| return (inflags & ~(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | |
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)) != 0; |
| } |
| |
| // transient helper struct for checking parts of VUID 02285 |
| struct RenderPassDepState { |
| using Location = core_error::Location; |
| using Func = core_error::Func; |
| using Struct = core_error::Struct; |
| using Field = core_error::Field; |
| |
| const CoreChecks *core; |
| const std::string func_name; |
| const std::string vuid; |
| uint32_t active_subpass; |
| const VkRenderPass rp_handle; |
| const VkPipelineStageFlags2KHR disabled_features; |
| const std::vector<uint32_t> &self_dependencies; |
| const safe_VkSubpassDependency2 *dependencies; |
| |
| RenderPassDepState(const CoreChecks *c, const std::string &f, const std::string &v, uint32_t subpass, const VkRenderPass handle, |
| const DeviceFeatures &features, const std::vector<uint32_t> &self_deps, |
| const safe_VkSubpassDependency2 *deps) |
| : core(c), |
| func_name(f), |
| vuid(v), |
| active_subpass(subpass), |
| rp_handle(handle), |
| disabled_features(sync_utils::DisabledPipelineStages(features)), |
| self_dependencies(self_deps), |
| dependencies(deps) {} |
| |
| VkMemoryBarrier2KHR GetSubPassDepBarrier(const safe_VkSubpassDependency2 &dep) { |
| VkMemoryBarrier2KHR result; |
| |
| const auto *barrier = LvlFindInChain<VkMemoryBarrier2KHR>(dep.pNext); |
| if (barrier) { |
| result = *barrier; |
| } else { |
| result.srcStageMask = dep.srcStageMask; |
| result.dstStageMask = dep.dstStageMask; |
| result.srcAccessMask = dep.srcAccessMask; |
| result.dstAccessMask = dep.dstAccessMask; |
| } |
| return result; |
| } |
| |
| bool ValidateStage(const Location &loc, VkPipelineStageFlags2KHR src_stage_mask, VkPipelineStageFlags2KHR dst_stage_mask) { |
| // Look for matching mask in any self-dependency |
| bool match = false; |
| for (const auto self_dep_index : self_dependencies) { |
| const auto sub_dep = GetSubPassDepBarrier(dependencies[self_dep_index]); |
| auto sub_src_stage_mask = |
| sync_utils::ExpandPipelineStages(sub_dep.srcStageMask, sync_utils::kAllQueueTypes, disabled_features); |
| auto sub_dst_stage_mask = |
| sync_utils::ExpandPipelineStages(sub_dep.dstStageMask, sync_utils::kAllQueueTypes, disabled_features); |
| match = ((sub_src_stage_mask == VK_PIPELINE_STAGE_ALL_COMMANDS_BIT) || |
| (src_stage_mask == (sub_src_stage_mask & src_stage_mask))) && |
| ((sub_dst_stage_mask == VK_PIPELINE_STAGE_ALL_COMMANDS_BIT) || |
| (dst_stage_mask == (sub_dst_stage_mask & dst_stage_mask))); |
| if (match) break; |
| } |
| if (!match) { |
| std::stringstream self_dep_ss; |
| stream_join(self_dep_ss, ", ", self_dependencies); |
| core->LogError(rp_handle, vuid, |
| "%s (0x%" PRIx64 |
| ") is not a subset of VkSubpassDependency srcAccessMask " |
| "for any self-dependency of subpass %d of %s for which dstAccessMask is also a subset. " |
| "Candidate VkSubpassDependency are pDependencies entries [%s].", |
| loc.dot(Field::srcStageMask).Message().c_str(), src_stage_mask, active_subpass, |
| core->report_data->FormatHandle(rp_handle).c_str(), self_dep_ss.str().c_str()); |
| core->LogError(rp_handle, vuid, |
| "%s (0x%" PRIx64 |
| ") is not a subset of VkSubpassDependency dstAccessMask " |
| "for any self-dependency of subpass %d of %s for which srcAccessMask is also a subset. " |
| "Candidate VkSubpassDependency are pDependencies entries [%s].", |
| loc.dot(Field::dstStageMask).Message().c_str(), dst_stage_mask, active_subpass, |
| core->report_data->FormatHandle(rp_handle).c_str(), self_dep_ss.str().c_str()); |
| } |
| return !match; |
| } |
| |
| bool ValidateAccess(const Location &loc, VkAccessFlags2KHR src_access_mask, VkAccessFlags2KHR dst_access_mask) { |
| bool match = false; |
| |
| for (const auto self_dep_index : self_dependencies) { |
| const auto sub_dep = GetSubPassDepBarrier(dependencies[self_dep_index]); |
| match = (src_access_mask == (sub_dep.srcAccessMask & src_access_mask)) && |
| (dst_access_mask == (sub_dep.dstAccessMask & dst_access_mask)); |
| if (match) break; |
| } |
| if (!match) { |
| std::stringstream self_dep_ss; |
| stream_join(self_dep_ss, ", ", self_dependencies); |
| core->LogError(rp_handle, vuid, |
| "%s (0x%" PRIx64 |
| ") is not a subset of VkSubpassDependency " |
| "srcAccessMask of subpass %d of %s. Candidate VkSubpassDependency are pDependencies entries [%s].", |
| loc.dot(Field::srcAccessMask).Message().c_str(), src_access_mask, active_subpass, |
| core->report_data->FormatHandle(rp_handle).c_str(), self_dep_ss.str().c_str()); |
| core->LogError(rp_handle, vuid, |
| "%s (0x%" PRIx64 |
| ") is not a subset of VkSubpassDependency " |
| "dstAccessMask of subpass %d of %s. Candidate VkSubpassDependency are pDependencies entries [%s].", |
| loc.dot(Field::dstAccessMask).Message().c_str(), dst_access_mask, active_subpass, |
| core->report_data->FormatHandle(rp_handle).c_str(), self_dep_ss.str().c_str()); |
| } |
| return !match; |
| } |
| |
| bool ValidateDependencyFlag(VkDependencyFlags dependency_flags) { |
| bool match = false; |
| |
| for (const auto self_dep_index : self_dependencies) { |
| const auto &sub_dep = dependencies[self_dep_index]; |
| match = sub_dep.dependencyFlags == dependency_flags; |
| if (match) break; |
| } |
| if (!match) { |
| std::stringstream self_dep_ss; |
| stream_join(self_dep_ss, ", ", self_dependencies); |
| core->LogError(rp_handle, vuid, |
| "%s: dependencyFlags param (0x%X) does not equal VkSubpassDependency dependencyFlags value for any " |
| "self-dependency of subpass %d of %s. Candidate VkSubpassDependency are pDependencies entries [%s].", |
| func_name.c_str(), dependency_flags, active_subpass, core->report_data->FormatHandle(rp_handle).c_str(), |
| self_dep_ss.str().c_str()); |
| } |
| return !match; |
| } |
| }; |
| |
| // Validate VUs for Pipeline Barriers that are within a renderPass |
| // Pre: cb_state->activeRenderPass must be a pointer to valid renderPass state |
| bool CoreChecks::ValidateRenderPassPipelineBarriers(const Location &outer_loc, const CMD_BUFFER_STATE *cb_state, |
| VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, |
| VkDependencyFlags dependency_flags, uint32_t mem_barrier_count, |
| const VkMemoryBarrier *mem_barriers, uint32_t buffer_mem_barrier_count, |
| const VkBufferMemoryBarrier *buffer_mem_barriers, |
| uint32_t image_mem_barrier_count, |
| const VkImageMemoryBarrier *image_barriers) const { |
| bool skip = false; |
| const auto& rp_state = cb_state->activeRenderPass; |
| RenderPassDepState state(this, outer_loc.StringFunc().c_str(), "VUID-vkCmdPipelineBarrier-pDependencies-02285", |
| cb_state->activeSubpass, rp_state->renderPass(), enabled_features, |
| rp_state->self_dependencies[cb_state->activeSubpass], rp_state->createInfo.pDependencies); |
| if (state.self_dependencies.size() == 0) { |
| skip |= LogError(state.rp_handle, "VUID-vkCmdPipelineBarrier-pDependencies-02285", |
| "%s Barriers cannot be set during subpass %d of %s with no self-dependency specified.", |
| outer_loc.Message().c_str(), state.active_subpass, report_data->FormatHandle(state.rp_handle).c_str()); |
| return skip; |
| } |
| // Grab ref to current subpassDescription up-front for use below |
| const auto &sub_desc = rp_state->createInfo.pSubpasses[state.active_subpass]; |
| skip |= state.ValidateStage(outer_loc, src_stage_mask, dst_stage_mask); |
| |
| if (0 != buffer_mem_barrier_count) { |
| skip |= LogError(state.rp_handle, "VUID-vkCmdPipelineBarrier-bufferMemoryBarrierCount-01178", |
| "%s: bufferMemoryBarrierCount is non-zero (%d) for subpass %d of %s.", state.func_name.c_str(), |
| buffer_mem_barrier_count, state.active_subpass, report_data->FormatHandle(rp_state->renderPass()).c_str()); |
| } |
| for (uint32_t i = 0; i < mem_barrier_count; ++i) { |
| const auto &mem_barrier = mem_barriers[i]; |
| Location loc(outer_loc.function, Struct::VkMemoryBarrier, Field::pMemoryBarriers, i); |
| skip |= state.ValidateAccess(loc, mem_barrier.srcAccessMask, mem_barrier.dstAccessMask); |
| } |
| |
| for (uint32_t i = 0; i < image_mem_barrier_count; ++i) { |
| const auto &img_barrier = image_barriers[i]; |
| Location loc(outer_loc.function, Struct::VkImageMemoryBarrier, Field::pImageMemoryBarriers, i); |
| skip |= state.ValidateAccess(loc, img_barrier.srcAccessMask, img_barrier.dstAccessMask); |
| |
| if (VK_QUEUE_FAMILY_IGNORED != img_barrier.srcQueueFamilyIndex || |
| VK_QUEUE_FAMILY_IGNORED != img_barrier.dstQueueFamilyIndex) { |
| skip |= LogError(state.rp_handle, "VUID-vkCmdPipelineBarrier-srcQueueFamilyIndex-01182", |
| "%s is %d and dstQueueFamilyIndex is %d but both must be VK_QUEUE_FAMILY_IGNORED.", |
| loc.dot(Field::srcQueueFamilyIndex).Message().c_str(), img_barrier.srcQueueFamilyIndex, |
| img_barrier.dstQueueFamilyIndex); |
| } |
| // Secondary CBs can have null framebuffer so record will queue up validation in that case 'til FB is known |
| if (VK_NULL_HANDLE != cb_state->activeFramebuffer) { |
| skip |= ValidateImageBarrierAttachment(loc, cb_state, cb_state->activeFramebuffer.get(), state.active_subpass, sub_desc, |
| state.rp_handle, img_barrier); |
| } |
| } |
| skip |= state.ValidateDependencyFlag(dependency_flags); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateRenderPassPipelineBarriers(const Location &outer_loc, const CMD_BUFFER_STATE *cb_state, |
| const VkDependencyInfoKHR *dep_info) const { |
| bool skip = false; |
| const auto &rp_state = cb_state->activeRenderPass; |
| if (rp_state->use_dynamic_rendering || rp_state->use_dynamic_rendering_inherited) { |
| return skip; |
| } |
| RenderPassDepState state(this, outer_loc.StringFunc().c_str(), "VUID-vkCmdPipelineBarrier2-pDependencies-02285", |
| cb_state->activeSubpass, rp_state->renderPass(), enabled_features, |
| rp_state->self_dependencies[cb_state->activeSubpass], rp_state->createInfo.pDependencies); |
| |
| if (state.self_dependencies.size() == 0) { |
| skip |= LogError(state.rp_handle, state.vuid, |
| "%s: Barriers cannot be set during subpass %d of %s with no self-dependency specified.", |
| state.func_name.c_str(), state.active_subpass, report_data->FormatHandle(rp_state->renderPass()).c_str()); |
| return skip; |
| } |
| // Grab ref to current subpassDescription up-front for use below |
| const auto &sub_desc = rp_state->createInfo.pSubpasses[state.active_subpass]; |
| for (uint32_t i = 0; i < dep_info->memoryBarrierCount; ++i) { |
| const auto &mem_barrier = dep_info->pMemoryBarriers[i]; |
| Location loc(outer_loc.function, Struct::VkMemoryBarrier2, Field::pMemoryBarriers, i); |
| skip |= state.ValidateStage(loc, mem_barrier.srcStageMask, mem_barrier.dstStageMask); |
| skip |= state.ValidateAccess(loc, mem_barrier.srcAccessMask, mem_barrier.dstAccessMask); |
| } |
| if (0 != dep_info->bufferMemoryBarrierCount) { |
| skip |= |
| LogError(state.rp_handle, "VUID-vkCmdPipelineBarrier2-bufferMemoryBarrierCount-01178", |
| "%s: bufferMemoryBarrierCount is non-zero (%d) for subpass %d of %s.", state.func_name.c_str(), |
| dep_info->bufferMemoryBarrierCount, state.active_subpass, report_data->FormatHandle(state.rp_handle).c_str()); |
| } |
| for (uint32_t i = 0; i < dep_info->imageMemoryBarrierCount; ++i) { |
| const auto &img_barrier = dep_info->pImageMemoryBarriers[i]; |
| Location loc(outer_loc.function, Struct::VkImageMemoryBarrier2, Field::pImageMemoryBarriers, i); |
| |
| skip |= state.ValidateStage(loc, img_barrier.srcStageMask, img_barrier.dstStageMask); |
| skip |= state.ValidateAccess(loc, img_barrier.srcAccessMask, img_barrier.dstAccessMask); |
| |
| if (VK_QUEUE_FAMILY_IGNORED != img_barrier.srcQueueFamilyIndex || |
| VK_QUEUE_FAMILY_IGNORED != img_barrier.dstQueueFamilyIndex) { |
| skip |= LogError(state.rp_handle, "VUID-vkCmdPipelineBarrier2-srcQueueFamilyIndex-01182", |
| "%s is %d and dstQueueFamilyIndex is %d but both must be VK_QUEUE_FAMILY_IGNORED.", |
| loc.dot(Field::srcQueueFamilyIndex).Message().c_str(), img_barrier.srcQueueFamilyIndex, |
| img_barrier.dstQueueFamilyIndex); |
| } |
| // Secondary CBs can have null framebuffer so record will queue up validation in that case 'til FB is known |
| if (VK_NULL_HANDLE != cb_state->activeFramebuffer) { |
| skip |= ValidateImageBarrierAttachment(loc, cb_state, cb_state->activeFramebuffer.get(), state.active_subpass, sub_desc, |
| state.rp_handle, img_barrier); |
| } |
| } |
| skip |= state.ValidateDependencyFlag(dep_info->dependencyFlags); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateStageMasksAgainstQueueCapabilities(const LogObjectList &objects, const Location &loc, |
| VkQueueFlags queue_flags, VkPipelineStageFlags2KHR stage_mask) const { |
| bool skip = false; |
| // these are always allowed. |
| stage_mask &= ~(VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR | VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR | |
| VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR | VK_PIPELINE_STAGE_2_HOST_BIT_KHR); |
| if (stage_mask == 0) { |
| return skip; |
| } |
| |
| static const std::map<VkPipelineStageFlags2KHR, VkQueueFlags> metaFlags{ |
| {VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT_KHR, VK_QUEUE_GRAPHICS_BIT}, |
| {VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT}, |
| {VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT_KHR, VK_QUEUE_GRAPHICS_BIT}, |
| {VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT_KHR, VK_QUEUE_GRAPHICS_BIT}, |
| }; |
| |
| for (const auto &entry : metaFlags) { |
| if (((entry.first & stage_mask) != 0) && ((entry.second & queue_flags) == 0)) { |
| const auto& vuid = sync_vuid_maps::GetStageQueueCapVUID(loc, entry.first); |
| skip |= LogError(objects, vuid, |
| "%s flag %s is not compatible with the queue family properties (%s) of this command buffer.", |
| loc.Message().c_str(), sync_utils::StringPipelineStageFlags(entry.first).c_str(), |
| string_VkQueueFlags(queue_flags).c_str()); |
| } |
| stage_mask &= ~entry.first; |
| } |
| if (stage_mask == 0) { |
| return skip; |
| } |
| |
| auto supported_flags = sync_utils::ExpandPipelineStages(VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, queue_flags); |
| |
| auto bad_flags = stage_mask & ~supported_flags; |
| |
| // Lookup each bit in the stagemask and check for overlap between its table bits and queue_flags |
| for (size_t i = 0; i < sizeof(bad_flags) * 8; i++) { |
| VkPipelineStageFlags2KHR bit = (1ULL << i) & bad_flags; |
| if (bit) { |
| const auto& vuid = sync_vuid_maps::GetStageQueueCapVUID(loc, bit); |
| skip |= LogError( |
| objects, vuid, "%s flag %s is not compatible with the queue family properties (%s) of this command buffer.", |
| loc.Message().c_str(), sync_utils::StringPipelineStageFlags(bit).c_str(), string_VkQueueFlags(queue_flags).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineStageFeatureEnables(const LogObjectList &objects, const Location &loc, |
| VkPipelineStageFlags2KHR stage_mask) const { |
| bool skip = false; |
| if (!enabled_features.core13.synchronization2 && stage_mask == 0) { |
| const auto& vuid = sync_vuid_maps::GetBadFeatureVUID(loc, 0); |
| std::stringstream msg; |
| msg << loc.Message() << " must not be 0 unless synchronization2 is enabled."; |
| skip |= LogError(objects, vuid, "%s", msg.str().c_str()); |
| } |
| |
| auto disabled_stages = sync_utils::DisabledPipelineStages(enabled_features); |
| auto bad_bits = stage_mask & disabled_stages; |
| if (bad_bits == 0) { |
| return skip; |
| } |
| for (size_t i = 0; i < sizeof(bad_bits) * 8; i++) { |
| VkPipelineStageFlags2KHR bit = 1ULL << i; |
| if (bit & bad_bits) { |
| const auto& vuid = sync_vuid_maps::GetBadFeatureVUID(loc, bit); |
| std::stringstream msg; |
| msg << loc.Message() << " includes " << sync_utils::StringPipelineStageFlags(bit) << " when the device does not have " |
| << sync_vuid_maps::kFeatureNameMap.at(bit) << " feature enabled."; |
| |
| skip |= LogError(objects, vuid, "%s", msg.str().c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineStage(const LogObjectList &objects, const Location &loc, VkQueueFlags queue_flags, |
| VkPipelineStageFlags2KHR stage_mask) const { |
| bool skip = false; |
| skip |= ValidateStageMasksAgainstQueueCapabilities(objects, loc, queue_flags, stage_mask); |
| skip |= ValidatePipelineStageFeatureEnables(objects, loc, stage_mask); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateAccessMask(const LogObjectList &objects, const Location &loc, VkQueueFlags queue_flags, |
| VkAccessFlags2KHR access_mask, VkPipelineStageFlags2KHR stage_mask) const { |
| bool skip = false; |
| // Early out if all commands set |
| if ((stage_mask & VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR) != 0) return skip; |
| |
| // or if only generic memory accesses are specified (or we got a 0 mask) |
| access_mask &= ~(VK_ACCESS_2_MEMORY_READ_BIT_KHR | VK_ACCESS_2_MEMORY_WRITE_BIT_KHR); |
| if (access_mask == 0) return skip; |
| |
| auto expanded_stages = sync_utils::ExpandPipelineStages(stage_mask, queue_flags); // TODO: |
| auto valid_accesses = sync_utils::CompatibleAccessMask(expanded_stages); |
| auto bad_accesses = (access_mask & ~valid_accesses); |
| if (bad_accesses == 0) { |
| return skip; |
| } |
| for (size_t i = 0; i < sizeof(bad_accesses) * 8; i++) { |
| VkAccessFlags2KHR bit = (1ULL << i); |
| if (bad_accesses & bit) { |
| const auto& vuid = sync_vuid_maps::GetBadAccessFlagsVUID(loc, bit); |
| std::stringstream msg; |
| msg << loc.Message() << " bit " << sync_utils::StringAccessFlags(bit) << " is not supported by stage mask (" |
| << sync_utils::StringPipelineStageFlags(stage_mask) << ")."; |
| skip |= LogError(objects, vuid, "%s", msg.str().c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateEventStageMask(const ValidationStateTracker *state_data, const CMD_BUFFER_STATE *pCB, size_t eventCount, |
| size_t firstEventIndex, VkPipelineStageFlags2KHR sourceStageMask, |
| EventToStageMap *localEventToStageMap) { |
| bool skip = false; |
| VkPipelineStageFlags2KHR stage_mask = 0; |
| const auto max_event = std::min((firstEventIndex + eventCount), pCB->events.size()); |
| for (size_t event_index = firstEventIndex; event_index < max_event; ++event_index) { |
| auto event = pCB->events[event_index]; |
| auto event_data = localEventToStageMap->find(event); |
| if (event_data != localEventToStageMap->end()) { |
| stage_mask |= event_data->second; |
| } else { |
| auto global_event_data = state_data->Get<EVENT_STATE>(event); |
| if (!global_event_data) { |
| skip |= state_data->LogError(event, kVUID_Core_DrawState_InvalidEvent, |
| "%s cannot be waited on if it has never been set.", |
| state_data->report_data->FormatHandle(event).c_str()); |
| } else { |
| stage_mask |= global_event_data->stageMask; |
| } |
| } |
| } |
| // TODO: Need to validate that host_bit is only set if set event is called |
| // but set event can be called at any time. |
| if (sourceStageMask != stage_mask && sourceStageMask != (stage_mask | VK_PIPELINE_STAGE_HOST_BIT)) { |
| skip |= state_data->LogError( |
| pCB->commandBuffer(), "VUID-vkCmdWaitEvents-srcStageMask-parameter", |
| "Submitting cmdbuffer with call to VkCmdWaitEvents using srcStageMask 0x%" PRIx64 |
| " which must be the bitwise OR of " |
| "the stageMask parameters used in calls to vkCmdSetEvent and VK_PIPELINE_STAGE_HOST_BIT if used with " |
| "vkSetEvent but instead is 0x%" PRIx64 ".", |
| sourceStageMask, stage_mask); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, |
| uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, |
| uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, |
| uint32_t imageMemoryBarrierCount, |
| const VkImageMemoryBarrier *pImageMemoryBarriers) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| auto queue_flags = cb_state->GetQueueFlags(); |
| LogObjectList objects(commandBuffer); |
| Location loc(Func::vkCmdWaitEvents); |
| |
| skip |= ValidatePipelineStage(objects, loc.dot(Field::srcStageMask), queue_flags, srcStageMask); |
| skip |= ValidatePipelineStage(objects, loc.dot(Field::dstStageMask), queue_flags, dstStageMask); |
| |
| skip |= ValidateCmd(cb_state.get(), CMD_WAITEVENTS); |
| skip |= ValidateBarriers(loc.dot(Field::pDependencyInfo), cb_state.get(), srcStageMask, dstStageMask, memoryBarrierCount, |
| pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, |
| pImageMemoryBarriers); |
| for (uint32_t i = 0; i < bufferMemoryBarrierCount; ++i) { |
| if (pBufferMemoryBarriers[i].srcQueueFamilyIndex != pBufferMemoryBarriers[i].dstQueueFamilyIndex) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdWaitEvents-srcQueueFamilyIndex-02803", |
| "vkCmdWaitEvents(): pBufferMemoryBarriers[%" PRIu32 "] has different srcQueueFamilyIndex (%" PRIu32 |
| ") and dstQueueFamilyIndex (%" PRIu32 ").", |
| i, pBufferMemoryBarriers[i].srcQueueFamilyIndex, pBufferMemoryBarriers[i].dstQueueFamilyIndex); |
| } |
| } |
| for (uint32_t i = 0; i < imageMemoryBarrierCount; ++i) { |
| if (pImageMemoryBarriers[i].srcQueueFamilyIndex != pImageMemoryBarriers[i].dstQueueFamilyIndex) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdWaitEvents-srcQueueFamilyIndex-02803", |
| "vkCmdWaitEvents(): pImageMemoryBarriers[%" PRIu32 "] has different srcQueueFamilyIndex (%" PRIu32 |
| ") and dstQueueFamilyIndex (%" PRIu32 ").", |
| i, pImageMemoryBarriers[i].srcQueueFamilyIndex, pImageMemoryBarriers[i].dstQueueFamilyIndex); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfo *pDependencyInfos, CMD_TYPE cmd_type) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| bool skip = false; |
| const char *func_name = CommandTypeString(cmd_type); |
| if (!enabled_features.core13.synchronization2) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdWaitEvents2-synchronization2-03836", |
| "%s(): Synchronization2 feature is not enabled", func_name); |
| } |
| for (uint32_t i = 0; (i < eventCount) && !skip; i++) { |
| LogObjectList objects(commandBuffer); |
| objects.add(pEvents[i]); |
| Location loc(Func::vkCmdWaitEvents2, Field::pDependencyInfos, i); |
| if (pDependencyInfos[i].dependencyFlags != 0) { |
| skip |= LogError(objects, "VUID-vkCmdWaitEvents2-dependencyFlags-03844", "%s (%s) must be 0.", |
| loc.dot(Field::dependencyFlags).Message().c_str(), |
| string_VkDependencyFlags(pDependencyInfos[i].dependencyFlags).c_str()); |
| } |
| skip |= ValidateDependencyInfo(objects, loc, cb_state.get(), &pDependencyInfos[i]); |
| } |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWaitEvents2KHR(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfoKHR *pDependencyInfos) const { |
| return ValidateCmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos, CMD_WAITEVENTS2KHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfo *pDependencyInfos) const { |
| return ValidateCmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos, CMD_WAITEVENTS2); |
| } |
| |
| void CORE_CMD_BUFFER_STATE::RecordWaitEvents(CMD_TYPE cmd_type, uint32_t eventCount, const VkEvent *pEvents, |
| VkPipelineStageFlags2KHR srcStageMask) { |
| // CMD_BUFFER_STATE will add to the events vector. |
| auto first_event_index = events.size(); |
| CMD_BUFFER_STATE::RecordWaitEvents(cmd_type, eventCount, pEvents, srcStageMask); |
| auto event_added_count = events.size() - first_event_index; |
| eventUpdates.emplace_back([event_added_count, first_event_index, srcStageMask](CMD_BUFFER_STATE &cb, bool do_validate, |
| EventToStageMap *localEventToStageMap) { |
| if (!do_validate) return false; |
| return CoreChecks::ValidateEventStageMask(cb.dev_data, &cb, event_added_count, first_event_index, srcStageMask, |
| localEventToStageMap); |
| }); |
| } |
| |
| void CoreChecks::PreCallRecordCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| VkPipelineStageFlags sourceStageMask, VkPipelineStageFlags dstStageMask, |
| uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, |
| uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, |
| uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) { |
| StateTracker::PreCallRecordCmdWaitEvents(commandBuffer, eventCount, pEvents, sourceStageMask, dstStageMask, |
| memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, |
| pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| TransitionImageLayouts(cb_state.get(), imageMemoryBarrierCount, pImageMemoryBarriers); |
| } |
| |
| void CoreChecks::RecordCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfo *pDependencyInfos, CMD_TYPE cmd_type) { |
| // don't hold read lock during the base class method |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| for (uint32_t i = 0; i < eventCount; i++) { |
| const auto &dep_info = pDependencyInfos[i]; |
| TransitionImageLayouts(cb_state.get(), dep_info.imageMemoryBarrierCount, dep_info.pImageMemoryBarriers); |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdWaitEvents2KHR(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfoKHR *pDependencyInfos) { |
| StateTracker::PreCallRecordCmdWaitEvents2KHR(commandBuffer, eventCount, pEvents, pDependencyInfos); |
| RecordCmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos, CMD_WAITEVENTS2KHR); |
| } |
| |
| void CoreChecks::PreCallRecordCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfo *pDependencyInfos) { |
| StateTracker::PreCallRecordCmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos); |
| RecordCmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos, CMD_WAITEVENTS2); |
| } |
| |
| void CoreChecks::PostCallRecordCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| VkPipelineStageFlags sourceStageMask, VkPipelineStageFlags dstStageMask, |
| uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, |
| uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, |
| uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) { |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| RecordBarriers(Func::vkCmdWaitEvents, cb_state.get(), bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, |
| pImageMemoryBarriers); |
| } |
| |
| void CoreChecks::PostCallRecordCmdWaitEvents2KHR(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, |
| const VkDependencyInfoKHR *pDependencyInfos) { |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| for (uint32_t i = 0; i < eventCount; i++) { |
| const auto &dep_info = pDependencyInfos[i]; |
| RecordBarriers(Func::vkCmdWaitEvents2, cb_state.get(), dep_info); |
| } |
| } |
| |
| void CoreChecks::PostCallRecordCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, |
| const VkDependencyInfo* pDependencyInfos) { |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| for (uint32_t i = 0; i < eventCount; i++) { |
| const auto& dep_info = pDependencyInfos[i]; |
| RecordBarriers(Func::vkCmdWaitEvents2, cb_state.get(), dep_info); |
| } |
| } |
| |
| bool CoreChecks::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 { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| LogObjectList objects(commandBuffer); |
| auto queue_flags = cb_state->GetQueueFlags(); |
| Location loc(Func::vkCmdPipelineBarrier); |
| |
| skip |= ValidatePipelineStage(objects, loc.dot(Field::srcStageMask), queue_flags, srcStageMask); |
| skip |= ValidatePipelineStage(objects, loc.dot(Field::dstStageMask), queue_flags, dstStageMask); |
| skip |= ValidateCmd(cb_state.get(), CMD_PIPELINEBARRIER); |
| if (cb_state->activeRenderPass && !cb_state->activeRenderPass->use_dynamic_rendering) { |
| skip |= ValidateRenderPassPipelineBarriers(loc, cb_state.get(), srcStageMask, dstStageMask, dependencyFlags, |
| memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, |
| pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); |
| if (skip) return true; // Early return to avoid redundant errors from below calls |
| } else { |
| if (dependencyFlags & VK_DEPENDENCY_VIEW_LOCAL_BIT) { |
| skip = LogError(objects, "VUID-vkCmdPipelineBarrier-dependencyFlags-01186", |
| "%s VK_DEPENDENCY_VIEW_LOCAL_BIT must not be set outside of a render pass instance", |
| loc.dot(Field::dependencyFlags).Message().c_str()); |
| } |
| } |
| if (cb_state->activeRenderPass && cb_state->activeRenderPass->use_dynamic_rendering) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdPipelineBarrier-None-06191", |
| "vkCmdPipelineBarrier(): a dynamic render pass instance is active."); |
| } |
| skip |= ValidateBarriers(loc, cb_state.get(), srcStageMask, dstStageMask, memoryBarrierCount, pMemoryBarriers, |
| bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo, |
| CMD_TYPE cmd_type) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| LogObjectList objects(commandBuffer); |
| const char *func_name = CommandTypeString(cmd_type); |
| |
| Location loc(Func::vkCmdPipelineBarrier2, Field::pDependencyInfo); |
| if (!enabled_features.core13.synchronization2) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdPipelineBarrier2-synchronization2-03848", |
| "%s(): Synchronization2 feature is not enabled", func_name); |
| } |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| if (cb_state->activeRenderPass) { |
| skip |= ValidateRenderPassPipelineBarriers(loc, cb_state.get(), pDependencyInfo); |
| if (skip) return true; // Early return to avoid redundant errors from below calls |
| } else { |
| if (pDependencyInfo->dependencyFlags & VK_DEPENDENCY_VIEW_LOCAL_BIT) { |
| skip = LogError(objects, "VUID-vkCmdPipelineBarrier2-dependencyFlags-01186", |
| "%s VK_DEPENDENCY_VIEW_LOCAL_BIT must not be set outside of a render pass instance", |
| loc.dot(Field::dependencyFlags).Message().c_str()); |
| } |
| } |
| if (cb_state->activeRenderPass && cb_state->activeRenderPass->use_dynamic_rendering) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdPipelineBarrier2-None-06191", |
| "vkCmdPipelineBarrier(): a dynamic render pass instance is active."); |
| } |
| skip |= ValidateDependencyInfo(objects, loc, cb_state.get(), pDependencyInfo); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdPipelineBarrier2KHR(VkCommandBuffer commandBuffer, |
| const VkDependencyInfoKHR *pDependencyInfo) const { |
| return ValidateCmdPipelineBarrier2(commandBuffer, pDependencyInfo, CMD_PIPELINEBARRIER2KHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo) const { |
| return ValidateCmdPipelineBarrier2(commandBuffer, pDependencyInfo, CMD_PIPELINEBARRIER2); |
| } |
| |
| void CoreChecks::PreCallRecordCmdPipelineBarrier(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) { |
| StateTracker::PreCallRecordCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, |
| pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, |
| imageMemoryBarrierCount, pImageMemoryBarriers); |
| |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| |
| RecordBarriers(Func::vkCmdPipelineBarrier, cb_state.get(), bufferMemoryBarrierCount, pBufferMemoryBarriers, |
| imageMemoryBarrierCount, pImageMemoryBarriers); |
| TransitionImageLayouts(cb_state.get(), imageMemoryBarrierCount, pImageMemoryBarriers); |
| |
| } |
| |
| void CoreChecks::PreCallRecordCmdPipelineBarrier2KHR(VkCommandBuffer commandBuffer, const VkDependencyInfoKHR *pDependencyInfo) { |
| StateTracker::PreCallRecordCmdPipelineBarrier2KHR(commandBuffer, pDependencyInfo); |
| |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| RecordBarriers(Func::vkCmdPipelineBarrier2, cb_state.get(), *pDependencyInfo); |
| TransitionImageLayouts(cb_state.get(), pDependencyInfo->imageMemoryBarrierCount, pDependencyInfo->pImageMemoryBarriers); |
| } |
| |
| void CoreChecks::PreCallRecordCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo *pDependencyInfo) { |
| StateTracker::PreCallRecordCmdPipelineBarrier2(commandBuffer, pDependencyInfo); |
| |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| RecordBarriers(Func::vkCmdPipelineBarrier2, cb_state.get(), *pDependencyInfo); |
| TransitionImageLayouts(cb_state.get(), pDependencyInfo->imageMemoryBarrierCount, pDependencyInfo->pImageMemoryBarriers); |
| } |
| |
| bool CoreChecks::ValidateBeginQuery(const CMD_BUFFER_STATE *cb_state, const QueryObject &query_obj, VkFlags flags, uint32_t index, |
| CMD_TYPE cmd, const ValidateBeginQueryVuids *vuids) const { |
| bool skip = false; |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query_obj.pool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| const char *cmd_name = CommandTypeString(cmd); |
| |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_TIMESTAMP) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBeginQuery-queryType-02804", |
| "%s: The querypool's query type must not be VK_QUERY_TYPE_TIMESTAMP.", cmd_name); |
| } |
| |
| // Check for nested queries |
| if (cb_state->activeQueries.size()) { |
| for (const auto &a_query : cb_state->activeQueries) { |
| auto active_query_pool_state = Get<QUERY_POOL_STATE>(a_query.pool); |
| if (active_query_pool_state->createInfo.queryType == query_pool_ci.queryType && a_query.index == index) { |
| LogObjectList obj_list(cb_state->commandBuffer()); |
| obj_list.add(query_obj.pool); |
| obj_list.add(a_query.pool); |
| skip |= LogError(obj_list, vuids->vuid_dup_query_type, |
| "%s: Within the same command buffer %s, query %d from pool %s has same queryType as active query " |
| "%d from pool %s.", |
| cmd_name, report_data->FormatHandle(cb_state->commandBuffer()).c_str(), query_obj.index, |
| report_data->FormatHandle(query_obj.pool).c_str(), a_query.index, |
| report_data->FormatHandle(a_query.pool).c_str()); |
| } |
| } |
| } |
| |
| // There are tighter queue constraints to test for certain query pools |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) { |
| skip |= ValidateCmdQueueFlags(cb_state, cmd_name, VK_QUEUE_GRAPHICS_BIT, vuids->vuid_queue_feedback); |
| if (!phys_dev_ext_props.transform_feedback_props.transformFeedbackQueries) { |
| const char *vuid = cmd == CMD_BEGINQUERYINDEXEDEXT ? "VUID-vkCmdBeginQueryIndexedEXT-queryType-02341" |
| : "VUID-vkCmdBeginQuery-queryType-02328"; |
| skip |= LogError(cb_state->commandBuffer(), vuid, |
| "%s: queryPool was created with queryType VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, but " |
| "VkPhysicalDeviceTransformFeedbackPropertiesEXT::transformFeedbackQueries is not supported.", |
| cmd_name); |
| } |
| } |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_OCCLUSION) { |
| skip |= ValidateCmdQueueFlags(cb_state, cmd_name, VK_QUEUE_GRAPHICS_BIT, vuids->vuid_queue_occlusion); |
| } |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) { |
| if (!cb_state->performance_lock_acquired) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_profile_lock, |
| "%s: profiling lock must be held before vkBeginCommandBuffer is called on " |
| "a command buffer where performance queries are recorded.", |
| cmd_name); |
| } |
| |
| if (query_pool_state->has_perf_scope_command_buffer && cb_state->commandCount > 0) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_scope_not_first, |
| "%s: Query pool %s was created with a counter of scope " |
| "VK_QUERY_SCOPE_COMMAND_BUFFER_KHR but %s is not the first recorded " |
| "command in the command buffer.", |
| cmd_name, report_data->FormatHandle(query_obj.pool).c_str(), cmd_name); |
| } |
| |
| if (query_pool_state->has_perf_scope_render_pass && cb_state->activeRenderPass) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_scope_in_rp, |
| "%s: Query pool %s was created with a counter of scope " |
| "VK_QUERY_SCOPE_RENDER_PASS_KHR but %s is inside a render pass.", |
| cmd_name, report_data->FormatHandle(query_obj.pool).c_str(), cmd_name); |
| } |
| } |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR || |
| query_pool_ci.queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR) { |
| const char *vuid = cmd == CMD_BEGINQUERYINDEXEDEXT ? "VUID-vkCmdBeginQueryIndexedEXT-queryType-04728" |
| : "VUID-vkCmdBeginQuery-queryType-04728"; |
| skip |= LogError(cb_state->commandBuffer(), vuid, "%s: QueryPool was created with queryType %s.", cmd_name, |
| string_VkQueryType(query_pool_ci.queryType)); |
| } |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV) { |
| const char *vuid = cmd == CMD_BEGINQUERYINDEXEDEXT ? "VUID-vkCmdBeginQueryIndexedEXT-queryType-04729" |
| : "VUID-vkCmdBeginQuery-queryType-04729"; |
| skip |= |
| LogError(cb_state->commandBuffer(), vuid, |
| "%s: QueryPool was created with queryType VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV.", cmd_name); |
| } |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) { |
| if ((cb_state->command_pool->queue_flags & VK_QUEUE_GRAPHICS_BIT) == 0) { |
| if (query_pool_ci.pipelineStatistics & |
| (VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT | VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT | |
| VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT)) { |
| skip |= LogError( |
| cb_state->commandBuffer(), vuids->vuid_graphics_support, |
| "%s(): queryType of queryPool is VK_QUERY_TYPE_PIPELINE_STATISTICS (%s) and indicates graphics operations, but " |
| "the command pool the command buffer %s was allocated from does not support graphics operations (%s).", |
| cmd_name, string_VkQueryPipelineStatisticFlags(query_pool_ci.pipelineStatistics).c_str(), |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| string_VkQueueFlags(cb_state->command_pool->queue_flags).c_str()); |
| } |
| } |
| if ((cb_state->command_pool->queue_flags & VK_QUEUE_COMPUTE_BIT) == 0) { |
| if (query_pool_ci.pipelineStatistics & VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT) { |
| skip |= LogError( |
| cb_state->commandBuffer(), vuids->vuid_compute_support, |
| "%s(): queryType of queryPool is VK_QUERY_TYPE_PIPELINE_STATISTICS (%s) and indicates compute operations, but " |
| "the command pool the command buffer %s was allocated from does not support compute operations (%s).", |
| cmd_name, string_VkQueryPipelineStatisticFlags(query_pool_ci.pipelineStatistics).c_str(), |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| string_VkQueueFlags(cb_state->command_pool->queue_flags).c_str()); |
| } |
| } |
| } |
| |
| skip |= ValidateCmdQueueFlags(cb_state, cmd_name, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, vuids->vuid_queue_flags); |
| |
| if (flags & VK_QUERY_CONTROL_PRECISE_BIT) { |
| if (!enabled_features.core.occlusionQueryPrecise) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_precise, |
| "%s: VK_QUERY_CONTROL_PRECISE_BIT provided, but precise occlusion queries not enabled on the device.", |
| cmd_name); |
| } |
| |
| if (query_pool_ci.queryType != VK_QUERY_TYPE_OCCLUSION) { |
| skip |= |
| LogError(cb_state->commandBuffer(), vuids->vuid_precise, |
| "%s: VK_QUERY_CONTROL_PRECISE_BIT provided, but pool query type is not VK_QUERY_TYPE_OCCLUSION", cmd_name); |
| } |
| } |
| |
| if (query_obj.query >= query_pool_ci.queryCount) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_query_count, |
| "%s: Query index %" PRIu32 " must be less than query count %" PRIu32 " of %s.", cmd_name, query_obj.query, |
| query_pool_ci.queryCount, report_data->FormatHandle(query_obj.pool).c_str()); |
| } |
| |
| if (cb_state->unprotected == false) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_protected_cb, |
| "%s: command can't be used in protected command buffers.", cmd_name); |
| } |
| |
| if (cb_state->activeRenderPass) { |
| const auto *render_pass_info = cb_state->activeRenderPass->createInfo.ptr(); |
| if (!cb_state->activeRenderPass->use_dynamic_rendering && !cb_state->activeRenderPass->use_dynamic_rendering_inherited) { |
| const auto *subpass_desc = &render_pass_info->pSubpasses[cb_state->activeSubpass]; |
| if (subpass_desc) { |
| constexpr int num_bits = sizeof(subpass_desc->viewMask) * CHAR_BIT; |
| std::bitset<num_bits> view_bits(subpass_desc->viewMask); |
| uint32_t bits = static_cast<uint32_t>(view_bits.count()); |
| if (query_obj.query + bits > query_pool_state->createInfo.queryCount) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_multiview_query, |
| "%s: query (%" PRIu32 ") + bits set in current subpass view mask (%" PRIu32 |
| ") is greater than the number of queries in queryPool (%" PRIu32 ").", |
| cmd_name, query_obj.query, bits, query_pool_state->createInfo.queryCount); |
| } |
| } |
| } |
| } |
| |
| skip |= ValidateCmd(cb_state, cmd); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot, |
| VkFlags flags) const { |
| if (disabled[query_validation]) return false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| QueryObject query_obj(queryPool, slot); |
| struct BeginQueryVuids : ValidateBeginQueryVuids { |
| BeginQueryVuids() : ValidateBeginQueryVuids() { |
| vuid_queue_flags = "VUID-vkCmdBeginQuery-commandBuffer-cmdpool"; |
| vuid_queue_feedback = "VUID-vkCmdBeginQuery-queryType-02327"; |
| vuid_queue_occlusion = "VUID-vkCmdBeginQuery-queryType-00803"; |
| vuid_precise = "VUID-vkCmdBeginQuery-queryType-00800"; |
| vuid_query_count = "VUID-vkCmdBeginQuery-query-00802"; |
| vuid_profile_lock = "VUID-vkCmdBeginQuery-queryPool-03223"; |
| vuid_scope_not_first = "VUID-vkCmdBeginQuery-queryPool-03224"; |
| vuid_scope_in_rp = "VUID-vkCmdBeginQuery-queryPool-03225"; |
| vuid_dup_query_type = "VUID-vkCmdBeginQuery-queryPool-01922"; |
| vuid_protected_cb = "VUID-vkCmdBeginQuery-commandBuffer-01885"; |
| vuid_multiview_query = "VUID-vkCmdBeginQuery-query-00808"; |
| vuid_graphics_support = "VUID-vkCmdBeginQuery-queryType-00804"; |
| vuid_compute_support = "VUID-vkCmdBeginQuery-queryType-00805"; |
| } |
| }; |
| BeginQueryVuids vuids; |
| return ValidateBeginQuery(cb_state.get(), query_obj, flags, 0, CMD_BEGINQUERY, &vuids); |
| } |
| |
| static QueryState GetLocalQueryState(const QueryMap *localQueryToStateMap, VkQueryPool queryPool, uint32_t queryIndex, |
| uint32_t perfPass) { |
| QueryObject query = QueryObject(QueryObject(queryPool, queryIndex), perfPass); |
| |
| auto iter = localQueryToStateMap->find(query); |
| if (iter != localQueryToStateMap->end()) return iter->second; |
| |
| return QUERYSTATE_UNKNOWN; |
| } |
| |
| bool CoreChecks::VerifyQueryIsReset(const ValidationStateTracker *state_data, VkCommandBuffer commandBuffer, QueryObject query_obj, |
| const CMD_TYPE cmd_type, VkQueryPool &firstPerfQueryPool, uint32_t perfPass, |
| QueryMap *localQueryToStateMap) { |
| bool skip = false; |
| |
| auto query_pool_state = state_data->Get<QUERY_POOL_STATE>(query_obj.pool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| |
| QueryState state = GetLocalQueryState(localQueryToStateMap, query_obj.pool, query_obj.query, perfPass); |
| // If reset was in another command buffer, check the global map |
| if (state == QUERYSTATE_UNKNOWN) { |
| state = query_pool_state->GetQueryState(query_obj.query, perfPass); |
| } |
| // Performance queries have limitation upon when they can be |
| // reset. |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR && state == QUERYSTATE_UNKNOWN && |
| perfPass >= query_pool_state->n_performance_passes) { |
| // If the pass is invalid, assume RESET state, another error |
| // will be raised in ValidatePerformanceQuery(). |
| state = QUERYSTATE_RESET; |
| } |
| |
| if (state != QUERYSTATE_RESET) { |
| skip |= state_data->LogError(commandBuffer, kVUID_Core_DrawState_QueryNotReset, |
| "%s: %s and query %" PRIu32 |
| ": query not reset. " |
| "After query pool creation, each query must be reset before it is used. " |
| "Queries must also be reset between uses.", |
| CommandTypeString(cmd_type), state_data->report_data->FormatHandle(query_obj.pool).c_str(), |
| query_obj.query); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePerformanceQuery(const ValidationStateTracker *state_data, VkCommandBuffer commandBuffer, |
| QueryObject query_obj, const CMD_TYPE cmd_type, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| auto query_pool_state = state_data->Get<QUERY_POOL_STATE>(query_obj.pool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| |
| if (query_pool_ci.queryType != VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) return false; |
| |
| auto cb_state = state_data->GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| |
| if (perfPass >= query_pool_state->n_performance_passes) { |
| skip |= state_data->LogError(commandBuffer, "VUID-VkPerformanceQuerySubmitInfoKHR-counterPassIndex-03221", |
| "%s: Invalid counterPassIndex (%u, maximum allowed %u) value for query pool %s.", |
| CommandTypeString(cmd_type), perfPass, query_pool_state->n_performance_passes, |
| state_data->report_data->FormatHandle(query_obj.pool).c_str()); |
| } |
| |
| if (!cb_state->performance_lock_acquired || cb_state->performance_lock_released) { |
| skip |= state_data->LogError(commandBuffer, "VUID-vkQueueSubmit-pCommandBuffers-03220", |
| "%s: Commandbuffer %s was submitted and contains a performance query but the" |
| "profiling lock was not held continuously throughout the recording of commands.", |
| CommandTypeString(cmd_type), state_data->report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| |
| QueryState command_buffer_state = GetLocalQueryState(localQueryToStateMap, query_obj.pool, query_obj.query, perfPass); |
| if (command_buffer_state == QUERYSTATE_RESET) { |
| skip |= state_data->LogError( |
| commandBuffer, query_obj.indexed ? "VUID-vkCmdBeginQueryIndexedEXT-None-02863" : "VUID-vkCmdBeginQuery-None-02863", |
| "%s: VkQuery begin command recorded in a command buffer that, either directly or " |
| "through secondary command buffers, also contains a vkCmdResetQueryPool command " |
| "affecting the same query.", |
| CommandTypeString(cmd_type)); |
| } |
| |
| if (firstPerfQueryPool != VK_NULL_HANDLE) { |
| if (firstPerfQueryPool != query_obj.pool && |
| !state_data->enabled_features.performance_query_features.performanceCounterMultipleQueryPools) { |
| skip |= state_data->LogError( |
| commandBuffer, |
| query_obj.indexed ? "VUID-vkCmdBeginQueryIndexedEXT-queryPool-03226" : "VUID-vkCmdBeginQuery-queryPool-03226", |
| "%s: Commandbuffer %s contains more than one performance query pool but " |
| "performanceCounterMultipleQueryPools is not enabled.", |
| CommandTypeString(cmd_type), state_data->report_data->FormatHandle(commandBuffer).c_str()); |
| } |
| } else { |
| firstPerfQueryPool = query_obj.pool; |
| } |
| |
| return skip; |
| } |
| |
| void CoreChecks::EnqueueVerifyBeginQuery(VkCommandBuffer command_buffer, const QueryObject &query_obj, const CMD_TYPE cmd_type) { |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(command_buffer); |
| |
| // Enqueue the submit time validation here, ahead of the submit time state update in the StateTracker's PostCallRecord |
| cb_state->queryUpdates.emplace_back([command_buffer, query_obj, cmd_type](const ValidationStateTracker *device_data, |
| bool do_validate, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| bool skip = false; |
| skip |= ValidatePerformanceQuery(device_data, command_buffer, query_obj, cmd_type, firstPerfQueryPool, perfPass, |
| localQueryToStateMap); |
| skip |= VerifyQueryIsReset(device_data, command_buffer, query_obj, cmd_type, firstPerfQueryPool, perfPass, |
| localQueryToStateMap); |
| return skip; |
| }); |
| } |
| |
| void CoreChecks::PreCallRecordCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot, VkFlags flags) { |
| if (disabled[query_validation]) return; |
| QueryObject query_obj = {queryPool, slot}; |
| EnqueueVerifyBeginQuery(commandBuffer, query_obj, CMD_BEGINQUERY); |
| } |
| |
| void CoreChecks::EnqueueVerifyEndQuery(CMD_BUFFER_STATE &cb_state, const QueryObject &query_obj) { |
| auto command_buffer = cb_state.commandBuffer(); |
| // Enqueue the submit time validation here, ahead of the submit time state update in the StateTracker's PostCallRecord |
| cb_state.queryUpdates.emplace_back([command_buffer, query_obj](const ValidationStateTracker *device_data, bool do_validate, |
| VkQueryPool &firstPerfQueryPool, uint32_t perfPass, |
| QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| bool skip = false; |
| // TODO there is no way to know if this command buffer is locked or not, because this lambda runs at queue submit time, |
| // and the callbacks in secondary command buffers are intermingled with the callbacks for the primary command buffer. |
| // (see CMD_BUFFER_STATE::ExecuteCommands) |
| auto cb_state = device_data->Get<CMD_BUFFER_STATE>(command_buffer); |
| auto query_pool_state = device_data->Get<QUERY_POOL_STATE>(query_obj.pool); |
| if (query_pool_state->has_perf_scope_command_buffer && (cb_state->commandCount - 1) != query_obj.endCommandIndex) { |
| skip |= device_data->LogError(command_buffer, "VUID-vkCmdEndQuery-queryPool-03227", |
| "vkCmdEndQuery: Query pool %s was created with a counter of scope" |
| "VK_QUERY_SCOPE_COMMAND_BUFFER_KHR but the end of the query is not the last " |
| "command in the command buffer %s.", |
| device_data->report_data->FormatHandle(query_obj.pool).c_str(), |
| device_data->report_data->FormatHandle(command_buffer).c_str()); |
| } |
| return skip; |
| }); |
| } |
| |
| bool CoreChecks::ValidateCmdEndQuery(const CMD_BUFFER_STATE *cb_state, const QueryObject &query_obj, uint32_t index, CMD_TYPE cmd, |
| const ValidateEndQueryVuids *vuids) const { |
| bool skip = false; |
| const char *cmd_name = CommandTypeString(cmd); |
| if (!cb_state->activeQueries.count(query_obj)) { |
| skip |= |
| LogError(cb_state->commandBuffer(), vuids->vuid_active_queries, "%s: Ending a query before it was started: %s, index %d.", |
| cmd_name, report_data->FormatHandle(query_obj.pool).c_str(), query_obj.query); |
| } |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query_obj.pool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) { |
| if (query_pool_state->has_perf_scope_render_pass && cb_state->activeRenderPass) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdEndQuery-queryPool-03228", |
| "%s: Query pool %s was created with a counter of scope " |
| "VK_QUERY_SCOPE_RENDER_PASS_KHR but %s is inside a render pass.", |
| cmd_name, report_data->FormatHandle(query_obj.pool).c_str(), cmd_name); |
| } |
| } |
| skip |= ValidateCmdQueueFlags(cb_state, cmd_name, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, vuids->vuid_queue_flags); |
| skip |= ValidateCmd(cb_state, cmd); |
| |
| if (cb_state->unprotected == false) { |
| skip |= LogError(cb_state->commandBuffer(), vuids->vuid_protected_cb, |
| "%s: command can't be used in protected command buffers.", cmd_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot) const { |
| if (disabled[query_validation]) return false; |
| bool skip = false; |
| QueryObject query_obj = {queryPool, slot}; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if (query_pool_state) { |
| const uint32_t available_query_count = query_pool_state->createInfo.queryCount; |
| // Only continue validating if the slot is even within range |
| if (slot >= available_query_count) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdEndQuery-query-00810", |
| "vkCmdEndQuery(): query index (%u) is greater or equal to the queryPool size (%u).", slot, |
| available_query_count); |
| } else { |
| struct EndQueryVuids : ValidateEndQueryVuids { |
| EndQueryVuids() : ValidateEndQueryVuids() { |
| vuid_queue_flags = "VUID-vkCmdEndQuery-commandBuffer-cmdpool"; |
| vuid_active_queries = "VUID-vkCmdEndQuery-None-01923"; |
| vuid_protected_cb = "VUID-vkCmdEndQuery-commandBuffer-01886"; |
| } |
| }; |
| EndQueryVuids vuids; |
| skip |= ValidateCmdEndQuery(cb_state.get(), query_obj, 0, CMD_ENDQUERY, &vuids); |
| } |
| } |
| return skip; |
| } |
| |
| void CoreChecks::PreCallRecordCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t slot) { |
| if (disabled[query_validation]) return; |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| QueryObject query_obj = {queryPool, slot}; |
| query_obj.endCommandIndex = cb_state->commandCount - 1; |
| EnqueueVerifyEndQuery(*cb_state, query_obj); |
| } |
| |
| bool CoreChecks::ValidateQueryPoolIndex(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, const char *func_name, |
| const char *first_vuid, const char *sum_vuid) const { |
| bool skip = false; |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if (query_pool_state) { |
| const uint32_t available_query_count = query_pool_state->createInfo.queryCount; |
| if (firstQuery >= available_query_count) { |
| skip |= LogError(queryPool, first_vuid, |
| "%s: In Query %s the firstQuery (%u) is greater or equal to the queryPool size (%u).", func_name, |
| report_data->FormatHandle(queryPool).c_str(), firstQuery, available_query_count); |
| } |
| if ((firstQuery + queryCount) > available_query_count) { |
| skip |= |
| LogError(queryPool, sum_vuid, |
| "%s: In Query %s the sum of firstQuery (%u) + queryCount (%u) is greater than the queryPool size (%u).", |
| func_name, report_data->FormatHandle(queryPool).c_str(), firstQuery, queryCount, available_query_count); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdResetQueryPool(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, |
| uint32_t queryCount) const { |
| if (disabled[query_validation]) return false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_RESETQUERYPOOL); |
| skip |= ValidateQueryPoolIndex(queryPool, firstQuery, queryCount, "VkCmdResetQueryPool()", |
| "VUID-vkCmdResetQueryPool-firstQuery-00796", "VUID-vkCmdResetQueryPool-firstQuery-00797"); |
| |
| return skip; |
| } |
| |
| static QueryResultType GetQueryResultType(QueryState state, VkQueryResultFlags flags) { |
| switch (state) { |
| case QUERYSTATE_UNKNOWN: |
| return QUERYRESULT_UNKNOWN; |
| case QUERYSTATE_RESET: |
| case QUERYSTATE_RUNNING: |
| if (flags & VK_QUERY_RESULT_WAIT_BIT) { |
| return ((state == QUERYSTATE_RESET) ? QUERYRESULT_WAIT_ON_RESET : QUERYRESULT_WAIT_ON_RUNNING); |
| } else if ((flags & VK_QUERY_RESULT_PARTIAL_BIT) || (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)) { |
| return QUERYRESULT_SOME_DATA; |
| } else { |
| return QUERYRESULT_NO_DATA; |
| } |
| case QUERYSTATE_ENDED: |
| if ((flags & VK_QUERY_RESULT_WAIT_BIT) || (flags & VK_QUERY_RESULT_PARTIAL_BIT) || |
| (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)) { |
| return QUERYRESULT_SOME_DATA; |
| } else { |
| return QUERYRESULT_UNKNOWN; |
| } |
| case QUERYSTATE_AVAILABLE: |
| return QUERYRESULT_SOME_DATA; |
| } |
| assert(false); |
| return QUERYRESULT_UNKNOWN; |
| } |
| |
| bool CoreChecks::ValidateCopyQueryPoolResults(const ValidationStateTracker *state_data, VkCommandBuffer commandBuffer, |
| VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, uint32_t perfPass, |
| VkQueryResultFlags flags, QueryMap *localQueryToStateMap) { |
| bool skip = false; |
| for (uint32_t i = 0; i < queryCount; i++) { |
| QueryState state = GetLocalQueryState(localQueryToStateMap, queryPool, firstQuery + i, perfPass); |
| QueryResultType result_type = GetQueryResultType(state, flags); |
| if (result_type != QUERYRESULT_SOME_DATA && result_type != QUERYRESULT_UNKNOWN) { |
| skip |= state_data->LogError( |
| commandBuffer, kVUID_Core_DrawState_InvalidQuery, |
| "vkCmdCopyQueryPoolResults(): Requesting a copy from query to buffer on %s query %" PRIu32 ": %s", |
| state_data->report_data->FormatHandle(queryPool).c_str(), firstQuery + i, string_QueryResultType(result_type)); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, |
| uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, |
| VkDeviceSize stride, VkQueryResultFlags flags) const { |
| if (disabled[query_validation]) return false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| auto dst_buff_state = Get<BUFFER_STATE>(dstBuffer); |
| assert(cb_state); |
| assert(dst_buff_state); |
| bool skip = ValidateMemoryIsBoundToBuffer(dst_buff_state.get(), "vkCmdCopyQueryPoolResults()", |
| "VUID-vkCmdCopyQueryPoolResults-dstBuffer-00826"); |
| skip |= ValidateQueryPoolStride("VUID-vkCmdCopyQueryPoolResults-flags-00822", "VUID-vkCmdCopyQueryPoolResults-flags-00823", |
| stride, "dstOffset", dstOffset, flags); |
| // Validate that DST buffer has correct usage flags set |
| skip |= ValidateBufferUsageFlags(dst_buff_state.get(), VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, |
| "VUID-vkCmdCopyQueryPoolResults-dstBuffer-00825", "vkCmdCopyQueryPoolResults()", |
| "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); |
| skip |= ValidateCmd(cb_state.get(), CMD_COPYQUERYPOOLRESULTS); |
| skip |= ValidateQueryPoolIndex(queryPool, firstQuery, queryCount, "vkCmdCopyQueryPoolResults()", |
| "VUID-vkCmdCopyQueryPoolResults-firstQuery-00820", |
| "VUID-vkCmdCopyQueryPoolResults-firstQuery-00821"); |
| |
| if (dstOffset >= dst_buff_state->requirements.size) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdCopyQueryPoolResults-dstOffset-00819", |
| "vkCmdCopyQueryPoolResults() dstOffset (0x%" PRIxLEAST64 ") is not less than the size (0x%" PRIxLEAST64 |
| ") of buffer (%s).", |
| dstOffset, dst_buff_state->requirements.size, report_data->FormatHandle(dst_buff_state->buffer()).c_str()); |
| } else if (dstOffset + (queryCount * stride) > dst_buff_state->requirements.size) { |
| skip |= |
| LogError(commandBuffer, "VUID-vkCmdCopyQueryPoolResults-dstBuffer-00824", |
| "vkCmdCopyQueryPoolResults() storage required (0x%" PRIxLEAST64 |
| ") equal to dstOffset + (queryCount * stride) is greater than the size (0x%" PRIxLEAST64 ") of buffer (%s).", |
| dstOffset + (queryCount * stride), dst_buff_state->requirements.size, |
| report_data->FormatHandle(dst_buff_state->buffer()).c_str()); |
| } |
| |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if (query_pool_state) { |
| if (query_pool_state->createInfo.queryType == VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR) { |
| skip |= |
| ValidatePerformanceQueryResults("vkCmdCopyQueryPoolResults", query_pool_state.get(), firstQuery, queryCount, flags); |
| if (!phys_dev_ext_props.performance_query_props.allowCommandBufferQueryCopies) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdCopyQueryPoolResults-queryType-03232", |
| "vkCmdCopyQueryPoolResults called with query pool %s but " |
| "VkPhysicalDevicePerformanceQueryPropertiesKHR::allowCommandBufferQueryCopies " |
| "is not set.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| } |
| if ((query_pool_state->createInfo.queryType == VK_QUERY_TYPE_TIMESTAMP) && ((flags & VK_QUERY_RESULT_PARTIAL_BIT) != 0)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdCopyQueryPoolResults-queryType-00827", |
| "vkCmdCopyQueryPoolResults() query pool %s was created with VK_QUERY_TYPE_TIMESTAMP so flags must not " |
| "contain VK_QUERY_RESULT_PARTIAL_BIT.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| if (query_pool_state->createInfo.queryType == VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL) { |
| skip |= LogError(queryPool, "VUID-vkCmdCopyQueryPoolResults-queryType-02734", |
| "vkCmdCopyQueryPoolResults() called but QueryPool %s was created with queryType " |
| "VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| |
| void CoreChecks::PreCallRecordCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, |
| uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, |
| VkDeviceSize stride, VkQueryResultFlags flags) { |
| if (disabled[query_validation]) return; |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| cb_state->queryUpdates.emplace_back([commandBuffer, queryPool, firstQuery, queryCount, flags]( |
| const ValidationStateTracker *device_data, bool do_validate, |
| VkQueryPool &firstPerfQueryPool, uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| return ValidateCopyQueryPoolResults(device_data, commandBuffer, queryPool, firstQuery, queryCount, perfPass, flags, |
| localQueryToStateMap); |
| }); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, |
| VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, |
| const void *pValues) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| skip |= ValidateCmd(cb_state.get(), CMD_PUSHCONSTANTS); |
| |
| // Check if pipeline_layout VkPushConstantRange(s) overlapping offset, size have stageFlags set for each stage in the command |
| // stageFlags argument, *and* that the command stageFlags argument has bits set for the stageFlags in each overlapping range. |
| if (!skip) { |
| auto layout_state = Get<PIPELINE_LAYOUT_STATE>(layout); |
| const auto &ranges = *layout_state->push_constant_ranges; |
| VkShaderStageFlags found_stages = 0; |
| for (const auto &range : ranges) { |
| if ((offset >= range.offset) && (offset + size <= range.offset + range.size)) { |
| VkShaderStageFlags matching_stages = range.stageFlags & stageFlags; |
| if (matching_stages != range.stageFlags) { |
| skip |= |
| LogError(commandBuffer, "VUID-vkCmdPushConstants-offset-01796", |
| "vkCmdPushConstants(): stageFlags (%s, offset (%" PRIu32 "), and size (%" PRIu32 |
| "), must contain all stages in overlapping VkPushConstantRange stageFlags (%s), offset (%" PRIu32 |
| "), and size (%" PRIu32 ") in %s.", |
| string_VkShaderStageFlags(stageFlags).c_str(), offset, size, |
| string_VkShaderStageFlags(range.stageFlags).c_str(), range.offset, range.size, |
| report_data->FormatHandle(layout).c_str()); |
| } |
| |
| // Accumulate all stages we've found |
| found_stages = matching_stages | found_stages; |
| } |
| } |
| if (found_stages != stageFlags) { |
| uint32_t missing_stages = ~found_stages & stageFlags; |
| skip |= LogError( |
| commandBuffer, "VUID-vkCmdPushConstants-offset-01795", |
| "vkCmdPushConstants(): %s, VkPushConstantRange in %s overlapping offset = %d and size = %d, do not contain %s.", |
| string_VkShaderStageFlags(stageFlags).c_str(), report_data->FormatHandle(layout).c_str(), offset, size, |
| string_VkShaderStageFlags(missing_stages).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, |
| VkQueryPool queryPool, uint32_t slot) const { |
| if (disabled[query_validation]) return false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_WRITETIMESTAMP); |
| |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if ((query_pool_state != nullptr) && (query_pool_state->createInfo.queryType != VK_QUERY_TYPE_TIMESTAMP)) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp-queryPool-01416", |
| "vkCmdWriteTimestamp(): Query Pool %s was not created with VK_QUERY_TYPE_TIMESTAMP.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| const uint32_t timestamp_valid_bits = |
| physical_device_state->queue_family_properties[cb_state->command_pool->queueFamilyIndex].timestampValidBits; |
| if (timestamp_valid_bits == 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp-timestampValidBits-00829", |
| "vkCmdWriteTimestamp(): Query Pool %s has a timestampValidBits value of zero.", |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| if ((query_pool_state != nullptr) && (slot >= query_pool_state->createInfo.queryCount)) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp-query-04904", |
| "vkCmdWriteTimestamp(): query (%" PRIu32 ") is not lower than the number of queries (%" PRIu32 |
| ") in Query pool %s.", |
| slot, query_pool_state->createInfo.queryCount, report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, |
| uint32_t slot, CMD_TYPE cmd_type) const { |
| if (disabled[query_validation]) return false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| |
| const char *func_name = CommandTypeString(cmd_type); |
| if (!enabled_features.core13.synchronization2) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdWriteTimestamp2-synchronization2-03858", |
| "%s(): Synchronization2 feature is not enabled", func_name); |
| } |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| |
| Location loc(Func::vkCmdWriteTimestamp2, Field::stage); |
| if ((stage & (stage - 1)) != 0) { |
| skip |= |
| LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp2-stage-03859", |
| "%s (%s) must only set a single pipeline stage.", func_name, string_VkPipelineStageFlags2KHR(stage).c_str()); |
| } |
| skip |= ValidatePipelineStage(LogObjectList(cb_state->commandBuffer()), loc, cb_state->GetQueueFlags(), stage); |
| |
| loc.field = Field::queryPool; |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| |
| if (query_pool_state) { |
| if (query_pool_state->createInfo.queryType != VK_QUERY_TYPE_TIMESTAMP) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp2-queryPool-03861", |
| "%s Query Pool %s was not created with VK_QUERY_TYPE_TIMESTAMP.", func_name, |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| if (slot >= query_pool_state->createInfo.queryCount) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp2-query-04903", |
| "vkCmdWriteTimestamp2KHR(): query (%" PRIu32 ") is not lower than the number of queries (%" PRIu32 |
| ") in Query pool %s.", |
| slot, query_pool_state->createInfo.queryCount, report_data->FormatHandle(queryPool).c_str()); |
| } |
| } |
| |
| const uint32_t timestampValidBits = |
| physical_device_state->queue_family_properties[cb_state->command_pool->queueFamilyIndex].timestampValidBits; |
| if (timestampValidBits == 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdWriteTimestamp2-timestampValidBits-03863", |
| "%s Query Pool %s has a timestampValidBits value of zero.", func_name, |
| report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWriteTimestamp2KHR(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, |
| VkQueryPool queryPool, uint32_t query) const { |
| return ValidateCmdWriteTimestamp2(commandBuffer, stage, queryPool, query, CMD_WRITETIMESTAMP2KHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, |
| VkQueryPool queryPool, uint32_t query) const { |
| return ValidateCmdWriteTimestamp2(commandBuffer, stage, queryPool, query, CMD_WRITETIMESTAMP2); |
| } |
| |
| void CoreChecks::PreCallRecordCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, |
| VkQueryPool queryPool, uint32_t slot) { |
| if (disabled[query_validation]) return; |
| // Enqueue the submit time validation check here, before the submit time state update in StateTracker::PostCall... |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| QueryObject query = {queryPool, slot}; |
| CMD_TYPE cmd_type = CMD_WRITETIMESTAMP; |
| cb_state->queryUpdates.emplace_back([commandBuffer, query, cmd_type](const ValidationStateTracker *device_data, |
| bool do_validate, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| return VerifyQueryIsReset(device_data, commandBuffer, query, cmd_type, firstPerfQueryPool, perfPass, localQueryToStateMap); |
| }); |
| } |
| |
| void CoreChecks::PreCallRecordCmdWriteTimestamp2KHR(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR pipelineStage, |
| VkQueryPool queryPool, uint32_t slot) { |
| if (disabled[query_validation]) return; |
| // Enqueue the submit time validation check here, before the submit time state update in StateTracker::PostCall... |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| QueryObject query = {queryPool, slot}; |
| CMD_TYPE cmd_type = CMD_WRITETIMESTAMP2KHR; |
| cb_state->queryUpdates.emplace_back([commandBuffer, query, cmd_type](const ValidationStateTracker *device_data, |
| bool do_validate, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| return VerifyQueryIsReset(device_data, commandBuffer, query, cmd_type, firstPerfQueryPool, perfPass, localQueryToStateMap); |
| }); |
| } |
| |
| void CoreChecks::PreCallRecordCmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 pipelineStage, |
| VkQueryPool queryPool, uint32_t slot) { |
| if (disabled[query_validation]) return; |
| // Enqueue the submit time validation check here, before the submit time state update in StateTracker::PostCall... |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| QueryObject query = {queryPool, slot}; |
| CMD_TYPE cmd_type = CMD_WRITETIMESTAMP2; |
| cb_state->queryUpdates.emplace_back([commandBuffer, query, cmd_type](const ValidationStateTracker *device_data, |
| bool do_validate, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| return VerifyQueryIsReset(device_data, commandBuffer, query, cmd_type, firstPerfQueryPool, perfPass, localQueryToStateMap); |
| }); |
| } |
| |
| void CoreChecks::PreCallRecordCmdWriteAccelerationStructuresPropertiesKHR(VkCommandBuffer commandBuffer, |
| uint32_t accelerationStructureCount, |
| const VkAccelerationStructureKHR *pAccelerationStructures, |
| VkQueryType queryType, VkQueryPool queryPool, |
| uint32_t firstQuery) { |
| if (disabled[query_validation]) return; |
| // Enqueue the submit time validation check here, before the submit time state update in StateTracker::PostCall... |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| CMD_TYPE cmd_type = CMD_WRITEACCELERATIONSTRUCTURESPROPERTIESKHR; |
| cb_state->queryUpdates.emplace_back([accelerationStructureCount, commandBuffer, firstQuery, queryPool, cmd_type]( |
| const ValidationStateTracker *device_data, bool do_validate, |
| VkQueryPool &firstPerfQueryPool, uint32_t perfPass, QueryMap *localQueryToStateMap) { |
| if (!do_validate) return false; |
| bool skip = false; |
| for (uint32_t i = 0; i < accelerationStructureCount; i++) { |
| QueryObject query = {{queryPool, firstQuery + i}, perfPass}; |
| skip |= |
| VerifyQueryIsReset(device_data, commandBuffer, query, cmd_type, firstPerfQueryPool, perfPass, localQueryToStateMap); |
| } |
| return skip; |
| }); |
| } |
| |
| bool CoreChecks::MatchUsage(uint32_t count, const VkAttachmentReference2 *attachments, const VkFramebufferCreateInfo *fbci, |
| VkImageUsageFlagBits usage_flag, const char *error_code) const { |
| bool skip = false; |
| |
| if (attachments) { |
| for (uint32_t attach = 0; attach < count; attach++) { |
| if (attachments[attach].attachment != VK_ATTACHMENT_UNUSED) { |
| // Attachment counts are verified elsewhere, but prevent an invalid access |
| if (attachments[attach].attachment < fbci->attachmentCount) { |
| if ((fbci->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0) { |
| const VkImageView *image_view = &fbci->pAttachments[attachments[attach].attachment]; |
| auto view_state = Get<IMAGE_VIEW_STATE>(*image_view); |
| if (view_state) { |
| const auto &ici = view_state->image_state->createInfo; |
| auto creation_usage = ici.usage; |
| const auto stencil_usage_info = LvlFindInChain<VkImageStencilUsageCreateInfo>(ici.pNext); |
| if (stencil_usage_info) { |
| creation_usage |= stencil_usage_info->stencilUsage; |
| } |
| if ((creation_usage & usage_flag) == 0) { |
| skip |= LogError(device, error_code, |
| "vkCreateFramebuffer: Framebuffer Attachment (%d) conflicts with the image's " |
| "IMAGE_USAGE flags (%s).", |
| attachments[attach].attachment, string_VkImageUsageFlagBits(usage_flag)); |
| } |
| } |
| } else { |
| const VkFramebufferAttachmentsCreateInfo *fbaci = |
| LvlFindInChain<VkFramebufferAttachmentsCreateInfo>(fbci->pNext); |
| if (fbaci != nullptr && fbaci->pAttachmentImageInfos != nullptr && |
| fbaci->attachmentImageInfoCount > attachments[attach].attachment) { |
| uint32_t image_usage = fbaci->pAttachmentImageInfos[attachments[attach].attachment].usage; |
| if ((image_usage & usage_flag) == 0) { |
| skip |= |
| LogError(device, error_code, |
| "vkCreateFramebuffer: Framebuffer attachment info (%d) conflicts with the image's " |
| "IMAGE_USAGE flags (%s).", |
| attachments[attach].attachment, string_VkImageUsageFlagBits(usage_flag)); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateFramebufferCreateInfo(const VkFramebufferCreateInfo *pCreateInfo) const { |
| bool skip = false; |
| |
| const VkFramebufferAttachmentsCreateInfo *framebuffer_attachments_create_info = |
| LvlFindInChain<VkFramebufferAttachmentsCreateInfo>(pCreateInfo->pNext); |
| if ((pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) != 0) { |
| if (!enabled_features.core12.imagelessFramebuffer) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-03189", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo flags includes VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, " |
| "but the imagelessFramebuffer feature is not enabled."); |
| } |
| |
| if (framebuffer_attachments_create_info == nullptr) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-03190", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo flags includes VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, " |
| "but no instance of VkFramebufferAttachmentsCreateInfo is present in the pNext chain."); |
| } else { |
| if (framebuffer_attachments_create_info->attachmentImageInfoCount != 0 && |
| framebuffer_attachments_create_info->attachmentImageInfoCount != pCreateInfo->attachmentCount) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-03191", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachmentCount is %u, but " |
| "VkFramebufferAttachmentsCreateInfo attachmentImageInfoCount is %u.", |
| pCreateInfo->attachmentCount, framebuffer_attachments_create_info->attachmentImageInfoCount); |
| } |
| } |
| } |
| if (framebuffer_attachments_create_info) { |
| for (uint32_t i = 0; i < framebuffer_attachments_create_info->attachmentImageInfoCount; ++i) { |
| if (framebuffer_attachments_create_info->pAttachmentImageInfos[i].pNext != nullptr) { |
| skip |= LogError(device, "VUID-VkFramebufferAttachmentImageInfo-pNext-pNext", |
| "vkCreateFramebuffer(): VkFramebufferAttachmentsCreateInfo[%" PRIu32 "].pNext is not NULL.", i); |
| } |
| } |
| } |
| |
| auto rp_state = Get<RENDER_PASS_STATE>(pCreateInfo->renderPass); |
| if (rp_state) { |
| const VkRenderPassCreateInfo2 *rpci = rp_state->createInfo.ptr(); |
| |
| bool b_has_non_zero_view_masks = false; |
| for (uint32_t i = 0; i < rpci->subpassCount; ++i) { |
| if (rpci->pSubpasses[i].viewMask != 0) { |
| b_has_non_zero_view_masks = true; |
| break; |
| } |
| } |
| |
| if (rpci->attachmentCount != pCreateInfo->attachmentCount) { |
| skip |= LogError(pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-attachmentCount-00876", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachmentCount of %u does not match attachmentCount " |
| "of %u of %s being used to create Framebuffer.", |
| pCreateInfo->attachmentCount, rpci->attachmentCount, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } else { |
| // attachmentCounts match, so make sure corresponding attachment details line up |
| if ((pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0) { |
| const VkImageView *image_views = pCreateInfo->pAttachments; |
| for (uint32_t i = 0; i < pCreateInfo->attachmentCount; ++i) { |
| auto view_state = Get<IMAGE_VIEW_STATE>(image_views[i]); |
| if (view_state == nullptr) { |
| skip |= LogError( |
| image_views[i], "VUID-VkFramebufferCreateInfo-flags-02778", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u is not a valid VkImageView.", i); |
| } else { |
| auto &ivci = view_state->create_info; |
| auto &subresource_range = view_state->normalized_subresource_range; |
| if (ivci.format != rpci->pAttachments[i].format) { |
| skip |= LogError( |
| pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-pAttachments-00880", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has format of %s that does not " |
| "match the format of %s used by the corresponding attachment for %s.", |
| i, string_VkFormat(ivci.format), string_VkFormat(rpci->pAttachments[i].format), |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } |
| const auto &ici = view_state->image_state->createInfo; |
| if (ici.samples != rpci->pAttachments[i].samples) { |
| skip |= |
| LogError(pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-pAttachments-00881", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has %s samples that do not " |
| "match the %s " |
| "samples used by the corresponding attachment for %s.", |
| i, string_VkSampleCountFlagBits(ici.samples), |
| string_VkSampleCountFlagBits(rpci->pAttachments[i].samples), |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } |
| |
| // Verify that image memory is valid |
| auto image_data = Get<IMAGE_STATE>(ivci.image); |
| skip |= ValidateMemoryIsBoundToImage(image_data.get(), "vkCreateFramebuffer()", |
| kVUID_Core_Bound_Resource_FreedMemoryAccess); |
| |
| // Verify that view only has a single mip level |
| if (subresource_range.levelCount != 1) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-pAttachments-00883", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has mip levelCount of %u but " |
| "only a single mip level (levelCount == 1) is allowed when creating a Framebuffer.", |
| i, subresource_range.levelCount); |
| } |
| const uint32_t mip_level = subresource_range.baseMipLevel; |
| uint32_t mip_width = max(1u, ici.extent.width >> mip_level); |
| uint32_t mip_height = max(1u, ici.extent.height >> mip_level); |
| bool used_as_input_color_resolve_depth_stencil_attachment = false; |
| bool used_as_fragment_shading_rate_attachment = false; |
| bool fsr_non_zero_viewmasks = false; |
| |
| for (uint32_t j = 0; j < rpci->subpassCount; ++j) { |
| const VkSubpassDescription2 &subpass = rpci->pSubpasses[j]; |
| |
| int highest_view_bit = MostSignificantBit(subpass.viewMask); |
| |
| for (uint32_t k = 0; k < rpci->pSubpasses[j].inputAttachmentCount; ++k) { |
| if (subpass.pInputAttachments[k].attachment == i) { |
| used_as_input_color_resolve_depth_stencil_attachment = true; |
| break; |
| } |
| } |
| |
| for (uint32_t k = 0; k < rpci->pSubpasses[j].colorAttachmentCount; ++k) { |
| if (subpass.pColorAttachments[k].attachment == i || |
| (subpass.pResolveAttachments && subpass.pResolveAttachments[k].attachment == i)) { |
| used_as_input_color_resolve_depth_stencil_attachment = true; |
| break; |
| } |
| } |
| |
| if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment == i) { |
| used_as_input_color_resolve_depth_stencil_attachment = true; |
| } |
| |
| if (used_as_input_color_resolve_depth_stencil_attachment) { |
| if (static_cast<int32_t>(subresource_range.layerCount) <= highest_view_bit) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-renderPass-04536", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has a layer count (%u) " |
| "less than or equal to the highest bit in the view mask (%i) of subpass %u.", |
| i, subresource_range.layerCount, highest_view_bit, j); |
| } |
| } |
| |
| if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate) { |
| const VkFragmentShadingRateAttachmentInfoKHR *fsr_attachment; |
| fsr_attachment = LvlFindInChain<VkFragmentShadingRateAttachmentInfoKHR>(subpass.pNext); |
| if (fsr_attachment && fsr_attachment->pFragmentShadingRateAttachment && |
| fsr_attachment->pFragmentShadingRateAttachment->attachment == i) { |
| used_as_fragment_shading_rate_attachment = true; |
| if ((mip_width * fsr_attachment->shadingRateAttachmentTexelSize.width) < pCreateInfo->width) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-04539", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u mip level " |
| "%u is used as a " |
| "fragment shading rate attachment in subpass %u, but the product of its " |
| "width (%u) and the " |
| "specified shading rate texel width (%u) are smaller than the " |
| "corresponding framebuffer width (%u).", |
| i, subresource_range.baseMipLevel, j, mip_width, |
| fsr_attachment->shadingRateAttachmentTexelSize.width, pCreateInfo->width); |
| } |
| if ((mip_height * fsr_attachment->shadingRateAttachmentTexelSize.height) < |
| pCreateInfo->height) { |
| skip |= |
| LogError(device, "VUID-VkFramebufferCreateInfo-flags-04540", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u mip level %u " |
| "is used as a " |
| "fragment shading rate attachment in subpass %u, but the product of its " |
| "height (%u) and the " |
| "specified shading rate texel height (%u) are smaller than the corresponding " |
| "framebuffer height (%u).", |
| i, subresource_range.baseMipLevel, j, mip_height, |
| fsr_attachment->shadingRateAttachmentTexelSize.height, pCreateInfo->height); |
| } |
| if (highest_view_bit >= 0) { |
| fsr_non_zero_viewmasks = true; |
| } |
| if (static_cast<int32_t>(subresource_range.layerCount) <= highest_view_bit) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-flags-04537", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has a layer count (%u) " |
| "less than or equal to the highest bit in the view mask (%i) of subpass %u.", |
| i, subresource_range.layerCount, highest_view_bit, j); |
| } |
| } |
| } |
| |
| if (enabled_features.fragment_density_map_features.fragmentDensityMap && |
| api_version >= VK_API_VERSION_1_1) { |
| const VkRenderPassFragmentDensityMapCreateInfoEXT *fdm_attachment; |
| fdm_attachment = LvlFindInChain<VkRenderPassFragmentDensityMapCreateInfoEXT>(rpci->pNext); |
| |
| if (fdm_attachment && fdm_attachment->fragmentDensityMapAttachment.attachment == i) { |
| int32_t layer_count = view_state->normalized_subresource_range.layerCount; |
| if (b_has_non_zero_view_masks && layer_count != 1 && layer_count <= highest_view_bit) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-renderPass-02746", |
| "vkCreateFrameBuffer(): VkFramebufferCreateInfo attachment #%" PRIu32 |
| " has a layer count (%" PRIi32 |
| ") different than 1 or lower than the most significant bit in viewMask (%i" |
| ") but renderPass (%s) was specified with non-zero view masks\n", |
| i, layer_count, highest_view_bit, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } |
| |
| if (!b_has_non_zero_view_masks && layer_count != 1) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-renderPass-02747", |
| "vkCreateFrameBuffer(): VkFramebufferCreateInfo attachment #%" PRIu32 |
| " had a layer count (%" PRIu32 |
| ") not equal to 1 but renderPass (%s) was not specified with non-zero view masks\n", |
| i, layer_count, report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } |
| } |
| } |
| } |
| |
| if (enabled_features.fragment_density_map_features.fragmentDensityMap) { |
| const VkRenderPassFragmentDensityMapCreateInfoEXT *fdm_attachment; |
| fdm_attachment = LvlFindInChain<VkRenderPassFragmentDensityMapCreateInfoEXT>(rpci->pNext); |
| if (fdm_attachment && fdm_attachment->fragmentDensityMapAttachment.attachment == i) { |
| uint32_t ceiling_width = static_cast<uint32_t>(ceil( |
| static_cast<float>(pCreateInfo->width) / |
| std::max(static_cast<float>( |
| phys_dev_ext_props.fragment_density_map_props.maxFragmentDensityTexelSize.width), |
| 1.0f))); |
| if (mip_width < ceiling_width) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-pAttachments-02555", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u mip level %u has width " |
| "smaller than the corresponding the ceiling of framebuffer width / " |
| "maxFragmentDensityTexelSize.width " |
| "Here are the respective dimensions for attachment #%u, the ceiling value:\n " |
| "attachment #%u, framebuffer:\n" |
| "width: %u, the ceiling value: %u\n", |
| i, subresource_range.baseMipLevel, i, i, mip_width, ceiling_width); |
| } |
| uint32_t ceiling_height = static_cast<uint32_t>(ceil( |
| static_cast<float>(pCreateInfo->height) / |
| std::max(static_cast<float>( |
| phys_dev_ext_props.fragment_density_map_props.maxFragmentDensityTexelSize.height), |
| 1.0f))); |
| if (mip_height < ceiling_height) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-pAttachments-02556", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u mip level %u has height " |
| "smaller than the corresponding the ceiling of framebuffer height / " |
| "maxFragmentDensityTexelSize.height " |
| "Here are the respective dimensions for attachment #%u, the ceiling value:\n " |
| "attachment #%u, framebuffer:\n" |
| "height: %u, the ceiling value: %u\n", |
| i, subresource_range.baseMipLevel, i, i, mip_height, ceiling_height); |
| } |
| if (view_state->normalized_subresource_range.layerCount != 1 && |
| !(api_version >= VK_API_VERSION_1_1 || IsExtEnabled(device_extensions.vk_khr_multiview))) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-pAttachments-02744", |
| "vkCreateFramebuffer(): pCreateInfo->pAttachments[%" PRIu32 |
| "] is referenced by " |
| "VkRenderPassFragmentDensityMapCreateInfoEXT::fragmentDensityMapAttachment in " |
| "the pNext chain, but it was create with subresourceRange.layerCount (%" PRIu32 |
| ") different from 1.", |
| i, view_state->normalized_subresource_range.layerCount); |
| } |
| } |
| } |
| |
| if (used_as_input_color_resolve_depth_stencil_attachment) { |
| if (mip_width < pCreateInfo->width) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-04533", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u mip level %u has " |
| "width (%u) smaller than the corresponding framebuffer width (%u).", |
| i, mip_level, mip_width, pCreateInfo->width); |
| } |
| if (mip_height < pCreateInfo->height) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-04534", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u mip level %u has " |
| "height (%u) smaller than the corresponding framebuffer height (%u).", |
| i, mip_level, mip_height, pCreateInfo->height); |
| } |
| uint32_t layerCount = view_state->GetAttachmentLayerCount(); |
| if (layerCount < pCreateInfo->layers) { |
| skip |= |
| LogError(device, "VUID-VkFramebufferCreateInfo-flags-04535", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has a layer count (%u) " |
| "smaller than the corresponding framebuffer layer count (%u).", |
| i, layerCount, pCreateInfo->layers); |
| } |
| } |
| |
| if (used_as_fragment_shading_rate_attachment && !fsr_non_zero_viewmasks) { |
| if (subresource_range.layerCount != 1 && subresource_range.layerCount < pCreateInfo->layers) { |
| skip |= |
| LogError(device, "VUID-VkFramebufferCreateInfo-flags-04538", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has a layer count (%u) " |
| "smaller than the corresponding framebuffer layer count (%u).", |
| i, subresource_range.layerCount, pCreateInfo->layers); |
| } |
| } |
| |
| if (IsIdentitySwizzle(ivci.components) == false) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-pAttachments-00884", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has non-identy swizzle. All " |
| "framebuffer attachments must have been created with the identity swizzle. Here are the actual " |
| "swizzle values:\n" |
| "r swizzle = %s\n" |
| "g swizzle = %s\n" |
| "b swizzle = %s\n" |
| "a swizzle = %s\n", |
| i, string_VkComponentSwizzle(ivci.components.r), string_VkComponentSwizzle(ivci.components.g), |
| string_VkComponentSwizzle(ivci.components.b), string_VkComponentSwizzle(ivci.components.a)); |
| } |
| if ((ivci.viewType == VK_IMAGE_VIEW_TYPE_2D) || (ivci.viewType == VK_IMAGE_VIEW_TYPE_2D)) { |
| auto image_state = Get<IMAGE_STATE>(ivci.image); |
| if (image_state->createInfo.imageType == VK_IMAGE_TYPE_3D) { |
| if (FormatIsDepthOrStencil(ivci.format)) { |
| LogObjectList objlist(device); |
| objlist.add(ivci.image); |
| skip |= LogError( |
| objlist, "VUID-VkFramebufferCreateInfo-pAttachments-00891", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has an image view type of " |
| "%s " |
| "which was taken from image %s of type VK_IMAGE_TYPE_3D, but the image view format is a " |
| "depth/stencil format %s", |
| i, string_VkImageViewType(ivci.viewType), report_data->FormatHandle(ivci.image).c_str(), |
| string_VkFormat(ivci.format)); |
| } |
| } |
| } |
| if (ivci.viewType == VK_IMAGE_VIEW_TYPE_3D) { |
| LogObjectList objlist(device); |
| objlist.add(image_views[i]); |
| skip |= LogError(objlist, "VUID-VkFramebufferCreateInfo-flags-04113", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has an image view type " |
| "of VK_IMAGE_VIEW_TYPE_3D", |
| i); |
| } |
| } |
| } |
| } else if (framebuffer_attachments_create_info) { |
| // VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT is set |
| for (uint32_t i = 0; i < pCreateInfo->attachmentCount; ++i) { |
| auto &aii = framebuffer_attachments_create_info->pAttachmentImageInfos[i]; |
| bool format_found = false; |
| for (uint32_t j = 0; j < aii.viewFormatCount; ++j) { |
| if (aii.pViewFormats[j] == rpci->pAttachments[i].format) { |
| format_found = true; |
| } |
| } |
| if (!format_found) { |
| skip |= LogError(pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-flags-03205", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info #%u does not include " |
| "format %s used " |
| "by the corresponding attachment for renderPass (%s).", |
| i, string_VkFormat(rpci->pAttachments[i].format), |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } |
| |
| bool used_as_input_color_resolve_depth_stencil_attachment = false; |
| bool used_as_fragment_shading_rate_attachment = false; |
| bool fsr_non_zero_viewmasks = false; |
| |
| for (uint32_t j = 0; j < rpci->subpassCount; ++j) { |
| const VkSubpassDescription2 &subpass = rpci->pSubpasses[j]; |
| |
| int highest_view_bit = MostSignificantBit(subpass.viewMask); |
| |
| for (uint32_t k = 0; k < rpci->pSubpasses[j].inputAttachmentCount; ++k) { |
| if (subpass.pInputAttachments[k].attachment == i) { |
| used_as_input_color_resolve_depth_stencil_attachment = true; |
| break; |
| } |
| } |
| |
| for (uint32_t k = 0; k < rpci->pSubpasses[j].colorAttachmentCount; ++k) { |
| if (subpass.pColorAttachments[k].attachment == i || |
| (subpass.pResolveAttachments && subpass.pResolveAttachments[k].attachment == i)) { |
| used_as_input_color_resolve_depth_stencil_attachment = true; |
| break; |
| } |
| } |
| |
| if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment == i) { |
| used_as_input_color_resolve_depth_stencil_attachment = true; |
| } |
| |
| if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate) { |
| const VkFragmentShadingRateAttachmentInfoKHR *fsr_attachment; |
| fsr_attachment = LvlFindInChain<VkFragmentShadingRateAttachmentInfoKHR>(subpass.pNext); |
| if (fsr_attachment && fsr_attachment->pFragmentShadingRateAttachment->attachment == i) { |
| used_as_fragment_shading_rate_attachment = true; |
| if ((aii.width * fsr_attachment->shadingRateAttachmentTexelSize.width) < pCreateInfo->width) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-flags-04543", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u is used as a " |
| "fragment shading rate attachment in subpass %u, but the product of its width (%u) and the " |
| "specified shading rate texel width (%u) are smaller than the corresponding framebuffer " |
| "width (%u).", |
| i, j, aii.width, fsr_attachment->shadingRateAttachmentTexelSize.width, pCreateInfo->width); |
| } |
| if ((aii.height * fsr_attachment->shadingRateAttachmentTexelSize.height) < pCreateInfo->height) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-04544", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u is used as a " |
| "fragment shading rate attachment in subpass %u, but the product of its " |
| "height (%u) and the " |
| "specified shading rate texel height (%u) are smaller than the corresponding " |
| "framebuffer height (%u).", |
| i, j, aii.height, fsr_attachment->shadingRateAttachmentTexelSize.height, |
| pCreateInfo->height); |
| } |
| if (highest_view_bit >= 0) { |
| fsr_non_zero_viewmasks = true; |
| } |
| if (aii.layerCount != 1 && static_cast<int32_t>(aii.layerCount) <= highest_view_bit) { |
| skip |= LogError( |
| device, kVUIDUndefined, |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has a layer count (%u) " |
| "less than or equal to the highest bit in the view mask (%i) of subpass %u.", |
| i, aii.layerCount, highest_view_bit, j); |
| } |
| } |
| } |
| } |
| |
| if (used_as_input_color_resolve_depth_stencil_attachment) { |
| if (aii.width < pCreateInfo->width) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-flags-04541", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info #%u has a width of only #%u, " |
| "but framebuffer has a width of #%u.", |
| i, aii.width, pCreateInfo->width); |
| } |
| |
| if (aii.height < pCreateInfo->height) { |
| skip |= LogError( |
| device, "VUID-VkFramebufferCreateInfo-flags-04542", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info #%u has a height of only #%u, " |
| "but framebuffer has a height of #%u.", |
| i, aii.height, pCreateInfo->height); |
| } |
| |
| const char *mismatched_layers_no_multiview_vuid = IsExtEnabled(device_extensions.vk_khr_multiview) |
| ? "VUID-VkFramebufferCreateInfo-renderPass-04546" |
| : "VUID-VkFramebufferCreateInfo-flags-04547"; |
| if ((rpci->subpassCount == 0) || (rpci->pSubpasses[0].viewMask == 0)) { |
| if (aii.layerCount < pCreateInfo->layers) { |
| skip |= LogError( |
| device, mismatched_layers_no_multiview_vuid, |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info #%u has only #%u layers, " |
| "but framebuffer has #%u layers.", |
| i, aii.layerCount, pCreateInfo->layers); |
| } |
| } |
| } |
| |
| if (used_as_fragment_shading_rate_attachment && !fsr_non_zero_viewmasks) { |
| if (aii.layerCount != 1 && aii.layerCount < pCreateInfo->layers) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-flags-04545", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment #%u has a layer count (%u) " |
| "smaller than the corresponding framebuffer layer count (%u).", |
| i, aii.layerCount, pCreateInfo->layers); |
| } |
| } |
| } |
| |
| // Validate image usage |
| uint32_t attachment_index = VK_ATTACHMENT_UNUSED; |
| for (uint32_t i = 0; i < rpci->subpassCount; ++i) { |
| skip |= MatchUsage(rpci->pSubpasses[i].colorAttachmentCount, rpci->pSubpasses[i].pColorAttachments, pCreateInfo, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-flags-03201"); |
| skip |= |
| MatchUsage(rpci->pSubpasses[i].colorAttachmentCount, rpci->pSubpasses[i].pResolveAttachments, pCreateInfo, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-flags-03201"); |
| skip |= MatchUsage(1, rpci->pSubpasses[i].pDepthStencilAttachment, pCreateInfo, |
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-flags-03202"); |
| skip |= MatchUsage(rpci->pSubpasses[i].inputAttachmentCount, rpci->pSubpasses[i].pInputAttachments, pCreateInfo, |
| VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-flags-03204"); |
| |
| const VkSubpassDescriptionDepthStencilResolve *depth_stencil_resolve = |
| LvlFindInChain<VkSubpassDescriptionDepthStencilResolve>(rpci->pSubpasses[i].pNext); |
| if (IsExtEnabled(device_extensions.vk_khr_depth_stencil_resolve) && depth_stencil_resolve != nullptr) { |
| skip |= MatchUsage(1, depth_stencil_resolve->pDepthStencilResolveAttachment, pCreateInfo, |
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-flags-03203"); |
| } |
| |
| const VkFragmentShadingRateAttachmentInfoKHR *fragment_shading_rate_attachment_info = |
| LvlFindInChain<VkFragmentShadingRateAttachmentInfoKHR>(rpci->pSubpasses[i].pNext); |
| if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate && |
| fragment_shading_rate_attachment_info != nullptr) { |
| skip |= MatchUsage(1, fragment_shading_rate_attachment_info->pFragmentShadingRateAttachment, pCreateInfo, |
| VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, |
| "VUID-VkFramebufferCreateInfo-flags-04549"); |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_multiview)) { |
| if ((rpci->subpassCount > 0) && (rpci->pSubpasses[0].viewMask != 0)) { |
| for (uint32_t i = 0; i < rpci->subpassCount; ++i) { |
| const VkSubpassDescriptionDepthStencilResolve *depth_stencil_resolve = |
| LvlFindInChain<VkSubpassDescriptionDepthStencilResolve>(rpci->pSubpasses[i].pNext); |
| uint32_t view_bits = rpci->pSubpasses[i].viewMask; |
| int highest_view_bit = MostSignificantBit(view_bits); |
| |
| for (uint32_t j = 0; j < rpci->pSubpasses[i].colorAttachmentCount; ++j) { |
| attachment_index = rpci->pSubpasses[i].pColorAttachments[j].attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| int32_t layer_count = |
| framebuffer_attachments_create_info->pAttachmentImageInfos[attachment_index].layerCount; |
| if (layer_count <= highest_view_bit) { |
| skip |= LogError( |
| pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-renderPass-03198", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info %u " |
| "only specifies %" PRIi32 " layers, but the view mask for subpass %u in renderPass (%s) " |
| "includes layer %i, with that attachment specified as a color attachment %u.", |
| attachment_index, layer_count, i, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str(), highest_view_bit, j); |
| } |
| } |
| if (rpci->pSubpasses[i].pResolveAttachments) { |
| attachment_index = rpci->pSubpasses[i].pResolveAttachments[j].attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| int32_t layer_count = |
| framebuffer_attachments_create_info->pAttachmentImageInfos[attachment_index].layerCount; |
| if (layer_count <= highest_view_bit) { |
| skip |= LogError( |
| pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-renderPass-03198", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info %u " |
| "only specifies %" PRIi32 " layers, but the view mask for subpass %u in renderPass (%s) " |
| "includes layer %i, with that attachment specified as a resolve attachment %u.", |
| attachment_index, layer_count, i, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str(), highest_view_bit, j); |
| } |
| } |
| } |
| } |
| |
| for (uint32_t j = 0; j < rpci->pSubpasses[i].inputAttachmentCount; ++j) { |
| attachment_index = rpci->pSubpasses[i].pInputAttachments[j].attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| int32_t layer_count = |
| framebuffer_attachments_create_info->pAttachmentImageInfos[attachment_index].layerCount; |
| if (layer_count <= highest_view_bit) { |
| skip |= LogError( |
| pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-renderPass-03198", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info %u " |
| "only specifies %" PRIi32 " layers, but the view mask for subpass %u in renderPass (%s) " |
| "includes layer %i, with that attachment specified as an input attachment %u.", |
| attachment_index, layer_count, i, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str(), highest_view_bit, j); |
| } |
| } |
| } |
| |
| if (rpci->pSubpasses[i].pDepthStencilAttachment != nullptr) { |
| attachment_index = rpci->pSubpasses[i].pDepthStencilAttachment->attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| int32_t layer_count = |
| framebuffer_attachments_create_info->pAttachmentImageInfos[attachment_index].layerCount; |
| if (layer_count <= highest_view_bit) { |
| skip |= LogError( |
| pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-renderPass-03198", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info %u " |
| "only specifies %" PRIi32 " layers, but the view mask for subpass %u in renderPass (%s) " |
| "includes layer %i, with that attachment specified as a depth/stencil attachment.", |
| attachment_index, layer_count, i, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str(), highest_view_bit); |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_depth_stencil_resolve) && |
| depth_stencil_resolve != nullptr && |
| depth_stencil_resolve->pDepthStencilResolveAttachment != nullptr) { |
| attachment_index = depth_stencil_resolve->pDepthStencilResolveAttachment->attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| int32_t layer_count = |
| framebuffer_attachments_create_info->pAttachmentImageInfos[attachment_index].layerCount; |
| if (layer_count <= highest_view_bit) { |
| skip |= LogError( |
| pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-renderPass-03198", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo attachment info %u " |
| "only specifies %" PRIi32 " layers, but the view mask for subpass %u in renderPass (%s) " |
| "includes layer %i, with that attachment specified as a depth/stencil resolve " |
| "attachment.", |
| attachment_index, layer_count, i, |
| report_data->FormatHandle(pCreateInfo->renderPass).c_str(), highest_view_bit); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if ((pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0) { |
| // Verify correct attachment usage flags |
| for (uint32_t subpass = 0; subpass < rpci->subpassCount; subpass++) { |
| const VkSubpassDescription2 &subpass_description = rpci->pSubpasses[subpass]; |
| // Verify input attachments: |
| skip |= MatchUsage(subpass_description.inputAttachmentCount, subpass_description.pInputAttachments, pCreateInfo, |
| VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-pAttachments-00879"); |
| // Verify color attachments: |
| skip |= MatchUsage(subpass_description.colorAttachmentCount, subpass_description.pColorAttachments, pCreateInfo, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-pAttachments-00877"); |
| // Verify depth/stencil attachments: |
| skip |= |
| MatchUsage(1, subpass_description.pDepthStencilAttachment, pCreateInfo, |
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, "VUID-VkFramebufferCreateInfo-pAttachments-02633"); |
| // Verify depth/stecnil resolve |
| if (IsExtEnabled(device_extensions.vk_khr_depth_stencil_resolve)) { |
| const VkSubpassDescriptionDepthStencilResolve *ds_resolve = |
| LvlFindInChain<VkSubpassDescriptionDepthStencilResolve>(subpass_description.pNext); |
| if (ds_resolve) { |
| skip |= MatchUsage(1, ds_resolve->pDepthStencilResolveAttachment, pCreateInfo, |
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, |
| "VUID-VkFramebufferCreateInfo-pAttachments-02634"); |
| } |
| } |
| |
| // Verify fragment shading rate attachments |
| if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate) { |
| const VkFragmentShadingRateAttachmentInfoKHR *fragment_shading_rate_attachment_info = |
| LvlFindInChain<VkFragmentShadingRateAttachmentInfoKHR>(subpass_description.pNext); |
| if (fragment_shading_rate_attachment_info) { |
| skip |= MatchUsage(1, fragment_shading_rate_attachment_info->pFragmentShadingRateAttachment, |
| pCreateInfo, VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, |
| "VUID-VkFramebufferCreateInfo-flags-04548"); |
| } |
| } |
| } |
| } |
| |
| |
| if (b_has_non_zero_view_masks && pCreateInfo->layers != 1) { |
| skip |= LogError(pCreateInfo->renderPass, "VUID-VkFramebufferCreateInfo-renderPass-02531", |
| "vkCreateFramebuffer(): VkFramebufferCreateInfo has #%u layers but " |
| "renderPass (%s) was specified with non-zero view masks\n", |
| pCreateInfo->layers, report_data->FormatHandle(pCreateInfo->renderPass).c_str()); |
| } |
| } |
| } |
| // Verify FB dimensions are within physical device limits |
| if (pCreateInfo->width > phys_dev_props.limits.maxFramebufferWidth) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-width-00886", |
| "vkCreateFramebuffer(): Requested VkFramebufferCreateInfo width exceeds physical device limits. Requested " |
| "width: %u, device max: %u\n", |
| pCreateInfo->width, phys_dev_props.limits.maxFramebufferWidth); |
| } |
| if (pCreateInfo->height > phys_dev_props.limits.maxFramebufferHeight) { |
| skip |= |
| LogError(device, "VUID-VkFramebufferCreateInfo-height-00888", |
| "vkCreateFramebuffer(): Requested VkFramebufferCreateInfo height exceeds physical device limits. Requested " |
| "height: %u, device max: %u\n", |
| pCreateInfo->height, phys_dev_props.limits.maxFramebufferHeight); |
| } |
| if (pCreateInfo->layers > phys_dev_props.limits.maxFramebufferLayers) { |
| skip |= |
| LogError(device, "VUID-VkFramebufferCreateInfo-layers-00890", |
| "vkCreateFramebuffer(): Requested VkFramebufferCreateInfo layers exceeds physical device limits. Requested " |
| "layers: %u, device max: %u\n", |
| pCreateInfo->layers, phys_dev_props.limits.maxFramebufferLayers); |
| } |
| // Verify FB dimensions are greater than zero |
| if (pCreateInfo->width <= 0) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-width-00885", |
| "vkCreateFramebuffer(): Requested VkFramebufferCreateInfo width must be greater than zero."); |
| } |
| if (pCreateInfo->height <= 0) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-height-00887", |
| "vkCreateFramebuffer(): Requested VkFramebufferCreateInfo height must be greater than zero."); |
| } |
| if (pCreateInfo->layers <= 0) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-layers-00889", |
| "vkCreateFramebuffer(): Requested VkFramebufferCreateInfo layers must be greater than zero."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer) const { |
| // TODO : Verify that renderPass FB is created with is compatible with FB |
| bool skip = false; |
| skip |= ValidateFramebufferCreateInfo(pCreateInfo); |
| return skip; |
| } |
| |
| static bool FindDependency(const uint32_t index, const uint32_t dependent, const std::vector<DAGNode> &subpass_to_node, |
| layer_data::unordered_set<uint32_t> &processed_nodes) { |
| // If we have already checked this node we have not found a dependency path so return false. |
| if (processed_nodes.count(index)) return false; |
| processed_nodes.insert(index); |
| const DAGNode &node = subpass_to_node[index]; |
| // Look for a dependency path. If one exists return true else recurse on the previous nodes. |
| if (std::find(node.prev.begin(), node.prev.end(), dependent) == node.prev.end()) { |
| for (auto elem : node.prev) { |
| if (FindDependency(elem, dependent, subpass_to_node, processed_nodes)) return true; |
| } |
| } else { |
| return true; |
| } |
| return false; |
| } |
| |
| bool CoreChecks::CheckDependencyExists(const VkRenderPass renderpass, const uint32_t subpass, const VkImageLayout layout, |
| const std::vector<SubpassLayout> &dependent_subpasses, |
| const std::vector<DAGNode> &subpass_to_node, bool &skip) const { |
| bool result = true; |
| bool b_image_layout_read_only = IsImageLayoutReadOnly(layout); |
| // Loop through all subpasses that share the same attachment and make sure a dependency exists |
| for (uint32_t k = 0; k < dependent_subpasses.size(); ++k) { |
| const SubpassLayout &sp = dependent_subpasses[k]; |
| if (subpass == sp.index) continue; |
| if (b_image_layout_read_only && IsImageLayoutReadOnly(sp.layout)) continue; |
| |
| const DAGNode &node = subpass_to_node[subpass]; |
| // Check for a specified dependency between the two nodes. If one exists we are done. |
| auto prev_elem = std::find(node.prev.begin(), node.prev.end(), sp.index); |
| auto next_elem = std::find(node.next.begin(), node.next.end(), sp.index); |
| if (prev_elem == node.prev.end() && next_elem == node.next.end()) { |
| // If no dependency exits an implicit dependency still might. If not, throw an error. |
| layer_data::unordered_set<uint32_t> processed_nodes; |
| if (!(FindDependency(subpass, sp.index, subpass_to_node, processed_nodes) || |
| FindDependency(sp.index, subpass, subpass_to_node, processed_nodes))) { |
| skip |= |
| LogError(renderpass, kVUID_Core_DrawState_InvalidRenderpass, |
| "A dependency between subpasses %d and %d must exist but one is not specified.", subpass, sp.index); |
| result = false; |
| } |
| } |
| } |
| return result; |
| } |
| |
| bool CoreChecks::CheckPreserved(const VkRenderPass renderpass, const VkRenderPassCreateInfo2 *pCreateInfo, const int index, |
| const uint32_t attachment, const std::vector<DAGNode> &subpass_to_node, int depth, |
| bool &skip) const { |
| const DAGNode &node = subpass_to_node[index]; |
| // If this node writes to the attachment return true as next nodes need to preserve the attachment. |
| const VkSubpassDescription2 &subpass = pCreateInfo->pSubpasses[index]; |
| for (uint32_t j = 0; j < subpass.colorAttachmentCount; ++j) { |
| if (attachment == subpass.pColorAttachments[j].attachment) return true; |
| } |
| for (uint32_t j = 0; j < subpass.inputAttachmentCount; ++j) { |
| if (attachment == subpass.pInputAttachments[j].attachment) return true; |
| } |
| if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| if (attachment == subpass.pDepthStencilAttachment->attachment) return true; |
| } |
| bool result = false; |
| // Loop through previous nodes and see if any of them write to the attachment. |
| for (auto elem : node.prev) { |
| result |= CheckPreserved(renderpass, pCreateInfo, elem, attachment, subpass_to_node, depth + 1, skip); |
| } |
| // If the attachment was written to by a previous node than this node needs to preserve it. |
| if (result && depth > 0) { |
| bool has_preserved = false; |
| for (uint32_t j = 0; j < subpass.preserveAttachmentCount; ++j) { |
| if (subpass.pPreserveAttachments[j] == attachment) { |
| has_preserved = true; |
| break; |
| } |
| } |
| if (!has_preserved) { |
| skip |= LogError(renderpass, kVUID_Core_DrawState_InvalidRenderpass, |
| "Attachment %d is used by a later subpass and must be preserved in subpass %d.", attachment, index); |
| } |
| } |
| return result; |
| } |
| |
| template <class T> |
| bool IsRangeOverlapping(T offset1, T size1, T offset2, T size2) { |
| return (((offset1 + size1) > offset2) && ((offset1 + size1) < (offset2 + size2))) || |
| ((offset1 > offset2) && (offset1 < (offset2 + size2))); |
| } |
| |
| bool IsRegionOverlapping(VkImageSubresourceRange range1, VkImageSubresourceRange range2) { |
| return (IsRangeOverlapping(range1.baseMipLevel, range1.levelCount, range2.baseMipLevel, range2.levelCount) && |
| IsRangeOverlapping(range1.baseArrayLayer, range1.layerCount, range2.baseArrayLayer, range2.layerCount)); |
| } |
| |
| bool CoreChecks::ValidateDependencies(FRAMEBUFFER_STATE const *framebuffer, RENDER_PASS_STATE const *renderPass) const { |
| bool skip = false; |
| auto const framebuffer_info = framebuffer->createInfo.ptr(); |
| auto const create_info = renderPass->createInfo.ptr(); |
| auto const &subpass_to_node = renderPass->subpass_to_node; |
| |
| struct Attachment { |
| std::vector<SubpassLayout> outputs; |
| std::vector<SubpassLayout> inputs; |
| std::vector<uint32_t> overlapping; |
| }; |
| |
| std::vector<Attachment> attachments(create_info->attachmentCount); |
| |
| if (!(framebuffer_info->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)) { |
| // Find overlapping attachments |
| for (uint32_t i = 0; i < create_info->attachmentCount; ++i) { |
| for (uint32_t j = i + 1; j < create_info->attachmentCount; ++j) { |
| VkImageView viewi = framebuffer_info->pAttachments[i]; |
| VkImageView viewj = framebuffer_info->pAttachments[j]; |
| if (viewi == viewj) { |
| attachments[i].overlapping.emplace_back(j); |
| attachments[j].overlapping.emplace_back(i); |
| continue; |
| } |
| if (i >= framebuffer->attachments_view_state.size() || j >= framebuffer->attachments_view_state.size()) { |
| continue; |
| } |
| auto *view_state_i = framebuffer->attachments_view_state[i].get(); |
| auto *view_state_j = framebuffer->attachments_view_state[j].get(); |
| if (!view_state_i || !view_state_j) { |
| continue; |
| } |
| auto view_ci_i = view_state_i->create_info; |
| auto view_ci_j = view_state_j->create_info; |
| if (view_ci_i.image == view_ci_j.image && |
| IsRegionOverlapping(view_ci_i.subresourceRange, view_ci_j.subresourceRange)) { |
| attachments[i].overlapping.emplace_back(j); |
| attachments[j].overlapping.emplace_back(i); |
| continue; |
| } |
| auto *image_data_i = view_state_i->image_state.get(); |
| auto *image_data_j = view_state_j->image_state.get(); |
| if (!image_data_i || !image_data_j) { |
| continue; |
| } |
| const auto *binding_i = image_data_i->Binding(); |
| const auto *binding_j = image_data_j->Binding(); |
| if (binding_i && binding_j && binding_i->mem_state == binding_j->mem_state && |
| IsRangeOverlapping(binding_i->offset, binding_i->size, binding_j->offset, binding_j->size)) { |
| attachments[i].overlapping.emplace_back(j); |
| attachments[j].overlapping.emplace_back(i); |
| } |
| } |
| } |
| } |
| // Find for each attachment the subpasses that use them. |
| layer_data::unordered_set<uint32_t> attachment_indices; |
| for (uint32_t i = 0; i < create_info->subpassCount; ++i) { |
| const VkSubpassDescription2 &subpass = create_info->pSubpasses[i]; |
| attachment_indices.clear(); |
| for (uint32_t j = 0; j < subpass.inputAttachmentCount; ++j) { |
| uint32_t attachment = subpass.pInputAttachments[j].attachment; |
| if (attachment == VK_ATTACHMENT_UNUSED) continue; |
| SubpassLayout sp = {i, subpass.pInputAttachments[j].layout}; |
| attachments[attachment].inputs.emplace_back(sp); |
| for (auto overlapping_attachment : attachments[attachment].overlapping) { |
| attachments[overlapping_attachment].inputs.emplace_back(sp); |
| } |
| } |
| for (uint32_t j = 0; j < subpass.colorAttachmentCount; ++j) { |
| uint32_t attachment = subpass.pColorAttachments[j].attachment; |
| if (attachment == VK_ATTACHMENT_UNUSED) continue; |
| SubpassLayout sp = {i, subpass.pColorAttachments[j].layout}; |
| attachments[attachment].outputs.emplace_back(sp); |
| for (auto overlapping_attachment : attachments[attachment].overlapping) { |
| attachments[overlapping_attachment].outputs.emplace_back(sp); |
| } |
| attachment_indices.insert(attachment); |
| } |
| if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| uint32_t attachment = subpass.pDepthStencilAttachment->attachment; |
| SubpassLayout sp = {i, subpass.pDepthStencilAttachment->layout}; |
| attachments[attachment].outputs.emplace_back(sp); |
| for (auto overlapping_attachment : attachments[attachment].overlapping) { |
| attachments[overlapping_attachment].outputs.emplace_back(sp); |
| } |
| |
| if (attachment_indices.count(attachment)) { |
| skip |= |
| LogError(renderPass->renderPass(), kVUID_Core_DrawState_InvalidRenderpass, |
| "Cannot use same attachment (%u) as both color and depth output in same subpass (%u).", attachment, i); |
| } |
| } |
| } |
| // If there is a dependency needed make sure one exists |
| for (uint32_t i = 0; i < create_info->subpassCount; ++i) { |
| const VkSubpassDescription2 &subpass = create_info->pSubpasses[i]; |
| // If the attachment is an input then all subpasses that output must have a dependency relationship |
| for (uint32_t j = 0; j < subpass.inputAttachmentCount; ++j) { |
| uint32_t attachment = subpass.pInputAttachments[j].attachment; |
| if (attachment == VK_ATTACHMENT_UNUSED) continue; |
| CheckDependencyExists(renderPass->renderPass(), i, subpass.pInputAttachments[j].layout, attachments[attachment].outputs, |
| subpass_to_node, skip); |
| } |
| // If the attachment is an output then all subpasses that use the attachment must have a dependency relationship |
| for (uint32_t j = 0; j < subpass.colorAttachmentCount; ++j) { |
| uint32_t attachment = subpass.pColorAttachments[j].attachment; |
| if (attachment == VK_ATTACHMENT_UNUSED) continue; |
| CheckDependencyExists(renderPass->renderPass(), i, subpass.pColorAttachments[j].layout, attachments[attachment].outputs, |
| subpass_to_node, skip); |
| CheckDependencyExists(renderPass->renderPass(), i, subpass.pColorAttachments[j].layout, attachments[attachment].inputs, |
| subpass_to_node, skip); |
| } |
| if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const uint32_t &attachment = subpass.pDepthStencilAttachment->attachment; |
| CheckDependencyExists(renderPass->renderPass(), i, subpass.pDepthStencilAttachment->layout, |
| attachments[attachment].outputs, subpass_to_node, skip); |
| CheckDependencyExists(renderPass->renderPass(), i, subpass.pDepthStencilAttachment->layout, |
| attachments[attachment].inputs, subpass_to_node, skip); |
| } |
| } |
| // Loop through implicit dependencies, if this pass reads make sure the attachment is preserved for all passes after it was |
| // written. |
| for (uint32_t i = 0; i < create_info->subpassCount; ++i) { |
| const VkSubpassDescription2 &subpass = create_info->pSubpasses[i]; |
| for (uint32_t j = 0; j < subpass.inputAttachmentCount; ++j) { |
| CheckPreserved(renderPass->renderPass(), create_info, i, subpass.pInputAttachments[j].attachment, subpass_to_node, 0, |
| skip); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateRenderPassDAG(RenderPassCreateVersion rp_version, const VkRenderPassCreateInfo2 *pCreateInfo) const { |
| bool skip = false; |
| const char *vuid; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| |
| for (uint32_t i = 0; i < pCreateInfo->dependencyCount; ++i) { |
| const VkSubpassDependency2 &dependency = pCreateInfo->pDependencies[i]; |
| auto latest_src_stage = sync_utils::GetLogicallyLatestGraphicsPipelineStage(dependency.srcStageMask); |
| auto earliest_dst_stage = sync_utils::GetLogicallyEarliestGraphicsPipelineStage(dependency.dstStageMask); |
| |
| // The first subpass here serves as a good proxy for "is multiview enabled" - since all view masks need to be non-zero if |
| // any are, which enables multiview. |
| if (use_rp2 && (dependency.dependencyFlags & VK_DEPENDENCY_VIEW_LOCAL_BIT) && (pCreateInfo->pSubpasses[0].viewMask == 0)) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo2-viewMask-03059", |
| "Dependency %u specifies the VK_DEPENDENCY_VIEW_LOCAL_BIT, but multiview is not enabled for this render pass.", i); |
| } else if (use_rp2 && !(dependency.dependencyFlags & VK_DEPENDENCY_VIEW_LOCAL_BIT) && dependency.viewOffset != 0) { |
| skip |= LogError(device, "VUID-VkSubpassDependency2-dependencyFlags-03092", |
| "Dependency %u specifies the VK_DEPENDENCY_VIEW_LOCAL_BIT, but also specifies a view offset of %u.", i, |
| dependency.viewOffset); |
| } else if (dependency.srcSubpass == VK_SUBPASS_EXTERNAL || dependency.dstSubpass == VK_SUBPASS_EXTERNAL) { |
| if (dependency.srcSubpass == dependency.dstSubpass) { |
| vuid = use_rp2 ? "VUID-VkSubpassDependency2-srcSubpass-03085" : "VUID-VkSubpassDependency-srcSubpass-00865"; |
| skip |= LogError(device, vuid, "The src and dst subpasses in dependency %u are both external.", i); |
| } else if (dependency.dependencyFlags & VK_DEPENDENCY_VIEW_LOCAL_BIT) { |
| if (dependency.srcSubpass == VK_SUBPASS_EXTERNAL) { |
| vuid = "VUID-VkSubpassDependency-dependencyFlags-02520"; |
| } else { // dependency.dstSubpass == VK_SUBPASS_EXTERNAL |
| vuid = "VUID-VkSubpassDependency-dependencyFlags-02521"; |
| } |
| if (use_rp2) { |
| // Create render pass 2 distinguishes between source and destination external dependencies. |
| if (dependency.srcSubpass == VK_SUBPASS_EXTERNAL) { |
| vuid = "VUID-VkSubpassDependency2-dependencyFlags-03090"; |
| } else { |
| vuid = "VUID-VkSubpassDependency2-dependencyFlags-03091"; |
| } |
| } |
| skip |= |
| LogError(device, vuid, |
| "Dependency %u specifies an external dependency but also specifies VK_DEPENDENCY_VIEW_LOCAL_BIT.", i); |
| } |
| } else if (dependency.srcSubpass > dependency.dstSubpass) { |
| vuid = use_rp2 ? "VUID-VkSubpassDependency2-srcSubpass-03084" : "VUID-VkSubpassDependency-srcSubpass-00864"; |
| skip |= LogError(device, vuid, |
| "Dependency %u specifies a dependency from a later subpass (%u) to an earlier subpass (%u), which is " |
| "disallowed to prevent cyclic dependencies.", |
| i, dependency.srcSubpass, dependency.dstSubpass); |
| } else if (dependency.srcSubpass == dependency.dstSubpass) { |
| if (dependency.viewOffset != 0) { |
| vuid = use_rp2 ? "VUID-VkSubpassDependency2-viewOffset-02530" : "VUID-VkRenderPassCreateInfo-pNext-01930"; |
| skip |= LogError(device, vuid, "Dependency %u specifies a self-dependency but has a non-zero view offset of %u", i, |
| dependency.viewOffset); |
| } else if ((dependency.dependencyFlags | VK_DEPENDENCY_VIEW_LOCAL_BIT) != dependency.dependencyFlags && |
| pCreateInfo->pSubpasses[dependency.srcSubpass].viewMask > 1) { |
| vuid = use_rp2 ? "VUID-VkRenderPassCreateInfo2-pDependencies-03060" : "VUID-VkSubpassDependency-srcSubpass-00872"; |
| skip |= LogError(device, vuid, |
| "Dependency %u specifies a self-dependency for subpass %u with a non-zero view mask, but does not " |
| "specify VK_DEPENDENCY_VIEW_LOCAL_BIT.", |
| i, dependency.srcSubpass); |
| } else if ((HasNonFramebufferStagePipelineStageFlags(dependency.srcStageMask) || |
| HasNonFramebufferStagePipelineStageFlags(dependency.dstStageMask)) && |
| (sync_utils::GetGraphicsPipelineStageLogicalOrdinal(latest_src_stage) > |
| sync_utils::GetGraphicsPipelineStageLogicalOrdinal(earliest_dst_stage))) { |
| vuid = use_rp2 ? "VUID-VkSubpassDependency2-srcSubpass-03087" : "VUID-VkSubpassDependency-srcSubpass-00867"; |
| skip |= LogError( |
| device, vuid, |
| "Dependency %u specifies a self-dependency from logically-later stage (%s) to a logically-earlier stage (%s).", |
| i, sync_utils::StringPipelineStageFlags(latest_src_stage).c_str(), |
| sync_utils::StringPipelineStageFlags(earliest_dst_stage).c_str()); |
| } else if ((HasNonFramebufferStagePipelineStageFlags(dependency.srcStageMask) == false) && |
| (HasNonFramebufferStagePipelineStageFlags(dependency.dstStageMask) == false) && |
| ((dependency.dependencyFlags & VK_DEPENDENCY_BY_REGION_BIT) == 0)) { |
| vuid = use_rp2 ? "VUID-VkSubpassDependency2-srcSubpass-02245" : "VUID-VkSubpassDependency-srcSubpass-02243"; |
| skip |= LogError(device, vuid, |
| "Dependency %u specifies a self-dependency for subpass %u with both stages including a " |
| "framebuffer-space stage, but does not specify VK_DEPENDENCY_BY_REGION_BIT in dependencyFlags.", |
| i, dependency.srcSubpass); |
| } |
| } else if ((dependency.srcSubpass < dependency.dstSubpass) && |
| ((pCreateInfo->pSubpasses[dependency.srcSubpass].flags & VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM) != 0)) { |
| vuid = use_rp2 ? "VUID-VkRenderPassCreateInfo2-flags-04909" : "VUID-VkSubpassDescription-flags-03343"; |
| skip |= LogError(device, vuid, |
| "Dependency %u specifies that subpass %u has a dependency on a later subpass" |
| "and includes VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM subpass flags.", |
| i, dependency.srcSubpass); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateAttachmentIndex(RenderPassCreateVersion rp_version, uint32_t attachment, uint32_t attachment_count, |
| const char *error_type, const char *function_name) const { |
| bool skip = false; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| assert(attachment != VK_ATTACHMENT_UNUSED); |
| if (attachment >= attachment_count) { |
| const char *vuid = |
| use_rp2 ? "VUID-VkRenderPassCreateInfo2-attachment-03051" : "VUID-VkRenderPassCreateInfo-attachment-00834"; |
| skip |= LogError(device, vuid, "%s: %s attachment %d must be less than the total number of attachments %d.", function_name, |
| error_type, attachment, attachment_count); |
| } |
| return skip; |
| } |
| |
| enum AttachmentType { |
| ATTACHMENT_COLOR = 1, |
| ATTACHMENT_DEPTH = 2, |
| ATTACHMENT_INPUT = 4, |
| ATTACHMENT_PRESERVE = 8, |
| ATTACHMENT_RESOLVE = 16, |
| }; |
| |
| char const *StringAttachmentType(uint8_t type) { |
| switch (type) { |
| case ATTACHMENT_COLOR: |
| return "color"; |
| case ATTACHMENT_DEPTH: |
| return "depth"; |
| case ATTACHMENT_INPUT: |
| return "input"; |
| case ATTACHMENT_PRESERVE: |
| return "preserve"; |
| case ATTACHMENT_RESOLVE: |
| return "resolve"; |
| default: |
| return "(multiple)"; |
| } |
| } |
| |
| bool CoreChecks::AddAttachmentUse(RenderPassCreateVersion rp_version, uint32_t subpass, std::vector<uint8_t> &attachment_uses, |
| std::vector<VkImageLayout> &attachment_layouts, uint32_t attachment, uint8_t new_use, |
| VkImageLayout new_layout) const { |
| if (attachment >= attachment_uses.size()) return false; /* out of range, but already reported */ |
| |
| bool skip = false; |
| auto &uses = attachment_uses[attachment]; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| const char *vuid; |
| const char *const function_name = use_rp2 ? "vkCreateRenderPass2()" : "vkCreateRenderPass()"; |
| |
| if (uses & new_use) { |
| if (attachment_layouts[attachment] != new_layout) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-layout-02528" : "VUID-VkSubpassDescription-layout-02519"; |
| skip |= LogError(device, vuid, "%s: subpass %u already uses attachment %u with a different image layout (%s vs %s).", |
| function_name, subpass, attachment, string_VkImageLayout(attachment_layouts[attachment]), |
| string_VkImageLayout(new_layout)); |
| } |
| } else if (((new_use & ATTACHMENT_COLOR) && (uses & ATTACHMENT_DEPTH)) || |
| ((uses & ATTACHMENT_COLOR) && (new_use & ATTACHMENT_DEPTH))) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pDepthStencilAttachment-04440" |
| : "VUID-VkSubpassDescription-pDepthStencilAttachment-04438"; |
| skip |= LogError(device, vuid, "%s: subpass %u uses attachment %u as both %s and %s attachment.", function_name, subpass, |
| attachment, StringAttachmentType(uses), StringAttachmentType(new_use)); |
| } else if ((uses && (new_use & ATTACHMENT_PRESERVE)) || (new_use && (uses & ATTACHMENT_PRESERVE))) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pPreserveAttachments-03074" |
| : "VUID-VkSubpassDescription-pPreserveAttachments-00854"; |
| skip |= LogError(device, vuid, "%s: subpass %u uses attachment %u as both %s and %s attachment.", function_name, subpass, |
| attachment, StringAttachmentType(uses), StringAttachmentType(new_use)); |
| } else { |
| attachment_layouts[attachment] = new_layout; |
| uses |= new_use; |
| } |
| |
| return skip; |
| } |
| |
| // Handles attachment references regardless of type (input, color, depth, etc) |
| // Input attachments have extra VUs associated with them |
| bool CoreChecks::ValidateAttachmentReference(RenderPassCreateVersion rp_version, VkAttachmentReference2 reference, |
| const VkFormat attachment_format, bool input, const char *error_type, |
| const char *function_name) const { |
| bool skip = false; |
| |
| // Currently all VUs require attachment to not be UNUSED |
| assert(reference.attachment != VK_ATTACHMENT_UNUSED); |
| |
| // currently VkAttachmentReference and VkAttachmentReference2 have no overlapping VUs |
| if (rp_version == RENDER_PASS_VERSION_1) { |
| switch (reference.layout) { |
| case VK_IMAGE_LAYOUT_UNDEFINED: |
| case VK_IMAGE_LAYOUT_PREINITIALIZED: |
| case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: |
| case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: |
| skip |= LogError(device, "VUID-VkAttachmentReference-layout-00857", |
| "%s: Layout for %s is %s but must not be " |
| "VK_IMAGE_LAYOUT_[UNDEFINED|PREINITIALIZED|PRESENT_SRC_KHR|DEPTH_ATTACHMENT_OPTIMAL|DEPTH_READ_" |
| "ONLY_OPTIMAL|STENCIL_ATTACHMENT_OPTIMAL|STENCIL_READ_ONLY_OPTIMAL].", |
| function_name, error_type, string_VkImageLayout(reference.layout)); |
| break; |
| default: |
| break; |
| } |
| } else { |
| const auto *attachment_reference_stencil_layout = LvlFindInChain<VkAttachmentReferenceStencilLayout>(reference.pNext); |
| switch (reference.layout) { |
| case VK_IMAGE_LAYOUT_UNDEFINED: |
| case VK_IMAGE_LAYOUT_PREINITIALIZED: |
| case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: |
| skip |= |
| LogError(device, "VUID-VkAttachmentReference2-layout-03077", |
| "%s: Layout for %s is %s but must not be VK_IMAGE_LAYOUT_[UNDEFINED|PREINITIALIZED|PRESENT_SRC_KHR].", |
| function_name, error_type, string_VkImageLayout(reference.layout)); |
| break; |
| |
| // Only other layouts in VUs to be checked |
| case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: |
| // First need to make sure feature bit is enabled and the format is actually a depth and/or stencil |
| if (!enabled_features.core12.separateDepthStencilLayouts) { |
| skip |= LogError(device, "VUID-VkAttachmentReference2-separateDepthStencilLayouts-03313", |
| "%s: Layout for %s is %s but without separateDepthStencilLayouts enabled the layout must not " |
| "be VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL, or VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL.", |
| function_name, error_type, string_VkImageLayout(reference.layout)); |
| } else if (!FormatIsDepthOrStencil(attachment_format)) { |
| // using this over FormatIsColor() incase a multiplane and/or undef would sneak in |
| // "color" format is still an ambiguous term in spec (internal issue #2484) |
| skip |= LogError( |
| device, "VUID-VkAttachmentReference2-attachment-04754", |
| "%s: Layout for %s is %s but the attachment is a not a depth/stencil format (%s) so the layout must not " |
| "be VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL, or VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL.", |
| function_name, error_type, string_VkImageLayout(reference.layout), string_VkFormat(attachment_format)); |
| } else { |
| if ((reference.layout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) || |
| (reference.layout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL)) { |
| if (FormatIsDepthOnly(attachment_format)) { |
| skip |= LogError( |
| device, "VUID-VkAttachmentReference2-attachment-04756", |
| "%s: Layout for %s is %s but the attachment is a depth-only format (%s) so the layout must not " |
| "be VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL.", |
| function_name, error_type, string_VkImageLayout(reference.layout), |
| string_VkFormat(attachment_format)); |
| } |
| } else { |
| // DEPTH_ATTACHMENT_OPTIMAL || DEPTH_READ_ONLY_OPTIMAL |
| if (FormatIsStencilOnly(attachment_format)) { |
| skip |= LogError( |
| device, "VUID-VkAttachmentReference2-attachment-04757", |
| "%s: Layout for %s is %s but the attachment is a depth-only format (%s) so the layout must not " |
| "be VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL.", |
| function_name, error_type, string_VkImageLayout(reference.layout), |
| string_VkFormat(attachment_format)); |
| } |
| |
| if (attachment_reference_stencil_layout) { |
| // This check doesn't rely on the aspect mask value |
| const VkImageLayout stencil_layout = attachment_reference_stencil_layout->stencilLayout; |
| // clang-format off |
| if (stencil_layout == VK_IMAGE_LAYOUT_UNDEFINED || |
| stencil_layout == VK_IMAGE_LAYOUT_PREINITIALIZED || |
| stencil_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL || |
| stencil_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { |
| skip |= LogError(device, "VUID-VkAttachmentReferenceStencilLayout-stencilLayout-03318", |
| "%s: In %s with pNext chain instance VkAttachmentReferenceStencilLayout, " |
| "the stencilLayout (%s) must not be " |
| "VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PREINITIALIZED, " |
| "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, or " |
| "VK_IMAGE_LAYOUT_PRESENT_SRC_KHR.", |
| function_name, error_type, string_VkImageLayout(stencil_layout)); |
| } |
| // clang-format on |
| } else if (FormatIsDepthAndStencil(attachment_format)) { |
| skip |= LogError( |
| device, "VUID-VkAttachmentReference2-attachment-04755", |
| "%s: Layout for %s is %s but the attachment is a depth and stencil format (%s) so if the layout is " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL there needs " |
| "to be a VkAttachmentReferenceStencilLayout in the pNext chain to set the seperate stencil layout " |
| "because the separateDepthStencilLayouts feature is enabled.", |
| function_name, error_type, string_VkImageLayout(reference.layout), |
| string_VkFormat(attachment_format)); |
| } |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateRenderpassAttachmentUsage(RenderPassCreateVersion rp_version, const VkRenderPassCreateInfo2 *pCreateInfo, |
| const char *function_name) const { |
| bool skip = false; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| const char *vuid; |
| |
| for (uint32_t i = 0; i < pCreateInfo->attachmentCount; ++i) { |
| VkFormat format = pCreateInfo->pAttachments[i].format; |
| if (pCreateInfo->pAttachments[i].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) { |
| if ((FormatIsColor(format) || FormatHasDepth(format)) && |
| pCreateInfo->pAttachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD) { |
| skip |= LogWarning(device, kVUID_Core_DrawState_InvalidRenderpass, |
| "%s: Render pass pAttachment[%u] has loadOp == VK_ATTACHMENT_LOAD_OP_LOAD and initialLayout == " |
| "VK_IMAGE_LAYOUT_UNDEFINED. This is probably not what you intended. Consider using " |
| "VK_ATTACHMENT_LOAD_OP_DONT_CARE instead if the image truely is undefined at the start of the " |
| "render pass.", |
| function_name, i); |
| } |
| if (FormatHasStencil(format) && pCreateInfo->pAttachments[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD) { |
| skip |= |
| LogWarning(device, kVUID_Core_DrawState_InvalidRenderpass, |
| "%s: Render pass pAttachment[%u] has stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD and initialLayout " |
| "== VK_IMAGE_LAYOUT_UNDEFINED. This is probably not what you intended. Consider using " |
| "VK_ATTACHMENT_LOAD_OP_DONT_CARE instead if the image truely is undefined at the start of the " |
| "render pass.", |
| function_name, i); |
| } |
| } |
| } |
| |
| // Track when we're observing the first use of an attachment |
| std::vector<bool> attach_first_use(pCreateInfo->attachmentCount, true); |
| |
| for (uint32_t i = 0; i < pCreateInfo->subpassCount; ++i) { |
| const VkSubpassDescription2 &subpass = pCreateInfo->pSubpasses[i]; |
| std::vector<uint8_t> attachment_uses(pCreateInfo->attachmentCount); |
| std::vector<VkImageLayout> attachment_layouts(pCreateInfo->attachmentCount); |
| |
| // Track if attachments are used as input as well as another type |
| layer_data::unordered_set<uint32_t> input_attachments; |
| |
| if (subpass.pipelineBindPoint != VK_PIPELINE_BIND_POINT_GRAPHICS && |
| subpass.pipelineBindPoint != VK_PIPELINE_BIND_POINT_SUBPASS_SHADING_HUAWEI) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pipelineBindPoint-04953" |
| : "VUID-VkSubpassDescription-pipelineBindPoint-04952"; |
| skip |= LogError(device, vuid, |
| "%s: Pipeline bind point for pSubpasses[%d] must be VK_PIPELINE_BIND_POINT_GRAPHICS or " |
| "VK_PIPELINE_BIND_POINT_SUBPASS_SHADING_HUAWEI.", |
| function_name, i); |
| } |
| |
| // Check input attachments first |
| // - so we can detect first-use-as-input for VU #00349 |
| // - if other color or depth/stencil is also input, it limits valid layouts |
| for (uint32_t j = 0; j < subpass.inputAttachmentCount; ++j) { |
| auto const &attachment_ref = subpass.pInputAttachments[j]; |
| const uint32_t attachment_index = attachment_ref.attachment; |
| const VkImageAspectFlags aspect_mask = attachment_ref.aspectMask; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| input_attachments.insert(attachment_index); |
| std::string error_type = "pSubpasses[" + std::to_string(i) + "].pInputAttachments[" + std::to_string(j) + "]"; |
| skip |= ValidateAttachmentIndex(rp_version, attachment_index, pCreateInfo->attachmentCount, error_type.c_str(), |
| function_name); |
| |
| if (aspect_mask & VK_IMAGE_ASPECT_METADATA_BIT) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-attachment-02801" |
| : "VUID-VkInputAttachmentAspectReference-aspectMask-01964"; |
| skip |= LogError( |
| device, vuid, |
| "%s: Aspect mask for input attachment reference %d in subpass %d includes VK_IMAGE_ASPECT_METADATA_BIT.", |
| function_name, j, i); |
| } else if (aspect_mask & (VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT | |
| VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT)) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-attachment-04563" |
| : "VUID-VkInputAttachmentAspectReference-aspectMask-02250"; |
| skip |= LogError(device, vuid, |
| "%s: Aspect mask for input attachment reference %d in subpass %d includes " |
| "VK_IMAGE_ASPECT_MEMORY_PLANE_*_BIT_EXT bit.", |
| function_name, j, i); |
| } |
| |
| // safe to dereference pCreateInfo->pAttachments[] |
| if (attachment_index < pCreateInfo->attachmentCount) { |
| const VkFormat attachment_format = pCreateInfo->pAttachments[attachment_index].format; |
| skip |= ValidateAttachmentReference(rp_version, attachment_ref, attachment_format, true, error_type.c_str(), |
| function_name); |
| |
| skip |= AddAttachmentUse(rp_version, i, attachment_uses, attachment_layouts, attachment_index, ATTACHMENT_INPUT, |
| attachment_ref.layout); |
| |
| vuid = use_rp2 ? "VUID-VkRenderPassCreateInfo2-attachment-02525" : "VUID-VkRenderPassCreateInfo-pNext-01963"; |
| skip |= ValidateImageAspectMask(VK_NULL_HANDLE, attachment_format, aspect_mask, function_name, vuid); |
| |
| if (attach_first_use[attachment_index]) { |
| skip |= |
| ValidateLayoutVsAttachmentDescription(report_data, rp_version, subpass.pInputAttachments[j].layout, |
| attachment_index, pCreateInfo->pAttachments[attachment_index]); |
| |
| bool used_as_depth = (subpass.pDepthStencilAttachment != NULL && |
| subpass.pDepthStencilAttachment->attachment == attachment_index); |
| bool used_as_color = false; |
| for (uint32_t k = 0; !used_as_depth && !used_as_color && k < subpass.colorAttachmentCount; ++k) { |
| used_as_color = (subpass.pColorAttachments[k].attachment == attachment_index); |
| } |
| if (!used_as_depth && !used_as_color && |
| pCreateInfo->pAttachments[attachment_index].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-loadOp-03064" : "VUID-VkSubpassDescription-loadOp-00846"; |
| skip |= LogError(device, vuid, |
| "%s: attachment %u is first used as an input attachment in %s with loadOp set to " |
| "VK_ATTACHMENT_LOAD_OP_CLEAR.", |
| function_name, attachment_index, error_type.c_str()); |
| } |
| } |
| attach_first_use[attachment_index] = false; |
| |
| const VkFormatFeatureFlags2KHR valid_flags = |
| VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR | VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR; |
| const VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(attachment_format); |
| if ((format_features & valid_flags) == 0) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pInputAttachments-02897" |
| : "VUID-VkSubpassDescription-pInputAttachments-02647"; |
| skip |= |
| LogError(device, vuid, |
| "%s: Input attachment %s format (%s) does not contain VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT " |
| "| VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| function_name, error_type.c_str(), string_VkFormat(attachment_format)); |
| } |
| } |
| |
| if (rp_version == RENDER_PASS_VERSION_2) { |
| // These are validated automatically as part of parameter validation for create renderpass 1 |
| // as they are in a struct that only applies to input attachments - not so for v2. |
| |
| // Check for 0 |
| if (aspect_mask == 0) { |
| skip |= LogError(device, "VUID-VkSubpassDescription2-attachment-02800", |
| "%s: Input attachment %s aspect mask must not be 0.", function_name, error_type.c_str()); |
| } else { |
| const VkImageAspectFlags valid_bits = |
| (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT | |
| VK_IMAGE_ASPECT_METADATA_BIT | VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | |
| VK_IMAGE_ASPECT_PLANE_2_BIT | VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT | |
| VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT | |
| VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT); |
| |
| // Check for valid aspect mask bits |
| if (aspect_mask & ~valid_bits) { |
| skip |= LogError(device, "VUID-VkSubpassDescription2-attachment-02799", |
| "%s: Input attachment %s aspect mask (0x%" PRIx32 ")is invalid.", function_name, |
| error_type.c_str(), aspect_mask); |
| } |
| } |
| } |
| |
| // Validate layout |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-None-04439" : "VUID-VkSubpassDescription-None-04437"; |
| switch (attachment_ref.layout) { |
| case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR: |
| case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_GENERAL: |
| case VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR: |
| case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: |
| break; // valid layouts |
| default: |
| skip |= LogError(device, vuid, |
| "%s: %s layout is %s but input attachments must be " |
| "VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, or " |
| "VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR", |
| function_name, error_type.c_str(), string_VkImageLayout(attachment_ref.layout)); |
| break; |
| } |
| } |
| } |
| |
| for (uint32_t j = 0; j < subpass.preserveAttachmentCount; ++j) { |
| std::string error_type = "pSubpasses[" + std::to_string(i) + "].pPreserveAttachments[" + std::to_string(j) + "]"; |
| uint32_t attachment = subpass.pPreserveAttachments[j]; |
| if (attachment == VK_ATTACHMENT_UNUSED) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-attachment-03073" : "VUID-VkSubpassDescription-attachment-00853"; |
| skip |= LogError(device, vuid, "%s: Preserve attachment (%d) must not be VK_ATTACHMENT_UNUSED.", function_name, j); |
| } else { |
| skip |= ValidateAttachmentIndex(rp_version, attachment, pCreateInfo->attachmentCount, error_type.c_str(), |
| function_name); |
| if (attachment < pCreateInfo->attachmentCount) { |
| skip |= AddAttachmentUse(rp_version, i, attachment_uses, attachment_layouts, attachment, ATTACHMENT_PRESERVE, |
| VkImageLayout(0) /* preserve doesn't have any layout */); |
| } |
| } |
| } |
| |
| bool subpass_performs_resolve = false; |
| |
| for (uint32_t j = 0; j < subpass.colorAttachmentCount; ++j) { |
| if (subpass.pResolveAttachments) { |
| std::string error_type = "pSubpasses[" + std::to_string(i) + "].pResolveAttachments[" + std::to_string(j) + "]"; |
| auto const &attachment_ref = subpass.pResolveAttachments[j]; |
| if (attachment_ref.attachment != VK_ATTACHMENT_UNUSED) { |
| skip |= ValidateAttachmentIndex(rp_version, attachment_ref.attachment, pCreateInfo->attachmentCount, |
| error_type.c_str(), function_name); |
| |
| // safe to dereference pCreateInfo->pAttachments[] |
| if (attachment_ref.attachment < pCreateInfo->attachmentCount) { |
| const VkFormat attachment_format = pCreateInfo->pAttachments[attachment_ref.attachment].format; |
| skip |= ValidateAttachmentReference(rp_version, attachment_ref, attachment_format, false, |
| error_type.c_str(), function_name); |
| skip |= AddAttachmentUse(rp_version, i, attachment_uses, attachment_layouts, attachment_ref.attachment, |
| ATTACHMENT_RESOLVE, attachment_ref.layout); |
| |
| subpass_performs_resolve = true; |
| |
| if (pCreateInfo->pAttachments[attachment_ref.attachment].samples != VK_SAMPLE_COUNT_1_BIT) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pResolveAttachments-03067" |
| : "VUID-VkSubpassDescription-pResolveAttachments-00849"; |
| skip |= LogError( |
| device, vuid, |
| "%s: Subpass %u requests multisample resolve into attachment %u, which must " |
| "have VK_SAMPLE_COUNT_1_BIT but has %s.", |
| function_name, i, attachment_ref.attachment, |
| string_VkSampleCountFlagBits(pCreateInfo->pAttachments[attachment_ref.attachment].samples)); |
| } |
| |
| const VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(attachment_format); |
| if ((format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR) == 0) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pResolveAttachments-02899" |
| : "VUID-VkSubpassDescription-pResolveAttachments-02649"; |
| skip |= LogError(device, vuid, |
| "%s: Resolve attachment %s format (%s) does not contain " |
| "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT.", |
| function_name, error_type.c_str(), string_VkFormat(attachment_format)); |
| } |
| |
| // VK_QCOM_render_pass_shader_resolve check of resolve attachmnents |
| if ((subpass.flags & VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM) != 0) { |
| vuid = use_rp2 ? "VUID-VkRenderPassCreateInfo2-flags-04907" : "VUID-VkSubpassDescription-flags-03341"; |
| skip |= LogError( |
| device, vuid, |
| "%s: Subpass %u enables shader resolve, which requires every element of pResolve attachments" |
| " must be VK_ATTACHMENT_UNUSED, but element %u contains a reference to attachment %u instead.", |
| function_name, i, j, attachment_ref.attachment); |
| } |
| } |
| } |
| } |
| } |
| |
| if (subpass.pDepthStencilAttachment) { |
| std::string error_type = "pSubpasses[" + std::to_string(i) + "].pDepthStencilAttachment"; |
| const uint32_t attachment = subpass.pDepthStencilAttachment->attachment; |
| const VkImageLayout image_layout = subpass.pDepthStencilAttachment->layout; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| skip |= ValidateAttachmentIndex(rp_version, attachment, pCreateInfo->attachmentCount, error_type.c_str(), |
| function_name); |
| |
| // safe to dereference pCreateInfo->pAttachments[] |
| if (attachment < pCreateInfo->attachmentCount) { |
| const VkFormat attachment_format = pCreateInfo->pAttachments[attachment].format; |
| skip |= ValidateAttachmentReference(rp_version, *subpass.pDepthStencilAttachment, attachment_format, false, |
| error_type.c_str(), function_name); |
| skip |= AddAttachmentUse(rp_version, i, attachment_uses, attachment_layouts, attachment, ATTACHMENT_DEPTH, |
| image_layout); |
| |
| if (attach_first_use[attachment]) { |
| skip |= ValidateLayoutVsAttachmentDescription(report_data, rp_version, image_layout, attachment, |
| pCreateInfo->pAttachments[attachment]); |
| } |
| attach_first_use[attachment] = false; |
| |
| const VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(attachment_format); |
| if ((format_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR) == 0) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pDepthStencilAttachment-02900" |
| : "VUID-VkSubpassDescription-pDepthStencilAttachment-02650"; |
| skip |= LogError(device, vuid, |
| "%s: Depth Stencil %s format (%s) does not contain " |
| "VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| function_name, error_type.c_str(), string_VkFormat(attachment_format)); |
| } |
| } |
| |
| // Check for valid imageLayout |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-None-04439" : "VUID-VkSubpassDescription-None-04437"; |
| switch (image_layout) { |
| case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR: |
| case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_GENERAL: |
| break; // valid layouts |
| case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: |
| case VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR: |
| case VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR: |
| if (input_attachments.find(attachment) != input_attachments.end()) { |
| skip |= LogError( |
| device, vuid, |
| "%s: %s is also an input attachment so the layout (%s) must not be " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR " |
| "or VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR.", |
| function_name, error_type.c_str(), string_VkImageLayout(image_layout)); |
| } |
| break; |
| default: |
| skip |= LogError(device, vuid, |
| "%s: %s layout is %s but depth/stencil attachments must be " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR, " |
| "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_GENERAL, " |
| "VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR or" |
| "VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR.", |
| function_name, error_type.c_str(), string_VkImageLayout(image_layout)); |
| break; |
| } |
| } |
| } |
| |
| uint32_t last_sample_count_attachment = VK_ATTACHMENT_UNUSED; |
| for (uint32_t j = 0; j < subpass.colorAttachmentCount; ++j) { |
| std::string error_type = "pSubpasses[" + std::to_string(i) + "].pColorAttachments[" + std::to_string(j) + "]"; |
| auto const &attachment_ref = subpass.pColorAttachments[j]; |
| const uint32_t attachment_index = attachment_ref.attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| skip |= ValidateAttachmentIndex(rp_version, attachment_index, pCreateInfo->attachmentCount, error_type.c_str(), |
| function_name); |
| |
| // safe to dereference pCreateInfo->pAttachments[] |
| if (attachment_index < pCreateInfo->attachmentCount) { |
| const VkFormat attachment_format = pCreateInfo->pAttachments[attachment_index].format; |
| skip |= ValidateAttachmentReference(rp_version, attachment_ref, attachment_format, false, error_type.c_str(), |
| function_name); |
| skip |= AddAttachmentUse(rp_version, i, attachment_uses, attachment_layouts, attachment_index, ATTACHMENT_COLOR, |
| attachment_ref.layout); |
| |
| VkSampleCountFlagBits current_sample_count = pCreateInfo->pAttachments[attachment_index].samples; |
| if (last_sample_count_attachment != VK_ATTACHMENT_UNUSED) { |
| VkSampleCountFlagBits last_sample_count = |
| pCreateInfo->pAttachments[subpass.pColorAttachments[last_sample_count_attachment].attachment].samples; |
| if (current_sample_count != last_sample_count) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pColorAttachments-03069" |
| : "VUID-VkSubpassDescription-pColorAttachments-01417"; |
| skip |= LogError( |
| device, vuid, |
| "%s: Subpass %u attempts to render to color attachments with inconsistent sample counts." |
| "Color attachment ref %u has sample count %s, whereas previous color attachment ref %u has " |
| "sample count %s.", |
| function_name, i, j, string_VkSampleCountFlagBits(current_sample_count), |
| last_sample_count_attachment, string_VkSampleCountFlagBits(last_sample_count)); |
| } |
| } |
| last_sample_count_attachment = j; |
| |
| if (subpass_performs_resolve && current_sample_count == VK_SAMPLE_COUNT_1_BIT) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pResolveAttachments-03066" |
| : "VUID-VkSubpassDescription-pResolveAttachments-00848"; |
| skip |= LogError(device, vuid, |
| "%s: Subpass %u requests multisample resolve from attachment %u which has " |
| "VK_SAMPLE_COUNT_1_BIT.", |
| function_name, i, attachment_index); |
| } |
| |
| if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED && |
| subpass.pDepthStencilAttachment->attachment < pCreateInfo->attachmentCount) { |
| const auto depth_stencil_sample_count = |
| pCreateInfo->pAttachments[subpass.pDepthStencilAttachment->attachment].samples; |
| |
| if (IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples)) { |
| if (current_sample_count > depth_stencil_sample_count) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pColorAttachments-03070" |
| : "VUID-VkSubpassDescription-pColorAttachments-01506"; |
| skip |= |
| LogError(device, vuid, "%s: %s has %s which is larger than depth/stencil attachment %s.", |
| function_name, error_type.c_str(), string_VkSampleCountFlagBits(current_sample_count), |
| string_VkSampleCountFlagBits(depth_stencil_sample_count)); |
| break; |
| } |
| } |
| |
| if (!IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples) && |
| !IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples) && |
| current_sample_count != depth_stencil_sample_count) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pDepthStencilAttachment-03071" |
| : "VUID-VkSubpassDescription-pDepthStencilAttachment-01418"; |
| skip |= LogError(device, vuid, |
| "%s: Subpass %u attempts to render to use a depth/stencil attachment with sample " |
| "count that differs " |
| "from color attachment %u." |
| "The depth attachment ref has sample count %s, whereas color attachment ref %u has " |
| "sample count %s.", |
| function_name, i, j, string_VkSampleCountFlagBits(depth_stencil_sample_count), j, |
| string_VkSampleCountFlagBits(current_sample_count)); |
| break; |
| } |
| } |
| |
| const VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(attachment_format); |
| if ((format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR) == 0) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pColorAttachments-02898" |
| : "VUID-VkSubpassDescription-pColorAttachments-02648"; |
| skip |= LogError(device, vuid, |
| "%s: Color attachment %s format (%s) does not contain " |
| "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT.", |
| function_name, error_type.c_str(), string_VkFormat(attachment_format)); |
| } |
| |
| if (attach_first_use[attachment_index]) { |
| skip |= |
| ValidateLayoutVsAttachmentDescription(report_data, rp_version, subpass.pColorAttachments[j].layout, |
| attachment_index, pCreateInfo->pAttachments[attachment_index]); |
| } |
| attach_first_use[attachment_index] = false; |
| } |
| |
| // Check for valid imageLayout |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-None-04439" : "VUID-VkSubpassDescription-None-04437"; |
| switch (attachment_ref.layout) { |
| case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR: |
| case VK_IMAGE_LAYOUT_GENERAL: |
| break; // valid layouts |
| case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: |
| case VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR: |
| if (input_attachments.find(attachment_index) != input_attachments.end()) { |
| skip |= LogError(device, vuid, |
| "%s: %s is also an input attachment so the layout (%s) must not be " |
| "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR.", |
| function_name, error_type.c_str(), string_VkImageLayout(attachment_ref.layout)); |
| } |
| break; |
| default: |
| skip |= LogError(device, vuid, |
| "%s: %s layout is %s but color attachments must be " |
| "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, " |
| "VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, " |
| "VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR or " |
| "VK_IMAGE_LAYOUT_GENERAL.", |
| function_name, error_type.c_str(), string_VkImageLayout(attachment_ref.layout)); |
| break; |
| } |
| } |
| |
| if (subpass_performs_resolve && subpass.pResolveAttachments[j].attachment != VK_ATTACHMENT_UNUSED && |
| subpass.pResolveAttachments[j].attachment < pCreateInfo->attachmentCount) { |
| if (attachment_index == VK_ATTACHMENT_UNUSED) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pResolveAttachments-03065" |
| : "VUID-VkSubpassDescription-pResolveAttachments-00847"; |
| skip |= LogError(device, vuid, |
| "%s: Subpass %u requests multisample resolve from attachment %u which has " |
| "attachment=VK_ATTACHMENT_UNUSED.", |
| function_name, i, attachment_index); |
| } else { |
| const auto &color_desc = pCreateInfo->pAttachments[attachment_index]; |
| const auto &resolve_desc = pCreateInfo->pAttachments[subpass.pResolveAttachments[j].attachment]; |
| if (color_desc.format != resolve_desc.format) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-pResolveAttachments-03068" |
| : "VUID-VkSubpassDescription-pResolveAttachments-00850"; |
| skip |= LogError(device, vuid, |
| "%s: %s resolves to an attachment with a " |
| "different format. color format: %u, resolve format: %u.", |
| function_name, error_type.c_str(), color_desc.format, resolve_desc.format); |
| } |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCreateRenderPass(VkDevice device, RenderPassCreateVersion rp_version, |
| const VkRenderPassCreateInfo2 *pCreateInfo, const char *function_name) const { |
| bool skip = false; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| const char *vuid; |
| |
| skip |= ValidateRenderpassAttachmentUsage(rp_version, pCreateInfo, function_name); |
| |
| skip |= ValidateRenderPassDAG(rp_version, pCreateInfo); |
| |
| // Validate multiview correlation and view masks |
| bool view_mask_zero = false; |
| bool view_mask_non_zero = false; |
| |
| for (uint32_t i = 0; i < pCreateInfo->subpassCount; ++i) { |
| const VkSubpassDescription2 &subpass = pCreateInfo->pSubpasses[i]; |
| if (subpass.viewMask != 0) { |
| view_mask_non_zero = true; |
| if (!enabled_features.multiview_features.multiview) { |
| skip |= LogError(device, "VUID-VkSubpassDescription2-multiview-06558", |
| "%s: pCreateInfo->pSubpasses[%" PRIu32 "].viewMask is %" PRIu32 |
| ", but multiview feature is not enabled.", |
| function_name, i, subpass.viewMask); |
| } |
| } else { |
| view_mask_zero = true; |
| } |
| |
| if ((subpass.flags & VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX) != 0 && |
| (subpass.flags & VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX) == 0) { |
| vuid = use_rp2 ? "VUID-VkSubpassDescription2-flags-03076" : "VUID-VkSubpassDescription-flags-00856"; |
| skip |= LogError(device, vuid, |
| "%s: The flags parameter of subpass description %u includes " |
| "VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX but does not also include " |
| "VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX.", |
| function_name, i); |
| } |
| } |
| |
| if (rp_version == RENDER_PASS_VERSION_2) { |
| if (view_mask_non_zero && view_mask_zero) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo2-viewMask-03058", |
| "%s: Some view masks are non-zero whilst others are zero.", function_name); |
| } |
| |
| if (view_mask_zero && pCreateInfo->correlatedViewMaskCount != 0) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo2-viewMask-03057", |
| "%s: Multiview is not enabled but correlation masks are still provided", function_name); |
| } |
| } |
| uint32_t aggregated_cvms = 0; |
| for (uint32_t i = 0; i < pCreateInfo->correlatedViewMaskCount; ++i) { |
| if (aggregated_cvms & pCreateInfo->pCorrelatedViewMasks[i]) { |
| vuid = use_rp2 ? "VUID-VkRenderPassCreateInfo2-pCorrelatedViewMasks-03056" |
| : "VUID-VkRenderPassMultiviewCreateInfo-pCorrelationMasks-00841"; |
| skip |= |
| LogError(device, vuid, "%s: pCorrelatedViewMasks[%u] contains a previously appearing view bit.", function_name, i); |
| } |
| aggregated_cvms |= pCreateInfo->pCorrelatedViewMasks[i]; |
| } |
| LogObjectList objects(device); |
| |
| auto func_name = use_rp2 ? Func::vkCreateRenderPass2 : Func::vkCreateRenderPass; |
| auto structure = use_rp2 ? Struct::VkSubpassDependency2 : Struct::VkSubpassDependency; |
| for (uint32_t i = 0; i < pCreateInfo->dependencyCount; ++i) { |
| auto const &dependency = pCreateInfo->pDependencies[i]; |
| Location loc(func_name, structure, Field::pDependencies, i); |
| skip |= ValidateSubpassDependency(objects, loc, dependency); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) const { |
| bool skip = false; |
| // Handle extension structs from KHR_multiview and KHR_maintenance2 that can only be validated for RP1 (indices out of bounds) |
| const VkRenderPassMultiviewCreateInfo *multiview_info = LvlFindInChain<VkRenderPassMultiviewCreateInfo>(pCreateInfo->pNext); |
| if (multiview_info) { |
| if (multiview_info->subpassCount && multiview_info->subpassCount != pCreateInfo->subpassCount) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo-pNext-01928", |
| "vkCreateRenderPass(): Subpass count is %u but multiview info has a subpass count of %u.", |
| pCreateInfo->subpassCount, multiview_info->subpassCount); |
| } else if (multiview_info->dependencyCount && multiview_info->dependencyCount != pCreateInfo->dependencyCount) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo-pNext-01929", |
| "vkCreateRenderPass(): Dependency count is %u but multiview info has a dependency count of %u.", |
| pCreateInfo->dependencyCount, multiview_info->dependencyCount); |
| } |
| bool all_zero = true; |
| bool all_not_zero = true; |
| for (uint32_t i = 0; i < multiview_info->subpassCount; ++i) { |
| all_zero &= multiview_info->pViewMasks[i] == 0; |
| all_not_zero &= !(multiview_info->pViewMasks[i] == 0); |
| } |
| if (!all_zero && !all_not_zero) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo-pNext-02513", |
| "vkCreateRenderPass(): elements of VkRenderPassMultiviewCreateInfo pViewMasks must all be either 0 or not 0."); |
| } |
| for (uint32_t i = 0; i < pCreateInfo->dependencyCount; ++i) { |
| if ((pCreateInfo->pDependencies[i].dependencyFlags & VK_DEPENDENCY_VIEW_LOCAL_BIT) == 0) { |
| if (i < multiview_info->dependencyCount && multiview_info->pViewOffsets[i] != 0) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo-pNext-02512", |
| "vkCreateRenderPass(): VkRenderPassCreateInfo::pDependencies[%" PRIu32 |
| "].dependencyFlags does not have VK_DEPENDENCY_VIEW_LOCAL_BIT bit set, but the corresponding " |
| "VkRenderPassMultiviewCreateInfo::pViewOffsets[%" PRIu32 "] is %" PRIi32 ".", |
| i, i, multiview_info->pViewOffsets[i]); |
| } |
| } |
| } |
| } |
| const VkRenderPassInputAttachmentAspectCreateInfo *input_attachment_aspect_info = |
| LvlFindInChain<VkRenderPassInputAttachmentAspectCreateInfo>(pCreateInfo->pNext); |
| if (input_attachment_aspect_info) { |
| for (uint32_t i = 0; i < input_attachment_aspect_info->aspectReferenceCount; ++i) { |
| uint32_t subpass = input_attachment_aspect_info->pAspectReferences[i].subpass; |
| uint32_t attachment = input_attachment_aspect_info->pAspectReferences[i].inputAttachmentIndex; |
| if (subpass >= pCreateInfo->subpassCount) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo-pNext-01926", |
| "vkCreateRenderPass(): Subpass index %u specified by input attachment aspect info %u is greater " |
| "than the subpass " |
| "count of %u for this render pass.", |
| subpass, i, pCreateInfo->subpassCount); |
| } else if (pCreateInfo->pSubpasses && attachment >= pCreateInfo->pSubpasses[subpass].inputAttachmentCount) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo-pNext-01927", |
| "vkCreateRenderPass(): Input attachment index %u specified by input attachment aspect info %u is " |
| "greater than the " |
| "input attachment count of %u for this subpass.", |
| attachment, i, pCreateInfo->pSubpasses[subpass].inputAttachmentCount); |
| } |
| } |
| } |
| |
| // TODO - VK_EXT_fragment_density_map should be moved into generic ValidateCreateRenderPass() and given RP2 VUIDs |
| const VkRenderPassFragmentDensityMapCreateInfoEXT *fragment_density_map_info = |
| LvlFindInChain<VkRenderPassFragmentDensityMapCreateInfoEXT>(pCreateInfo->pNext); |
| if (fragment_density_map_info) { |
| if (fragment_density_map_info->fragmentDensityMapAttachment.attachment != VK_ATTACHMENT_UNUSED) { |
| if (fragment_density_map_info->fragmentDensityMapAttachment.attachment >= pCreateInfo->attachmentCount) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo-fragmentDensityMapAttachment-06471", |
| "vkCreateRenderPass(): fragmentDensityMapAttachment %u must be less than attachmentCount %u of " |
| "for this render pass.", |
| fragment_density_map_info->fragmentDensityMapAttachment.attachment, pCreateInfo->attachmentCount); |
| } else { |
| if (!(fragment_density_map_info->fragmentDensityMapAttachment.layout == |
| VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT || |
| fragment_density_map_info->fragmentDensityMapAttachment.layout == VK_IMAGE_LAYOUT_GENERAL)) { |
| skip |= LogError(device, "VUID-VkRenderPassFragmentDensityMapCreateInfoEXT-fragmentDensityMapAttachment-02549", |
| "vkCreateRenderPass(): Layout of fragmentDensityMapAttachment %u' must be equal to " |
| "VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT, or VK_IMAGE_LAYOUT_GENERAL.", |
| fragment_density_map_info->fragmentDensityMapAttachment.attachment); |
| } |
| if (!(pCreateInfo->pAttachments[fragment_density_map_info->fragmentDensityMapAttachment.attachment].loadOp == |
| VK_ATTACHMENT_LOAD_OP_LOAD || |
| pCreateInfo->pAttachments[fragment_density_map_info->fragmentDensityMapAttachment.attachment].loadOp == |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE)) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassFragmentDensityMapCreateInfoEXT-fragmentDensityMapAttachment-02550", |
| "vkCreateRenderPass(): FragmentDensityMapAttachment %u' must reference an attachment with a loadOp " |
| "equal to VK_ATTACHMENT_LOAD_OP_LOAD or VK_ATTACHMENT_LOAD_OP_DONT_CARE.", |
| fragment_density_map_info->fragmentDensityMapAttachment.attachment); |
| } |
| if (pCreateInfo->pAttachments[fragment_density_map_info->fragmentDensityMapAttachment.attachment].storeOp != |
| VK_ATTACHMENT_STORE_OP_DONT_CARE) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassFragmentDensityMapCreateInfoEXT-fragmentDensityMapAttachment-02551", |
| "vkCreateRenderPass(): FragmentDensityMapAttachment %u' must reference an attachment with a storeOp " |
| "equal to VK_ATTACHMENT_STORE_OP_DONT_CARE.", |
| fragment_density_map_info->fragmentDensityMapAttachment.attachment); |
| } |
| } |
| } |
| } |
| |
| if (!skip) { |
| safe_VkRenderPassCreateInfo2 create_info_2; |
| ConvertVkRenderPassCreateInfoToV2KHR(*pCreateInfo, &create_info_2); |
| skip |= ValidateCreateRenderPass(device, RENDER_PASS_VERSION_1, create_info_2.ptr(), "vkCreateRenderPass()"); |
| } |
| |
| return skip; |
| } |
| |
| // VK_KHR_depth_stencil_resolve was added with a requirement on VK_KHR_create_renderpass2 so this will never be able to use |
| // VkRenderPassCreateInfo |
| bool CoreChecks::ValidateDepthStencilResolve(const VkRenderPassCreateInfo2 *pCreateInfo, const char *function_name) const { |
| bool skip = false; |
| |
| // If the pNext list of VkSubpassDescription2 includes a VkSubpassDescriptionDepthStencilResolve structure, |
| // then that structure describes depth/stencil resolve operations for the subpass. |
| for (uint32_t i = 0; i < pCreateInfo->subpassCount; i++) { |
| const VkSubpassDescription2 &subpass = pCreateInfo->pSubpasses[i]; |
| const auto *resolve = LvlFindInChain<VkSubpassDescriptionDepthStencilResolve>(subpass.pNext); |
| |
| // All of the VUs are wrapped in the wording: |
| // "If pDepthStencilResolveAttachment is not NULL" |
| if (resolve == nullptr || resolve->pDepthStencilResolveAttachment == nullptr) { |
| continue; |
| } |
| |
| // The spec says |
| // "If pDepthStencilAttachment is NULL, or if its attachment index is VK_ATTACHMENT_UNUSED, it indicates that no |
| // depth/stencil attachment will be used in the subpass." |
| if (subpass.pDepthStencilAttachment == nullptr) { |
| continue; |
| } else if (subpass.pDepthStencilAttachment->attachment == VK_ATTACHMENT_UNUSED) { |
| // while should be ignored, this is an explicit VU and some drivers will crash if this is let through |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03177", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with resolve attachment %" PRIu32 ", but pDepthStencilAttachment=VK_ATTACHMENT_UNUSED.", |
| function_name, i, resolve->pDepthStencilResolveAttachment->attachment); |
| continue; |
| } |
| |
| const uint32_t ds_attachment = subpass.pDepthStencilAttachment->attachment; |
| const uint32_t resolve_attachment = resolve->pDepthStencilResolveAttachment->attachment; |
| |
| // ValidateAttachmentIndex() should catch if this is invalid, but skip to avoid crashing |
| if (ds_attachment >= pCreateInfo->attachmentCount) { |
| continue; |
| } |
| |
| // All VUs in VkSubpassDescriptionDepthStencilResolve are wrapped with language saying it is not unused |
| if (resolve_attachment == VK_ATTACHMENT_UNUSED) { |
| continue; |
| } |
| |
| if (resolve_attachment >= pCreateInfo->attachmentCount) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo2-pSubpasses-06473", |
| "%s: pDepthStencilResolveAttachment %" PRIu32 " must be less than attachmentCount %" PRIu32 |
| " of for this render pass.", |
| function_name, resolve_attachment, pCreateInfo->attachmentCount); |
| // if the index is invalid need to skip everything else to prevent out of bounds index accesses crashing |
| continue; |
| } |
| |
| const VkFormat ds_attachment_format = pCreateInfo->pAttachments[ds_attachment].format; |
| const VkFormat resolve_attachment_format = pCreateInfo->pAttachments[resolve_attachment].format; |
| |
| // "depthResolveMode is ignored if the VkFormat of the pDepthStencilResolveAttachment does not have a depth component" |
| bool resolve_has_depth = FormatHasDepth(resolve_attachment_format); |
| // "stencilResolveMode is ignored if the VkFormat of the pDepthStencilResolveAttachment does not have a stencil component" |
| bool resolve_has_stencil = FormatHasStencil(resolve_attachment_format); |
| |
| if (resolve_has_depth) { |
| if (!(resolve->depthResolveMode == VK_RESOLVE_MODE_NONE || |
| resolve->depthResolveMode & phys_dev_props_core12.supportedDepthResolveModes)) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-depthResolveMode-03183", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with invalid depthResolveMode (%s), must be VK_RESOLVE_MODE_NONE or a value from " |
| "supportedDepthResolveModes (%s).", |
| function_name, i, string_VkResolveModeFlagBits(resolve->depthResolveMode), |
| string_VkResolveModeFlags(phys_dev_props_core12.supportedDepthResolveModes).c_str()); |
| } |
| } |
| |
| if (resolve_has_stencil) { |
| if (!(resolve->stencilResolveMode == VK_RESOLVE_MODE_NONE || |
| resolve->stencilResolveMode & phys_dev_props_core12.supportedStencilResolveModes)) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-stencilResolveMode-03184", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with invalid stencilResolveMode (%s), must be VK_RESOLVE_MODE_NONE or a value from " |
| "supportedStencilResolveModes (%s).", |
| function_name, i, string_VkResolveModeFlagBits(resolve->stencilResolveMode), |
| string_VkResolveModeFlags(phys_dev_props_core12.supportedStencilResolveModes).c_str()); |
| } |
| } |
| |
| if (resolve_has_depth && resolve_has_stencil) { |
| if (phys_dev_props_core12.independentResolve == VK_FALSE && phys_dev_props_core12.independentResolveNone == VK_FALSE && |
| !(resolve->depthResolveMode == resolve->stencilResolveMode)) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03185", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure. The values of depthResolveMode (%s) and stencilResolveMode (%s) must be identical.", |
| function_name, i, string_VkResolveModeFlagBits(resolve->depthResolveMode), |
| string_VkResolveModeFlagBits(resolve->stencilResolveMode)); |
| } |
| |
| if (phys_dev_props_core12.independentResolve == VK_FALSE && phys_dev_props_core12.independentResolveNone == VK_TRUE && |
| !(resolve->depthResolveMode == resolve->stencilResolveMode || resolve->depthResolveMode == VK_RESOLVE_MODE_NONE || |
| resolve->stencilResolveMode == VK_RESOLVE_MODE_NONE)) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03186", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure. The values of depthResolveMode (%s) and stencilResolveMode (%s) must be identical, or " |
| "one of them must be VK_RESOLVE_MODE_NONE.", |
| function_name, i, string_VkResolveModeFlagBits(resolve->depthResolveMode), |
| string_VkResolveModeFlagBits(resolve->stencilResolveMode)); |
| } |
| } |
| |
| // Same VU, but better error message if one of the resolves are ignored |
| if (resolve_has_depth && !resolve_has_stencil && resolve->depthResolveMode == VK_RESOLVE_MODE_NONE) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03178", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve structure with resolve " |
| "attachment %" PRIu32 |
| ", but the depth resolve mode is VK_RESOLVE_MODE_NONE (stencil resolve mode is " |
| "ignored due to format not having stencil component).", |
| function_name, i, resolve_attachment); |
| } else if (!resolve_has_depth && resolve_has_stencil && resolve->stencilResolveMode == VK_RESOLVE_MODE_NONE) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03178", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve structure with resolve " |
| "attachment %" PRIu32 |
| ", but the stencil resolve mode is VK_RESOLVE_MODE_NONE (depth resolve mode is " |
| "ignored due to format not having depth component).", |
| function_name, i, resolve_attachment); |
| } else if (resolve_has_depth && resolve_has_stencil && resolve->depthResolveMode == VK_RESOLVE_MODE_NONE && |
| resolve->stencilResolveMode == VK_RESOLVE_MODE_NONE) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03178", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve structure with resolve " |
| "attachment %" PRIu32 ", but both depth and stencil resolve modes are VK_RESOLVE_MODE_NONE.", |
| function_name, i, resolve_attachment); |
| } |
| |
| const uint32_t resolve_depth_size = FormatDepthSize(resolve_attachment_format); |
| const uint32_t resolve_stencil_size = FormatStencilSize(resolve_attachment_format); |
| |
| if (resolve_depth_size > 0 && |
| ((FormatDepthSize(ds_attachment_format) != resolve_depth_size) || |
| (FormatDepthNumericalType(ds_attachment_format) != FormatDepthNumericalType(ds_attachment_format)))) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03181", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with resolve attachment %" PRIu32 " which has a depth component (size %" PRIu32 |
| "). The depth component " |
| "of pDepthStencilAttachment must have the same number of bits (currently %" PRIu32 |
| ") and the same numerical type.", |
| function_name, i, resolve_attachment, resolve_depth_size, FormatDepthSize(ds_attachment_format)); |
| } |
| |
| if (resolve_stencil_size > 0 && |
| ((FormatStencilSize(ds_attachment_format) != resolve_stencil_size) || |
| (FormatStencilNumericalType(ds_attachment_format) != FormatStencilNumericalType(resolve_attachment_format)))) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03182", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with resolve attachment %" PRIu32 " which has a stencil component (size %" PRIu32 |
| "). The stencil component " |
| "of pDepthStencilAttachment must have the same number of bits (currently %" PRIu32 |
| ") and the same numerical type.", |
| function_name, i, resolve_attachment, resolve_stencil_size, FormatStencilSize(ds_attachment_format)); |
| } |
| |
| if (pCreateInfo->pAttachments[ds_attachment].samples == VK_SAMPLE_COUNT_1_BIT) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03179", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with resolve attachment %" PRIu32 |
| ". However pDepthStencilAttachment has sample count=VK_SAMPLE_COUNT_1_BIT.", |
| function_name, i, resolve_attachment); |
| } |
| |
| if (pCreateInfo->pAttachments[resolve_attachment].samples != VK_SAMPLE_COUNT_1_BIT) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-03180", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with resolve attachment %" PRIu32 " which has sample count=VK_SAMPLE_COUNT_1_BIT.", |
| function_name, i, resolve_attachment); |
| } |
| |
| const VkFormatFeatureFlags2KHR potential_format_features = GetPotentialFormatFeatures(resolve_attachment_format); |
| if ((potential_format_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR) == 0) { |
| skip |= LogError(device, "VUID-VkSubpassDescriptionDepthStencilResolve-pDepthStencilResolveAttachment-02651", |
| "%s: Subpass %" PRIu32 |
| " includes a VkSubpassDescriptionDepthStencilResolve " |
| "structure with resolve attachment %" PRIu32 |
| " with a format (%s) whose features do not contain VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| function_name, i, resolve_attachment, string_VkFormat(resolve_attachment_format)); |
| } |
| |
| // VK_QCOM_render_pass_shader_resolve check of depth/stencil attachmnent |
| if ((subpass.flags & VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM) != 0) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo2-flags-04908", |
| "%s: Subpass %" PRIu32 |
| " enables shader resolve, which requires the depth/stencil resolve attachment" |
| " must be VK_ATTACHMENT_UNUSED, but a reference to attachment %" PRIu32 " was found instead.", |
| function_name, i, resolve_attachment); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass, |
| const char *function_name) const { |
| bool skip = false; |
| |
| if (IsExtEnabled(device_extensions.vk_khr_depth_stencil_resolve)) { |
| skip |= ValidateDepthStencilResolve(pCreateInfo, function_name); |
| } |
| |
| skip |= ValidateFragmentShadingRateAttachments(device, pCreateInfo); |
| |
| safe_VkRenderPassCreateInfo2 create_info_2(pCreateInfo); |
| skip |= ValidateCreateRenderPass(device, RENDER_PASS_VERSION_2, create_info_2.ptr(), function_name); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateFragmentShadingRateAttachments(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo) const { |
| bool skip = false; |
| |
| if (enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate) { |
| for (uint32_t attachment_description = 0; attachment_description < pCreateInfo->attachmentCount; ++attachment_description) { |
| std::vector<uint32_t> used_as_fragment_shading_rate_attachment; |
| |
| // Prepass to find any use as a fragment shading rate attachment structures and validate them independently |
| for (uint32_t subpass = 0; subpass < pCreateInfo->subpassCount; ++subpass) { |
| const VkFragmentShadingRateAttachmentInfoKHR *fragment_shading_rate_attachment = |
| LvlFindInChain<VkFragmentShadingRateAttachmentInfoKHR>(pCreateInfo->pSubpasses[subpass].pNext); |
| |
| if (fragment_shading_rate_attachment && fragment_shading_rate_attachment->pFragmentShadingRateAttachment) { |
| const VkAttachmentReference2 &attachment_reference = |
| *(fragment_shading_rate_attachment->pFragmentShadingRateAttachment); |
| if (attachment_reference.attachment == attachment_description) { |
| used_as_fragment_shading_rate_attachment.push_back(subpass); |
| } |
| |
| if (((pCreateInfo->flags & VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM) != 0) && |
| (attachment_reference.attachment != VK_ATTACHMENT_UNUSED)) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo2-flags-04521", |
| "vkCreateRenderPass2: Render pass includes VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM but " |
| "a fragment shading rate attachment is specified in subpass %u.", |
| subpass); |
| } |
| |
| if (attachment_reference.attachment != VK_ATTACHMENT_UNUSED) { |
| const VkFormatFeatureFlags2KHR potential_format_features = |
| GetPotentialFormatFeatures(pCreateInfo->pAttachments[attachment_reference.attachment].format); |
| |
| if (!(potential_format_features & VK_FORMAT_FEATURE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) { |
| skip |= LogError(device, "VUID-VkRenderPassCreateInfo2-pAttachments-04586", |
| "vkCreateRenderPass2: Attachment description %u is used in subpass %u as a fragment " |
| "shading rate attachment, but specifies format %s, which does not support " |
| "VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR.", |
| attachment_reference.attachment, subpass, |
| string_VkFormat(pCreateInfo->pAttachments[attachment_reference.attachment].format)); |
| } |
| |
| if (attachment_reference.layout != VK_IMAGE_LAYOUT_GENERAL && |
| attachment_reference.layout != VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04524", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u specifies a layout of %s.", |
| subpass, string_VkImageLayout(attachment_reference.layout)); |
| } |
| |
| if (!IsPowerOfTwo(fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width)) { |
| skip |= |
| LogError(device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04525", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a " |
| "non-power-of-two texel width of %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width); |
| } |
| if (fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width < |
| phys_dev_ext_props.fragment_shading_rate_props.minFragmentShadingRateAttachmentTexelSize.width) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04526", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a texel width of %u which " |
| "is lower than the advertised minimum width %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width, |
| phys_dev_ext_props.fragment_shading_rate_props.minFragmentShadingRateAttachmentTexelSize.width); |
| } |
| if (fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width > |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSize.width) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04527", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a texel width of %u which " |
| "is higher than the advertised maximum width %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width, |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSize.width); |
| } |
| if (!IsPowerOfTwo(fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height)) { |
| skip |= |
| LogError(device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04528", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a " |
| "non-power-of-two texel height of %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height); |
| } |
| if (fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height < |
| phys_dev_ext_props.fragment_shading_rate_props.minFragmentShadingRateAttachmentTexelSize.height) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04529", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a texel height of %u " |
| "which is lower than the advertised minimum height %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height, |
| phys_dev_ext_props.fragment_shading_rate_props.minFragmentShadingRateAttachmentTexelSize.height); |
| } |
| if (fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height > |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSize.height) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04530", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a texel height of %u " |
| "which is higher than the advertised maximum height %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height, |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSize.height); |
| } |
| uint32_t aspect_ratio = fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width / |
| fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height; |
| uint32_t inverse_aspect_ratio = fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height / |
| fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width; |
| if (aspect_ratio > |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSizeAspectRatio) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04531", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a texel size of %u by %u, " |
| "which has an aspect ratio %u, which is higher than the advertised maximum aspect ratio %u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width, |
| fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height, aspect_ratio, |
| phys_dev_ext_props.fragment_shading_rate_props |
| .maxFragmentShadingRateAttachmentTexelSizeAspectRatio); |
| } |
| if (inverse_aspect_ratio > |
| phys_dev_ext_props.fragment_shading_rate_props.maxFragmentShadingRateAttachmentTexelSizeAspectRatio) { |
| skip |= LogError( |
| device, "VUID-VkFragmentShadingRateAttachmentInfoKHR-pFragmentShadingRateAttachment-04532", |
| "vkCreateRenderPass2: Fragment shading rate attachment in subpass %u has a texel size of %u by %u, " |
| "which has an inverse aspect ratio of %u, which is higher than the advertised maximum aspect ratio " |
| "%u.", |
| subpass, fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.width, |
| fragment_shading_rate_attachment->shadingRateAttachmentTexelSize.height, inverse_aspect_ratio, |
| phys_dev_ext_props.fragment_shading_rate_props |
| .maxFragmentShadingRateAttachmentTexelSizeAspectRatio); |
| } |
| } |
| } |
| } |
| |
| // Lambda function turning a vector of integers into a string |
| auto vector_to_string = [&](std::vector<uint32_t> vector) { |
| std::stringstream ss; |
| size_t size = vector.size(); |
| for (size_t i = 0; i < used_as_fragment_shading_rate_attachment.size(); i++) { |
| if (size == 2 && i == 1) { |
| ss << " and "; |
| } else if (size > 2 && i == size - 2) { |
| ss << ", and "; |
| } else if (i != 0) { |
| ss << ", "; |
| } |
| ss << vector[i]; |
| } |
| return ss.str(); |
| }; |
| |
| // Search for other uses of the same attachment |
| if (!used_as_fragment_shading_rate_attachment.empty()) { |
| for (uint32_t subpass = 0; subpass < pCreateInfo->subpassCount; ++subpass) { |
| const VkSubpassDescription2 &subpass_info = pCreateInfo->pSubpasses[subpass]; |
| const VkSubpassDescriptionDepthStencilResolve *depth_stencil_resolve_attachment = |
| LvlFindInChain<VkSubpassDescriptionDepthStencilResolve>(subpass_info.pNext); |
| |
| std::string fsr_attachment_subpasses_string = vector_to_string(used_as_fragment_shading_rate_attachment); |
| |
| for (uint32_t attachment = 0; attachment < subpass_info.colorAttachmentCount; ++attachment) { |
| if (subpass_info.pColorAttachments[attachment].attachment == attachment_description) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo2-pAttachments-04585", |
| "vkCreateRenderPass2: Attachment description %u is used as a fragment shading rate attachment in " |
| "subpass(es) %s but also as color attachment %u in subpass %u", |
| attachment_description, fsr_attachment_subpasses_string.c_str(), attachment, subpass); |
| } |
| } |
| for (uint32_t attachment = 0; attachment < subpass_info.colorAttachmentCount; ++attachment) { |
| if (subpass_info.pResolveAttachments && |
| subpass_info.pResolveAttachments[attachment].attachment == attachment_description) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo2-pAttachments-04585", |
| "vkCreateRenderPass2: Attachment description %u is used as a fragment shading rate attachment in " |
| "subpass(es) %s but also as color resolve attachment %u in subpass %u", |
| attachment_description, fsr_attachment_subpasses_string.c_str(), attachment, subpass); |
| } |
| } |
| for (uint32_t attachment = 0; attachment < subpass_info.inputAttachmentCount; ++attachment) { |
| if (subpass_info.pInputAttachments[attachment].attachment == attachment_description) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo2-pAttachments-04585", |
| "vkCreateRenderPass2: Attachment description %u is used as a fragment shading rate attachment in " |
| "subpass(es) %s but also as input attachment %u in subpass %u", |
| attachment_description, fsr_attachment_subpasses_string.c_str(), attachment, subpass); |
| } |
| } |
| if (subpass_info.pDepthStencilAttachment) { |
| if (subpass_info.pDepthStencilAttachment->attachment == attachment_description) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo2-pAttachments-04585", |
| "vkCreateRenderPass2: Attachment description %u is used as a fragment shading rate attachment in " |
| "subpass(es) %s but also as the depth/stencil attachment in subpass %u", |
| attachment_description, fsr_attachment_subpasses_string.c_str(), subpass); |
| } |
| } |
| if (depth_stencil_resolve_attachment && depth_stencil_resolve_attachment->pDepthStencilResolveAttachment) { |
| if (depth_stencil_resolve_attachment->pDepthStencilResolveAttachment->attachment == |
| attachment_description) { |
| skip |= LogError( |
| device, "VUID-VkRenderPassCreateInfo2-pAttachments-04585", |
| "vkCreateRenderPass2: Attachment description %u is used as a fragment shading rate attachment in " |
| "subpass(es) %s but also as the depth/stencil resolve attachment in subpass %u", |
| attachment_description, fsr_attachment_subpasses_string.c_str(), subpass); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) const { |
| return ValidateCreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass, "vkCreateRenderPass2KHR()"); |
| } |
| |
| bool CoreChecks::PreCallValidateCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) const { |
| return ValidateCreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass, "vkCreateRenderPass2()"); |
| } |
| |
| bool CoreChecks::ValidatePrimaryCommandBuffer(const CMD_BUFFER_STATE *pCB, char const *cmd_name, const char *error_code) const { |
| bool skip = false; |
| if (pCB->createInfo.level != VK_COMMAND_BUFFER_LEVEL_PRIMARY) { |
| skip |= LogError(pCB->commandBuffer(), error_code, "Cannot execute command %s on a secondary command buffer.", cmd_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::VerifyRenderAreaBounds(const VkRenderPassBeginInfo *pRenderPassBegin, const char *func_name) const { |
| bool skip = false; |
| |
| bool device_group = false; |
| uint32_t device_group_area_count = 0; |
| const VkDeviceGroupRenderPassBeginInfo *device_group_render_pass_begin_info = |
| LvlFindInChain<VkDeviceGroupRenderPassBeginInfo>(pRenderPassBegin->pNext); |
| if (IsExtEnabled(device_extensions.vk_khr_device_group)) { |
| device_group = true; |
| if (device_group_render_pass_begin_info) { |
| device_group_area_count = device_group_render_pass_begin_info->deviceRenderAreaCount; |
| } |
| } |
| auto framebuffer_state = Get<FRAMEBUFFER_STATE>(pRenderPassBegin->framebuffer); |
| const auto *framebuffer_info = &framebuffer_state->createInfo; |
| if (device_group && device_group_area_count > 0) { |
| for (uint32_t i = 0; i < device_group_render_pass_begin_info->deviceRenderAreaCount; ++i) { |
| const auto &deviceRenderArea = device_group_render_pass_begin_info->pDeviceRenderAreas[i]; |
| if (deviceRenderArea.offset.x < 0) { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkDeviceGroupRenderPassBeginInfo-offset-06166", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, " |
| "VkDeviceGroupRenderPassBeginInfo::pDeviceRenderAreas[%" PRIu32 "].offset.x is negative (%" PRIi32 |
| ").", |
| func_name, i, deviceRenderArea.offset.x); |
| } |
| if (deviceRenderArea.offset.y < 0) { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkDeviceGroupRenderPassBeginInfo-offset-06167", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, " |
| "VkDeviceGroupRenderPassBeginInfo::pDeviceRenderAreas[%" PRIu32 "].offset.y is negative (%" PRIi32 |
| ").", |
| func_name, i, deviceRenderArea.offset.y); |
| } |
| if ((deviceRenderArea.offset.x + deviceRenderArea.extent.width) > framebuffer_info->width) { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-pNext-02856", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, " |
| "VkDeviceGroupRenderPassBeginInfo::pDeviceRenderAreas[%" PRIu32 "] offset.x (%" PRIi32 |
| ") + extent.width (%" PRIi32 ") is greater than framebuffer width (%" PRIi32 ").", |
| func_name, i, deviceRenderArea.offset.x, deviceRenderArea.extent.width, framebuffer_info->width); |
| } |
| if ((deviceRenderArea.offset.y + deviceRenderArea.extent.height) > framebuffer_info->height) { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-pNext-02857", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, " |
| "VkDeviceGroupRenderPassBeginInfo::pDeviceRenderAreas[%" PRIu32 "] offset.y (%" PRIi32 |
| ") + extent.height (%" PRIi32 ") is greater than framebuffer height (%" PRIi32 ").", |
| func_name, i, deviceRenderArea.offset.y, deviceRenderArea.extent.height, framebuffer_info->height); |
| } |
| } |
| } else { |
| if (pRenderPassBegin->renderArea.offset.x < 0) { |
| if (device_group) { |
| skip |= |
| LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-pNext-02850", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer and pNext " |
| "of VkRenderPassBeginInfo does not contain VkDeviceGroupRenderPassBeginInfo or its " |
| "deviceRenderAreaCount is 0, renderArea.offset.x is negative (%" PRIi32 ") .", |
| func_name, pRenderPassBegin->renderArea.offset.x); |
| } else { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-renderArea-02846", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, " |
| "renderArea.offset.x is negative (%" PRIi32 ") .", |
| func_name, pRenderPassBegin->renderArea.offset.x); |
| } |
| } |
| if (pRenderPassBegin->renderArea.offset.y < 0) { |
| if (device_group) { |
| skip |= |
| LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-pNext-02851", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer and pNext " |
| "of VkRenderPassBeginInfo does not contain VkDeviceGroupRenderPassBeginInfo or its " |
| "deviceRenderAreaCount is 0, renderArea.offset.y is negative (%" PRIi32 ") .", |
| func_name, pRenderPassBegin->renderArea.offset.y); |
| } else { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-renderArea-02847", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, " |
| "renderArea.offset.y is negative (%" PRIi32 ") .", |
| func_name, pRenderPassBegin->renderArea.offset.y); |
| } |
| } |
| if ((pRenderPassBegin->renderArea.offset.x + pRenderPassBegin->renderArea.extent.width) > framebuffer_info->width) { |
| if (device_group) { |
| skip |= |
| LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-pNext-02852", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer and pNext " |
| "of VkRenderPassBeginInfo does not contain VkDeviceGroupRenderPassBeginInfo or its " |
| "deviceRenderAreaCount is 0, renderArea.offset.x (%" PRIi32 ") + renderArea.extent.width (%" PRIi32 |
| ") is greater than framebuffer width (%" PRIi32 ").", |
| func_name, pRenderPassBegin->renderArea.offset.x, pRenderPassBegin->renderArea.extent.width, |
| framebuffer_info->width); |
| } else { |
| skip |= LogError( |
| pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-renderArea-02848", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, renderArea.offset.x " |
| "(%" PRIi32 ") + renderArea.extent.width (%" PRIi32 ") is greater than framebuffer width (%" PRIi32 ").", |
| func_name, pRenderPassBegin->renderArea.offset.x, pRenderPassBegin->renderArea.extent.width, |
| framebuffer_info->width); |
| } |
| } |
| if ((pRenderPassBegin->renderArea.offset.y + pRenderPassBegin->renderArea.extent.height) > framebuffer_info->height) { |
| if (device_group) { |
| skip |= |
| LogError(pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-pNext-02853", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer and pNext " |
| "of VkRenderPassBeginInfo does not contain VkDeviceGroupRenderPassBeginInfo or its " |
| "deviceRenderAreaCount is 0, renderArea.offset.y (%" PRIi32 ") + renderArea.extent.height (%" PRIi32 |
| ") is greater than framebuffer height (%" PRIi32 ").", |
| func_name, pRenderPassBegin->renderArea.offset.y, pRenderPassBegin->renderArea.extent.height, |
| framebuffer_info->height); |
| } else { |
| skip |= LogError( |
| pRenderPassBegin->renderPass, "VUID-VkRenderPassBeginInfo-renderArea-02849", |
| "%s: Cannot execute a render pass with renderArea not within the bound of the framebuffer, renderArea.offset.y " |
| "(%" PRIi32 ") + renderArea.extent.height (%" PRIi32 ") is greater than framebuffer height (%" PRIi32 ").", |
| func_name, pRenderPassBegin->renderArea.offset.y, pRenderPassBegin->renderArea.extent.height, |
| framebuffer_info->height); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::VerifyFramebufferAndRenderPassImageViews(const VkRenderPassBeginInfo *pRenderPassBeginInfo, |
| const char *func_name) const { |
| bool skip = false; |
| const VkRenderPassAttachmentBeginInfo *render_pass_attachment_begin_info = |
| LvlFindInChain<VkRenderPassAttachmentBeginInfo>(pRenderPassBeginInfo->pNext); |
| |
| if (render_pass_attachment_begin_info && render_pass_attachment_begin_info->attachmentCount != 0) { |
| auto framebuffer_state = Get<FRAMEBUFFER_STATE>(pRenderPassBeginInfo->framebuffer); |
| const auto *framebuffer_create_info = &framebuffer_state->createInfo; |
| const VkFramebufferAttachmentsCreateInfo *framebuffer_attachments_create_info = |
| LvlFindInChain<VkFramebufferAttachmentsCreateInfo>(framebuffer_create_info->pNext); |
| if ((framebuffer_create_info->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03207", |
| "%s: Image views specified at render pass begin, but framebuffer not created with " |
| "VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT", |
| func_name); |
| } else if (framebuffer_attachments_create_info) { |
| if (framebuffer_attachments_create_info->attachmentImageInfoCount != |
| render_pass_attachment_begin_info->attachmentCount) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03208", |
| "%s: %u image views specified at render pass begin, but framebuffer " |
| "created expecting %u attachments", |
| func_name, render_pass_attachment_begin_info->attachmentCount, |
| framebuffer_attachments_create_info->attachmentImageInfoCount); |
| } else { |
| auto render_pass_state = Get<RENDER_PASS_STATE>(pRenderPassBeginInfo->renderPass); |
| const auto *render_pass_create_info = &render_pass_state->createInfo; |
| for (uint32_t i = 0; i < render_pass_attachment_begin_info->attachmentCount; ++i) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>(render_pass_attachment_begin_info->pAttachments[i]); |
| const VkImageViewCreateInfo *image_view_create_info = &image_view_state->create_info; |
| const auto &subresource_range = image_view_state->normalized_subresource_range; |
| const VkFramebufferAttachmentImageInfo *framebuffer_attachment_image_info = |
| &framebuffer_attachments_create_info->pAttachmentImageInfos[i]; |
| const auto *image_create_info = &image_view_state->image_state->createInfo; |
| |
| if (framebuffer_attachment_image_info->flags != image_create_info->flags) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03209", |
| "%s: Image view #%u created from an image with flags set as 0x%X, " |
| "but image info #%u used to create the framebuffer had flags set as 0x%X", |
| func_name, i, image_create_info->flags, i, framebuffer_attachment_image_info->flags); |
| } |
| |
| if (framebuffer_attachment_image_info->usage != image_view_state->inherited_usage) { |
| // Give clearer message if this error is due to the "inherited" part or not |
| if (image_create_info->usage == image_view_state->inherited_usage) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-04627", |
| "%s: Image view #%u created from an image with usage set as 0x%X, " |
| "but image info #%u used to create the framebuffer had usage set as 0x%X", |
| func_name, i, image_create_info->usage, i, framebuffer_attachment_image_info->usage); |
| } else { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-04627", |
| "%s: Image view #%u created from an image with usage set as 0x%X but using " |
| "VkImageViewUsageCreateInfo the inherited usage is the subset 0x%X " |
| "and the image info #%u used to create the framebuffer had usage set as 0x%X", |
| func_name, i, image_create_info->usage, image_view_state->inherited_usage, i, |
| framebuffer_attachment_image_info->usage); |
| } |
| } |
| |
| const auto view_width = max(1u, image_create_info->extent.width >> subresource_range.baseMipLevel); |
| if (framebuffer_attachment_image_info->width != view_width) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03211", |
| "%s: For VkRenderPassAttachmentBeginInfo::pAttachments[%" PRIu32 |
| "], VkImageView width (%" PRIu32 ") at mip level %" PRIu32 " (%" PRIu32 |
| ") != VkFramebufferAttachmentsCreateInfo::pAttachments[%" PRIu32 "]::width (%" PRIu32 ").", |
| func_name, i, image_create_info->extent.width, subresource_range.baseMipLevel, view_width, |
| i, framebuffer_attachment_image_info->width); |
| } |
| |
| const bool is_1d = (image_view_create_info->viewType == VK_IMAGE_VIEW_TYPE_1D) || |
| (image_view_create_info->viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY); |
| const auto view_height = (!is_1d) ? max(1u, image_create_info->extent.height >> subresource_range.baseMipLevel) |
| : image_create_info->extent.height; |
| if (framebuffer_attachment_image_info->height != view_height) { |
| skip |= |
| LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03212", |
| "%s: For VkRenderPassAttachmentBeginInfo::pAttachments[%" PRIu32 |
| "], VkImageView height (%" PRIu32 ") at mip level %" PRIu32 " (%" PRIu32 |
| ") != VkFramebufferAttachmentsCreateInfo::pAttachments[%" PRIu32 "]::height (%" PRIu32 ").", |
| func_name, i, image_create_info->extent.height, subresource_range.baseMipLevel, view_height, i, |
| framebuffer_attachment_image_info->height); |
| } |
| |
| if (framebuffer_attachment_image_info->layerCount != subresource_range.layerCount) { |
| skip |= |
| LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03213", |
| "%s: Image view #%u created with a subresource range with a layerCount of %u, " |
| "but image info #%u used to create the framebuffer had layerCount set as %u", |
| func_name, i, subresource_range.layerCount, i, framebuffer_attachment_image_info->layerCount); |
| } |
| |
| const VkImageFormatListCreateInfo *image_format_list_create_info = |
| LvlFindInChain<VkImageFormatListCreateInfo>(image_create_info->pNext); |
| if (image_format_list_create_info) { |
| if (image_format_list_create_info->viewFormatCount != framebuffer_attachment_image_info->viewFormatCount) { |
| skip |= LogError( |
| pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03214", |
| "VkRenderPassBeginInfo: Image view #%u created with an image with a viewFormatCount of %u, " |
| "but image info #%u used to create the framebuffer had viewFormatCount set as %u", |
| i, image_format_list_create_info->viewFormatCount, i, |
| framebuffer_attachment_image_info->viewFormatCount); |
| } |
| |
| for (uint32_t j = 0; j < image_format_list_create_info->viewFormatCount; ++j) { |
| bool format_found = false; |
| for (uint32_t k = 0; k < framebuffer_attachment_image_info->viewFormatCount; ++k) { |
| if (image_format_list_create_info->pViewFormats[j] == |
| framebuffer_attachment_image_info->pViewFormats[k]) { |
| format_found = true; |
| } |
| } |
| if (!format_found) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03215", |
| "VkRenderPassBeginInfo: Image view #%u created with an image including the format " |
| "%s in its view format list, " |
| "but image info #%u used to create the framebuffer does not include this format", |
| i, string_VkFormat(image_format_list_create_info->pViewFormats[j]), i); |
| } |
| } |
| } |
| |
| if (render_pass_create_info->pAttachments[i].format != image_view_create_info->format) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03216", |
| "%s: Image view #%u created with a format of %s, " |
| "but render pass attachment description #%u created with a format of %s", |
| func_name, i, string_VkFormat(image_view_create_info->format), i, |
| string_VkFormat(render_pass_create_info->pAttachments[i].format)); |
| } |
| |
| if (render_pass_create_info->pAttachments[i].samples != image_create_info->samples) { |
| skip |= LogError(pRenderPassBeginInfo->renderPass, "VUID-VkRenderPassBeginInfo-framebuffer-03217", |
| "%s: Image view #%u created with an image with %s samples, " |
| "but render pass attachment description #%u created with %s samples", |
| func_name, i, string_VkSampleCountFlagBits(image_create_info->samples), i, |
| string_VkSampleCountFlagBits(render_pass_create_info->pAttachments[i].samples)); |
| } |
| |
| if (subresource_range.levelCount != 1) { |
| skip |= LogError(render_pass_attachment_begin_info->pAttachments[i], |
| "VUID-VkRenderPassAttachmentBeginInfo-pAttachments-03218", |
| "%s: Image view #%u created with multiple (%u) mip levels.", func_name, i, |
| subresource_range.levelCount); |
| } |
| |
| if (IsIdentitySwizzle(image_view_create_info->components) == false) { |
| skip |= LogError( |
| render_pass_attachment_begin_info->pAttachments[i], |
| "VUID-VkRenderPassAttachmentBeginInfo-pAttachments-03219", |
| "%s: Image view #%u created with non-identity swizzle. All " |
| "framebuffer attachments must have been created with the identity swizzle. Here are the actual " |
| "swizzle values:\n" |
| "r swizzle = %s\n" |
| "g swizzle = %s\n" |
| "b swizzle = %s\n" |
| "a swizzle = %s\n", |
| func_name, i, string_VkComponentSwizzle(image_view_create_info->components.r), |
| string_VkComponentSwizzle(image_view_create_info->components.g), |
| string_VkComponentSwizzle(image_view_create_info->components.b), |
| string_VkComponentSwizzle(image_view_create_info->components.a)); |
| } |
| |
| if (image_view_create_info->viewType == VK_IMAGE_VIEW_TYPE_3D) { |
| skip |= LogError(render_pass_attachment_begin_info->pAttachments[i], |
| "VUID-VkRenderPassAttachmentBeginInfo-pAttachments-04114", |
| "%s: Image view #%u created with type VK_IMAGE_VIEW_TYPE_3D", func_name, i); |
| } |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| // If this is a stencil format, make sure the stencil[Load|Store]Op flag is checked, while if it is a depth/color attachment the |
| // [load|store]Op flag must be checked |
| // TODO: The memory valid flag in DEVICE_MEMORY_STATE should probably be split to track the validity of stencil memory separately. |
| template <typename T> |
| static bool FormatSpecificLoadAndStoreOpSettings(VkFormat format, T color_depth_op, T stencil_op, T op) { |
| if (color_depth_op != op && stencil_op != op) { |
| return false; |
| } |
| bool check_color_depth_load_op = !FormatIsStencilOnly(format); |
| bool check_stencil_load_op = FormatIsDepthAndStencil(format) || !check_color_depth_load_op; |
| |
| return ((check_color_depth_load_op && (color_depth_op == op)) || (check_stencil_load_op && (stencil_op == op))); |
| } |
| |
| bool CoreChecks::ValidateCmdBeginRenderPass(VkCommandBuffer commandBuffer, RenderPassCreateVersion rp_version, |
| const VkRenderPassBeginInfo *pRenderPassBegin, CMD_TYPE cmd_type) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| const char *function_name = CommandTypeString(cmd_type); |
| assert(cb_state); |
| if (pRenderPassBegin) { |
| auto rp_state = Get<RENDER_PASS_STATE>(pRenderPassBegin->renderPass); |
| auto fb_state = Get<FRAMEBUFFER_STATE>(pRenderPassBegin->framebuffer); |
| |
| if (rp_state) { |
| uint32_t clear_op_size = 0; // Make sure pClearValues is at least as large as last LOAD_OP_CLEAR |
| |
| // Handle extension struct from EXT_sample_locations |
| const VkRenderPassSampleLocationsBeginInfoEXT *sample_locations_begin_info = |
| LvlFindInChain<VkRenderPassSampleLocationsBeginInfoEXT>(pRenderPassBegin->pNext); |
| if (sample_locations_begin_info) { |
| for (uint32_t i = 0; i < sample_locations_begin_info->attachmentInitialSampleLocationsCount; ++i) { |
| const VkAttachmentSampleLocationsEXT &sample_location = |
| sample_locations_begin_info->pAttachmentInitialSampleLocations[i]; |
| skip |= ValidateSampleLocationsInfo(&sample_location.sampleLocationsInfo, function_name); |
| if (sample_location.attachmentIndex >= rp_state->createInfo.attachmentCount) { |
| skip |= LogError(device, "VUID-VkAttachmentSampleLocationsEXT-attachmentIndex-01531", |
| "%s: Attachment index %u specified by attachment sample locations %u is greater than the " |
| "attachment count of %u for the render pass being begun.", |
| function_name, sample_location.attachmentIndex, i, rp_state->createInfo.attachmentCount); |
| } |
| } |
| |
| for (uint32_t i = 0; i < sample_locations_begin_info->postSubpassSampleLocationsCount; ++i) { |
| const VkSubpassSampleLocationsEXT &sample_location = |
| sample_locations_begin_info->pPostSubpassSampleLocations[i]; |
| skip |= ValidateSampleLocationsInfo(&sample_location.sampleLocationsInfo, function_name); |
| if (sample_location.subpassIndex >= rp_state->createInfo.subpassCount) { |
| skip |= LogError( |
| device, "VUID-VkSubpassSampleLocationsEXT-subpassIndex-01532", |
| "%s: Subpass index %u specified by subpass sample locations %u is greater than the subpass count " |
| "of %u for the render pass being begun.", |
| function_name, sample_location.subpassIndex, i, rp_state->createInfo.subpassCount); |
| } |
| } |
| } |
| |
| for (uint32_t i = 0; i < rp_state->createInfo.attachmentCount; ++i) { |
| auto attachment = &rp_state->createInfo.pAttachments[i]; |
| if (FormatSpecificLoadAndStoreOpSettings(attachment->format, attachment->loadOp, attachment->stencilLoadOp, |
| VK_ATTACHMENT_LOAD_OP_CLEAR)) { |
| clear_op_size = static_cast<uint32_t>(i) + 1; |
| |
| if (FormatHasDepth(attachment->format) && pRenderPassBegin->pClearValues) { |
| skip |= ValidateClearDepthStencilValue(commandBuffer, pRenderPassBegin->pClearValues[i].depthStencil, |
| function_name); |
| } |
| } |
| } |
| |
| if (clear_op_size > pRenderPassBegin->clearValueCount) { |
| skip |= |
| LogError(rp_state->renderPass(), "VUID-VkRenderPassBeginInfo-clearValueCount-00902", |
| "In %s the VkRenderPassBeginInfo struct has a clearValueCount of %u but there " |
| "must be at least %u entries in pClearValues array to account for the highest index attachment in " |
| "%s that uses VK_ATTACHMENT_LOAD_OP_CLEAR is %u. Note that the pClearValues array is indexed by " |
| "attachment number so even if some pClearValues entries between 0 and %u correspond to attachments " |
| "that aren't cleared they will be ignored.", |
| function_name, pRenderPassBegin->clearValueCount, clear_op_size, |
| report_data->FormatHandle(rp_state->renderPass()).c_str(), clear_op_size, clear_op_size - 1); |
| } |
| skip |= VerifyFramebufferAndRenderPassImageViews(pRenderPassBegin, function_name); |
| skip |= VerifyRenderAreaBounds(pRenderPassBegin, function_name); |
| skip |= VerifyFramebufferAndRenderPassLayouts(rp_version, cb_state.get(), pRenderPassBegin, fb_state.get()); |
| if (fb_state->rp_state->renderPass() != rp_state->renderPass()) { |
| skip |= ValidateRenderPassCompatibility("render pass", rp_state.get(), "framebuffer", fb_state->rp_state.get(), |
| function_name, "VUID-VkRenderPassBeginInfo-renderPass-00904"); |
| } |
| |
| skip |= ValidateDependencies(fb_state.get(), rp_state.get()); |
| |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| } |
| } |
| |
| auto chained_device_group_struct = LvlFindInChain<VkDeviceGroupRenderPassBeginInfo>(pRenderPassBegin->pNext); |
| if (chained_device_group_struct) { |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(chained_device_group_struct->deviceMask, pRenderPassBegin->renderPass, |
| "VUID-VkDeviceGroupRenderPassBeginInfo-deviceMask-00905"); |
| skip |= ValidateDeviceMaskToZero(chained_device_group_struct->deviceMask, pRenderPassBegin->renderPass, |
| "VUID-VkDeviceGroupRenderPassBeginInfo-deviceMask-00906"); |
| skip |= |
| ValidateDeviceMaskToCommandBuffer(cb_state.get(), chained_device_group_struct->deviceMask, pRenderPassBegin->renderPass, |
| "VUID-VkDeviceGroupRenderPassBeginInfo-deviceMask-00907"); |
| |
| if (chained_device_group_struct->deviceRenderAreaCount != 0 && |
| chained_device_group_struct->deviceRenderAreaCount != physical_device_count) { |
| skip |= LogError(pRenderPassBegin->renderPass, "VUID-VkDeviceGroupRenderPassBeginInfo-deviceRenderAreaCount-00908", |
| "%s: deviceRenderAreaCount[%" PRIu32 "] is invaild. Physical device count is %" PRIu32 ".", |
| function_name, chained_device_group_struct->deviceRenderAreaCount, physical_device_count); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| VkSubpassContents contents) const { |
| bool skip = ValidateCmdBeginRenderPass(commandBuffer, RENDER_PASS_VERSION_1, pRenderPassBegin, CMD_BEGINRENDERPASS); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| const VkSubpassBeginInfo *pSubpassBeginInfo) const { |
| bool skip = ValidateCmdBeginRenderPass(commandBuffer, RENDER_PASS_VERSION_2, pRenderPassBegin, CMD_BEGINRENDERPASS2KHR); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| const VkSubpassBeginInfo *pSubpassBeginInfo) const { |
| bool skip = ValidateCmdBeginRenderPass(commandBuffer, RENDER_PASS_VERSION_2, pRenderPassBegin, CMD_BEGINRENDERPASS2); |
| return skip; |
| } |
| |
| void CoreChecks::RecordCmdBeginRenderPassLayouts(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| const VkSubpassContents contents) { |
| if (!pRenderPassBegin) { |
| return; |
| } |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto render_pass_state = Get<RENDER_PASS_STATE>(pRenderPassBegin->renderPass); |
| auto framebuffer = Get<FRAMEBUFFER_STATE>(pRenderPassBegin->framebuffer); |
| if (render_pass_state) { |
| // transition attachments to the correct layouts for beginning of renderPass and first subpass |
| TransitionBeginRenderPassLayouts(cb_state.get(), render_pass_state.get(), framebuffer.get()); |
| } |
| } |
| |
| void CoreChecks::PreCallRecordCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| VkSubpassContents contents) { |
| StateTracker::PreCallRecordCmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents); |
| RecordCmdBeginRenderPassLayouts(commandBuffer, pRenderPassBegin, contents); |
| } |
| |
| void CoreChecks::PreCallRecordCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| const VkSubpassBeginInfo *pSubpassBeginInfo) { |
| StateTracker::PreCallRecordCmdBeginRenderPass2KHR(commandBuffer, pRenderPassBegin, pSubpassBeginInfo); |
| RecordCmdBeginRenderPassLayouts(commandBuffer, pRenderPassBegin, pSubpassBeginInfo->contents); |
| } |
| |
| void CoreChecks::PreCallRecordCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, |
| const VkSubpassBeginInfo *pSubpassBeginInfo) { |
| StateTracker::PreCallRecordCmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo); |
| RecordCmdBeginRenderPassLayouts(commandBuffer, pRenderPassBegin, pSubpassBeginInfo->contents); |
| } |
| |
| bool CoreChecks::ValidateCmdNextSubpass(RenderPassCreateVersion rp_version, VkCommandBuffer commandBuffer, |
| CMD_TYPE cmd_type) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| const char *vuid; |
| const char *function_name = CommandTypeString(cmd_type); |
| |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| |
| auto subpass_count = cb_state->activeRenderPass->createInfo.subpassCount; |
| if (cb_state->activeSubpass == subpass_count - 1) { |
| vuid = use_rp2 ? "VUID-vkCmdNextSubpass2-None-03102" : "VUID-vkCmdNextSubpass-None-00909"; |
| skip |= LogError(commandBuffer, vuid, "%s: Attempted to advance beyond final subpass.", function_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents) const { |
| return ValidateCmdNextSubpass(RENDER_PASS_VERSION_1, commandBuffer, CMD_NEXTSUBPASS); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdNextSubpass2KHR(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo, |
| const VkSubpassEndInfo *pSubpassEndInfo) const { |
| return ValidateCmdNextSubpass(RENDER_PASS_VERSION_2, commandBuffer, CMD_NEXTSUBPASS2KHR); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo, |
| const VkSubpassEndInfo *pSubpassEndInfo) const { |
| return ValidateCmdNextSubpass(RENDER_PASS_VERSION_2, commandBuffer, CMD_NEXTSUBPASS2); |
| } |
| |
| void CoreChecks::RecordCmdNextSubpassLayouts(VkCommandBuffer commandBuffer, VkSubpassContents contents) { |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| auto framebuffer = Get<FRAMEBUFFER_STATE>(cb_state->activeRenderPassBeginInfo.framebuffer); |
| TransitionSubpassLayouts(cb_state.get(), cb_state->activeRenderPass.get(), cb_state->activeSubpass, framebuffer.get()); |
| } |
| |
| void CoreChecks::PostCallRecordCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents) { |
| StateTracker::PostCallRecordCmdNextSubpass(commandBuffer, contents); |
| RecordCmdNextSubpassLayouts(commandBuffer, contents); |
| } |
| |
| void CoreChecks::PostCallRecordCmdNextSubpass2KHR(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo, |
| const VkSubpassEndInfo *pSubpassEndInfo) { |
| StateTracker::PostCallRecordCmdNextSubpass2KHR(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo); |
| RecordCmdNextSubpassLayouts(commandBuffer, pSubpassBeginInfo->contents); |
| } |
| |
| void CoreChecks::PostCallRecordCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo *pSubpassBeginInfo, |
| const VkSubpassEndInfo *pSubpassEndInfo) { |
| StateTracker::PostCallRecordCmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo); |
| RecordCmdNextSubpassLayouts(commandBuffer, pSubpassBeginInfo->contents); |
| } |
| |
| bool CoreChecks::ValidateCmdEndRenderPass(RenderPassCreateVersion rp_version, VkCommandBuffer commandBuffer, CMD_TYPE cmd_type, |
| const VkSubpassEndInfo *pSubpassEndInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| const bool use_rp2 = (rp_version == RENDER_PASS_VERSION_2); |
| const char *vuid; |
| const char *function_name = CommandTypeString(cmd_type); |
| |
| RENDER_PASS_STATE *rp_state = cb_state->activeRenderPass.get(); |
| if (rp_state) { |
| const VkRenderPassCreateInfo2 *rpci = rp_state->createInfo.ptr(); |
| if ((cb_state->activeSubpass != rp_state->createInfo.subpassCount - 1) && !rp_state->use_dynamic_rendering) { |
| vuid = use_rp2 ? "VUID-vkCmdEndRenderPass2-None-03103" : "VUID-vkCmdEndRenderPass-None-00910"; |
| skip |= LogError(commandBuffer, vuid, "%s: Called before reaching final subpass.", function_name); |
| } |
| |
| if (rp_state->use_dynamic_rendering) { |
| vuid = use_rp2 ? "VUID-vkCmdEndRenderPass2-None-06171" : "VUID-vkCmdEndRenderPass-None-06170"; |
| skip |= LogError(commandBuffer, vuid, "%s: Called when the render pass instance was begun with %s().", function_name, |
| cb_state->begin_rendering_func_name.c_str()); |
| } |
| |
| if (pSubpassEndInfo && pSubpassEndInfo->pNext) { |
| const auto *fdm_offset_info = LvlFindInChain<VkSubpassFragmentDensityMapOffsetEndInfoQCOM>(pSubpassEndInfo->pNext); |
| if (fdm_offset_info != nullptr) { |
| if (fdm_offset_info->fragmentDensityOffsetCount != 0) { |
| if ((!enabled_features.fragment_density_map_offset_features.fragmentDensityMapOffset) || |
| (!enabled_features.fragment_density_map_features.fragmentDensityMap)) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-fragmentDensityMapOffsets-06503", |
| "%s(): fragmentDensityOffsetCount is %" PRIu32 " but must be 0 when feature is not enabled.", |
| function_name, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| |
| bool fdm_non_zero_offsets = false; |
| for (uint32_t k = 0; k < fdm_offset_info->fragmentDensityOffsetCount; k++) { |
| if ((fdm_offset_info->pFragmentDensityOffsets[k].x != 0) || |
| (fdm_offset_info->pFragmentDensityOffsets[k].y != 0)) { |
| fdm_non_zero_offsets = true; |
| uint32_t width = |
| phys_dev_ext_props.fragment_density_map_offset_props.fragmentDensityOffsetGranularity.width; |
| uint32_t height = |
| phys_dev_ext_props.fragment_density_map_offset_props.fragmentDensityOffsetGranularity.height; |
| |
| if (SafeModulo(fdm_offset_info->pFragmentDensityOffsets[k].x, width) != 0) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-x-06512", |
| "%s(): X component in fragmentDensityOffsets[%u] (%" PRIu32 |
| ") is" |
| " not an integer multiple of fragmentDensityOffsetGranularity.width (%" PRIu32 ").", |
| function_name, k, fdm_offset_info->pFragmentDensityOffsets[k].x, width); |
| } |
| |
| if (SafeModulo(fdm_offset_info->pFragmentDensityOffsets[k].y, height) != 0) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-y-06513", |
| "%s(): Y component in fragmentDensityOffsets[%u] (%" PRIu32 |
| ") is" |
| " not an integer multiple of fragmentDensityOffsetGranularity.height (%" PRIu32 ").", |
| function_name, k, fdm_offset_info->pFragmentDensityOffsets[k].y, height); |
| } |
| } |
| } |
| |
| const VkImageView *image_views = cb_state->activeFramebuffer.get()->createInfo.pAttachments; |
| for (uint32_t i = 0; i < rpci->attachmentCount; ++i) { |
| auto view_state = Get<IMAGE_VIEW_STATE>(image_views[i]); |
| const auto &ici = view_state->image_state->createInfo; |
| |
| if ((fdm_non_zero_offsets == true) && |
| ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0)) { |
| skip |= LogError(device, "VUID-VkFramebufferCreateInfo-renderPass-06502", |
| "%s(): Attachment #%" PRIu32 |
| " is not created with flag value" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and renderPass" |
| " uses non-zero fdm offsets.", |
| function_name, i); |
| } |
| |
| // fdm attachment |
| const auto *fdm_attachment = LvlFindInChain<VkRenderPassFragmentDensityMapCreateInfoEXT>(rpci->pNext); |
| const VkSubpassDescription2 &subpass = rpci->pSubpasses[cb_state->activeSubpass]; |
| if (fdm_attachment && fdm_attachment->fragmentDensityMapAttachment.attachment != VK_ATTACHMENT_UNUSED) { |
| if (fdm_attachment->fragmentDensityMapAttachment.attachment == i) { |
| if ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0) { |
| skip |= LogError( |
| device, |
| "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-fragmentDensityMapAttachment-06504", |
| "%s(): Fragment density map attachment #%" PRIu32 |
| " is not created with" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and fragmentDensityOffsetCount" |
| " is %" PRIu32 " but must be 0 due to missing fragmentDensityOffset feature bit.", |
| function_name, i, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| |
| if ((subpass.viewMask != 0) && (view_state->create_info.subresourceRange.layerCount != |
| fdm_offset_info->fragmentDensityOffsetCount)) { |
| skip |= LogError( |
| device, |
| "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-fragmentDensityOffsetCount-06510", |
| "%s(): fragmentDensityOffsetCount %" PRIu32 |
| " does not match the fragment density map attachment (%" PRIu32 |
| ") view layer count (%" PRIu32 ").", |
| function_name, fdm_offset_info->fragmentDensityOffsetCount, i, |
| view_state->create_info.subresourceRange.layerCount); |
| } |
| |
| if ((subpass.viewMask == 0) && (fdm_offset_info->fragmentDensityOffsetCount != 1)) { |
| skip |= LogError( |
| device, |
| "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-fragmentDensityOffsetCount-06511", |
| "%s(): fragmentDensityOffsetCount %" PRIu32 " should be 1 when multiview is not enabled.", |
| function_name, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| } |
| } |
| |
| // depth stencil attachment |
| if ((subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) && |
| (subpass.pDepthStencilAttachment->attachment == i) && |
| ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0)) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-pDepthStencilAttachment-06505", |
| "%s(): Depth/Stencil attachment #%" PRIu32 |
| " is not created with" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and fragmentDensityOffsetCount" |
| " is %" PRIu32 " but must be 0 due to missing fragmentDensityOffset feature bit.", |
| function_name, i, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| |
| // input attachments |
| for (uint32_t k = 0; k < subpass.inputAttachmentCount; k++) { |
| const auto attachment = subpass.pInputAttachments[k].attachment; |
| if ((attachment != VK_ATTACHMENT_UNUSED) && (attachment == i) && |
| ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0)) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-pInputAttachments-06506", |
| "%s(): Input attachment #%" PRIu32 |
| " is not created with" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and fragmentDensityOffsetCount" |
| " is %" PRIu32 " but must be 0 due to missing fragmentDensityOffset feature bit.", |
| function_name, i, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| } |
| |
| // color attachments |
| for (uint32_t k = 0; k < subpass.colorAttachmentCount; k++) { |
| const auto attachment = subpass.pColorAttachments[k].attachment; |
| if ((attachment != VK_ATTACHMENT_UNUSED) && (attachment == i) && |
| ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0)) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-pColorAttachments-06507", |
| "%s(): Color attachment #%" PRIu32 |
| " is not created with" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and fragmentDensityOffsetCount" |
| " is %" PRIu32 " but must be 0 due to missing fragmentDensityOffset feature bit.", |
| function_name, i, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| } |
| |
| // Resolve attachments |
| for (uint32_t k = 0; k < subpass.colorAttachmentCount; k++) { |
| const auto attachment = subpass.pResolveAttachments[k].attachment; |
| if ((attachment != VK_ATTACHMENT_UNUSED) && (attachment == i) && |
| ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0)) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-pResolveAttachments-06508", |
| "%s(): Resolve attachment #%" PRIu32 |
| " is not created with" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and fragmentDensityOffsetCount" |
| " is %" PRIu32 " but must be 0 due to missing fragmentDensityOffset feature bit.", |
| function_name, i, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| } |
| |
| // Preserve attachments |
| for (uint32_t k = 0; k < subpass.preserveAttachmentCount; k++) { |
| const auto attachment = subpass.pPreserveAttachments[k]; |
| if ((attachment != VK_ATTACHMENT_UNUSED) && (attachment == i) && |
| ((ici.flags & VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM) == 0)) { |
| skip |= |
| LogError(device, "VUID-VkSubpassFragmentDensityMapOffsetEndInfoQCOM-pPreserveAttachments-06509", |
| "%s(): Preserve attachment #%" PRIu32 |
| " is not created with" |
| " VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM and fragmentDensityOffsetCount" |
| " is %" PRIu32 " but must be 0 due to missing fragmentDensityOffset feature bit.", |
| function_name, i, fdm_offset_info->fragmentDensityOffsetCount); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndRenderPass(VkCommandBuffer commandBuffer) const { |
| bool skip = ValidateCmdEndRenderPass(RENDER_PASS_VERSION_1, commandBuffer, CMD_ENDRENDERPASS, VK_NULL_HANDLE); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndRenderPass2KHR(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo) const { |
| bool skip = ValidateCmdEndRenderPass(RENDER_PASS_VERSION_2, commandBuffer, CMD_ENDRENDERPASS2KHR, pSubpassEndInfo); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo) const { |
| bool skip = ValidateCmdEndRenderPass(RENDER_PASS_VERSION_2, commandBuffer, CMD_ENDRENDERPASS2, pSubpassEndInfo); |
| return skip; |
| } |
| |
| void CoreChecks::RecordCmdEndRenderPassLayouts(VkCommandBuffer commandBuffer) { |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| TransitionFinalSubpassLayouts(cb_state.get(), cb_state->activeRenderPassBeginInfo.ptr(), cb_state->activeFramebuffer.get()); |
| } |
| |
| void CoreChecks::PostCallRecordCmdEndRenderPass(VkCommandBuffer commandBuffer) { |
| // Record the end at the CoreLevel to ensure StateTracker cleanup doesn't step on anything we need. |
| RecordCmdEndRenderPassLayouts(commandBuffer); |
| StateTracker::PostCallRecordCmdEndRenderPass(commandBuffer); |
| } |
| |
| void CoreChecks::PostCallRecordCmdEndRenderPass2KHR(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo) { |
| // Record the end at the CoreLevel to ensure StateTracker cleanup doesn't step on anything we need. |
| RecordCmdEndRenderPassLayouts(commandBuffer); |
| StateTracker::PostCallRecordCmdEndRenderPass2KHR(commandBuffer, pSubpassEndInfo); |
| } |
| |
| void CoreChecks::PostCallRecordCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo *pSubpassEndInfo) { |
| RecordCmdEndRenderPassLayouts(commandBuffer); |
| StateTracker::PostCallRecordCmdEndRenderPass2(commandBuffer, pSubpassEndInfo); |
| } |
| |
| bool CoreChecks::ValidateFramebuffer(VkCommandBuffer primaryBuffer, const CMD_BUFFER_STATE *pCB, VkCommandBuffer secondaryBuffer, |
| const CMD_BUFFER_STATE *pSubCB, const char *caller) const { |
| bool skip = false; |
| if (!pSubCB->beginInfo.pInheritanceInfo) { |
| return skip; |
| } |
| VkFramebuffer primary_fb = pCB->activeFramebuffer ? pCB->activeFramebuffer->framebuffer() : VK_NULL_HANDLE; |
| VkFramebuffer secondary_fb = pSubCB->beginInfo.pInheritanceInfo->framebuffer; |
| if (secondary_fb != VK_NULL_HANDLE) { |
| if (primary_fb != secondary_fb) { |
| LogObjectList objlist(primaryBuffer); |
| objlist.add(secondaryBuffer); |
| objlist.add(secondary_fb); |
| objlist.add(primary_fb); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-pCommandBuffers-00099", |
| "vkCmdExecuteCommands() called w/ invalid secondary %s which has a %s" |
| " that is not the same as the primary command buffer's current active %s.", |
| report_data->FormatHandle(secondaryBuffer).c_str(), report_data->FormatHandle(secondary_fb).c_str(), |
| report_data->FormatHandle(primary_fb).c_str()); |
| } |
| auto fb = Get<FRAMEBUFFER_STATE>(secondary_fb); |
| if (!fb) { |
| LogObjectList objlist(primaryBuffer); |
| objlist.add(secondaryBuffer); |
| objlist.add(secondary_fb); |
| skip |= LogError(objlist, kVUID_Core_DrawState_InvalidSecondaryCommandBuffer, |
| "vkCmdExecuteCommands() called w/ invalid %s which has invalid %s.", |
| report_data->FormatHandle(secondaryBuffer).c_str(), report_data->FormatHandle(secondary_fb).c_str()); |
| return skip; |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateSecondaryCommandBufferState(const CMD_BUFFER_STATE *pCB, const CMD_BUFFER_STATE *pSubCB) const { |
| bool skip = false; |
| layer_data::unordered_set<int> active_types; |
| if (!disabled[query_validation]) { |
| for (const auto &query_object : pCB->activeQueries) { |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query_object.pool); |
| if (query_pool_state) { |
| if (query_pool_state->createInfo.queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS && |
| pSubCB->beginInfo.pInheritanceInfo) { |
| VkQueryPipelineStatisticFlags cmd_buf_statistics = pSubCB->beginInfo.pInheritanceInfo->pipelineStatistics; |
| if ((cmd_buf_statistics & query_pool_state->createInfo.pipelineStatistics) != cmd_buf_statistics) { |
| LogObjectList objlist(pCB->commandBuffer()); |
| objlist.add(query_object.pool); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-commandBuffer-00104", |
| "vkCmdExecuteCommands() called w/ invalid %s which has invalid active %s" |
| ". Pipeline statistics is being queried so the command buffer must have all bits set on the queryPool.", |
| report_data->FormatHandle(pCB->commandBuffer()).c_str(), |
| report_data->FormatHandle(query_object.pool).c_str()); |
| } |
| } |
| active_types.insert(query_pool_state->createInfo.queryType); |
| } |
| } |
| for (const auto &query_object : pSubCB->startedQueries) { |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query_object.pool); |
| if (query_pool_state && active_types.count(query_pool_state->createInfo.queryType)) { |
| LogObjectList objlist(pCB->commandBuffer()); |
| objlist.add(query_object.pool); |
| skip |= LogError(objlist, kVUID_Core_DrawState_InvalidSecondaryCommandBuffer, |
| "vkCmdExecuteCommands() called w/ invalid %s which has invalid active %s" |
| " of type %d but a query of that type has been started on secondary %s.", |
| report_data->FormatHandle(pCB->commandBuffer()).c_str(), |
| report_data->FormatHandle(query_object.pool).c_str(), query_pool_state->createInfo.queryType, |
| report_data->FormatHandle(pSubCB->commandBuffer()).c_str()); |
| } |
| } |
| } |
| const auto primary_pool = pCB->command_pool; |
| const auto secondary_pool = pSubCB->command_pool; |
| if (primary_pool && secondary_pool && (primary_pool->queueFamilyIndex != secondary_pool->queueFamilyIndex)) { |
| LogObjectList objlist(pSubCB->commandBuffer()); |
| objlist.add(pCB->commandBuffer()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-pCommandBuffers-00094", |
| "vkCmdExecuteCommands(): Primary %s created in queue family %d has secondary " |
| "%s created in queue family %d.", |
| report_data->FormatHandle(pCB->commandBuffer()).c_str(), primary_pool->queueFamilyIndex, |
| report_data->FormatHandle(pSubCB->commandBuffer()).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 (viewport_mask_ & bit) { |
| viewports_to_inherit_[n] = primary_state->dynamicViewports[n]; |
| } |
| } |
| |
| viewport_count_to_inherit_ = primary_state->viewportWithCountCount; |
| scissor_count_to_inherit_ = primary_state->scissorWithCountCount; |
| 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) { |
| viewports_to_inherit_[n] = secondary_state->dynamicViewports[n]; |
| viewport_trashed_by_[n] = kNotTrashed; |
| } |
| if ((secondary_state->scissorMask | secondary_state->scissorWithCountMask) & bit) { |
| scissor_trashed_by_[n] = kNotTrashed; |
| } |
| if (secondary_state->viewportWithCountCount != 0) { |
| viewport_count_to_inherit_ = secondary_state->viewportWithCountCount; |
| viewport_count_trashed_by_ = kNotTrashed; |
| } |
| if (secondary_state->scissorWithCountCount != 0) { |
| scissor_count_to_inherit_ = secondary_state->scissorWithCountCount; |
| 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-commandBuffer-02701", |
| "vkCmdExecuteCommands(): Draw commands in pCommandBuffers[%u] (%s) consume inherited viewport %u %s" |
| "but this state was not inherited as its depth range [%f, %f] does not match " |
| "pViewportDepths[%u] = [%f, %f]", |
| unsigned(cmd_buffer_idx), validation_.report_data->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_EXT: |
| state_name = "dynamic viewport count"; |
| break; |
| case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT: |
| 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_.report_data->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-commandBuffer-02701", "%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_EXT); |
| } 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_EXT); |
| } 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-commandBuffer-02701", |
| "vkCmdExecuteCommands(): " |
| "Draw commands in pCommandBuffers[%u] (%s) consume inherited dynamic viewport with count state " |
| "but the dynamic viewport count (%u) exceeds the inheritance limit (viewportDepthCount=%u).", |
| unsigned(cmd_buffer_idx), validation_.report_data->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 { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| layer_data::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.get()); |
| } |
| |
| bool active_occlusion_query = false; |
| for (const auto& active_query : cb_state->activeQueries) { |
| auto query_pool_state = Get<QUERY_POOL_STATE>(active_query.pool); |
| if (query_pool_state->createInfo.queryType == VK_QUERY_TYPE_OCCLUSION) { |
| active_occlusion_query = true; |
| break; |
| } |
| } |
| |
| if (cb_state->activeRenderPass) { |
| if ((cb_state->activeRenderPass->use_dynamic_rendering == false) && |
| (cb_state->activeSubpassContents != VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdExecuteCommands-contents-06018", |
| "vkCmdExecuteCommands(): contents must be set to VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS " |
| "when calling vkCmdExecuteCommands() within a render pass instance begun with " |
| "vkCmdBeginRenderPass()."); |
| } |
| |
| if ((cb_state->activeRenderPass->use_dynamic_rendering == true) && |
| !(cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.flags & |
| VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR)) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdExecuteCommands-flags-06024", |
| "vkCmdExecuteCommands(): VkRenderingInfo::flags must include " |
| "VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR when calling vkCmdExecuteCommands() within a " |
| "render pass instance begun with %s().", |
| cb_state->begin_rendering_func_name.c_str()); |
| } |
| } |
| |
| for (uint32_t i = 0; i < commandBuffersCount; i++) { |
| auto sub_cb_state = GetRead<CMD_BUFFER_STATE>(pCommandBuffers[i]); |
| assert(sub_cb_state); |
| |
| if (enabled_features.inherited_viewport_scissor_features.inheritedViewportScissor2D) |
| { |
| skip |= viewport_scissor_inheritance.VisitSecondary(i, sub_cb_state.get()); |
| } |
| |
| if (VK_COMMAND_BUFFER_LEVEL_PRIMARY == sub_cb_state->createInfo.level) { |
| skip |= LogError(pCommandBuffers[i], "VUID-vkCmdExecuteCommands-pCommandBuffers-00088", |
| "vkCmdExecuteCommands() called w/ Primary %s in element %u of pCommandBuffers array. All " |
| "cmd buffers in pCommandBuffers array must be secondary.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), i); |
| } else if (VK_COMMAND_BUFFER_LEVEL_SECONDARY == sub_cb_state->createInfo.level) { |
| if (sub_cb_state->beginInfo.pInheritanceInfo != nullptr) { |
| auto secondary_rp_state = Get<RENDER_PASS_STATE>(sub_cb_state->beginInfo.pInheritanceInfo->renderPass); |
| if (cb_state->activeRenderPass && |
| !(sub_cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-pCommandBuffers-00096", |
| "vkCmdExecuteCommands(): Secondary %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.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str()); |
| } else if (!cb_state->activeRenderPass && |
| (sub_cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) { |
| skip |= LogError(pCommandBuffers[i], "VUID-vkCmdExecuteCommands-pCommandBuffers-00100", |
| "vkCmdExecuteCommands(): Secondary %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.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str()); |
| } else if (cb_state->activeRenderPass && (cb_state->activeRenderPass->use_dynamic_rendering == false) && |
| (sub_cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) { |
| // Make sure render pass is compatible with parent command buffer pass if has continue |
| 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(), "vkCmdExecuteCommands()", "VUID-vkCmdExecuteCommands-pBeginInfo-06020"); |
| } |
| // If framebuffer for secondary CB is not NULL, then it must match active FB from primaryCB |
| skip |= ValidateFramebuffer(commandBuffer, cb_state.get(), pCommandBuffers[i], sub_cb_state.get(), |
| "vkCmdExecuteCommands()"); |
| if (!sub_cb_state->cmd_execute_commands_functions.empty()) { |
| // Inherit primary's activeFramebuffer and while running validate functions |
| for (auto &function : sub_cb_state->cmd_execute_commands_functions) { |
| skip |= function(*sub_cb_state, cb_state.get(), cb_state->activeFramebuffer.get()); |
| } |
| } |
| } |
| |
| if (cb_state->activeRenderPass && (cb_state->activeRenderPass->use_dynamic_rendering == false) && |
| (cb_state->activeSubpass != sub_cb_state->beginInfo.pInheritanceInfo->subpass)) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-pCommandBuffers-06019", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s " |
| "instance scope begun by vkCmdBeginRenderPass(), but " |
| "VkCommandBufferInheritanceInfo::subpass (%u) does not " |
| "match the current subpass (%u).", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| sub_cb_state->beginInfo.pInheritanceInfo->subpass, cb_state->activeSubpass); |
| } else if (cb_state->activeRenderPass && (cb_state->activeRenderPass->use_dynamic_rendering == true)) { |
| if (sub_cb_state->beginInfo.pInheritanceInfo->renderPass != VK_NULL_HANDLE) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-pBeginInfo-06025", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but " |
| "VkCommandBufferInheritanceInfo::pInheritanceInfo::renderPass is not VK_NULL_HANDLE.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str()); |
| } |
| |
| if (sub_cb_state->activeRenderPass->use_dynamic_rendering_inherited) { |
| if (sub_cb_state->activeRenderPass->inheritance_rendering_info.flags != |
| (cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.flags & |
| ~VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR)) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-flags-06026", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but VkCommandBufferInheritanceRenderingInfo::flags (%u) does " |
| "not match VkRenderingInfo::flags (%u), excluding " |
| "VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str(), |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.flags, |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.flags); |
| } |
| |
| if (sub_cb_state->activeRenderPass->inheritance_rendering_info.colorAttachmentCount != |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= |
| LogError(objlist, "VUID-vkCmdExecuteCommands-colorAttachmentCount-06027", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but " |
| "VkCommandBufferInheritanceRenderingInfo::colorAttachmentCount (%u) does " |
| "not match VkRenderingInfo::colorAttachmentCount (%u).", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str(), |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.colorAttachmentCount, |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount); |
| } |
| |
| for (uint32_t index = 0; |
| index < cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount; |
| index++) { |
| if (cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[index] |
| .imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[index] |
| .imageView); |
| |
| if (image_view_state->create_info.format != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.pColorAttachmentFormats[index]) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-imageView-06028", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but " |
| "VkCommandBufferInheritanceRenderingInfo::pColorAttachmentFormats at index (%u) does " |
| "not match the format of the imageView in VkRenderingInfo::pColorAttachments.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str(), index); |
| } |
| } |
| } |
| |
| if ((cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment != nullptr) && |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView); |
| |
| if (image_view_state->create_info.format != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.depthAttachmentFormat) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= |
| LogError(objlist, "VUID-vkCmdExecuteCommands-pDepthAttachment-06029", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but " |
| "VkCommandBufferInheritanceRenderingInfo::depthAttachmentFormat does " |
| "not match the format of the imageView in VkRenderingInfo::pDepthAttachment.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str()); |
| } |
| } |
| |
| if ((cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment != nullptr) && |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView); |
| |
| if (image_view_state->create_info.format != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.stencilAttachmentFormat) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= |
| LogError(objlist, "VUID-vkCmdExecuteCommands-pStencilAttachment-06030", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but " |
| "VkCommandBufferInheritanceRenderingInfo::stencilAttachmentFormat does " |
| "not match the format of the imageView in VkRenderingInfo::pStencilAttachment.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str()); |
| } |
| } |
| |
| if (cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.viewMask != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.viewMask) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-viewMask-06031", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by %s(), but " |
| "VkCommandBufferInheritanceRenderingInfo::viewMask (%u) does " |
| "not match VkRenderingInfo::viewMask (%u).", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), |
| cb_state->begin_rendering_func_name.c_str(), |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.viewMask, |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.viewMask); |
| } |
| |
| // VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV |
| const auto amd_sample_count = LvlFindInChain<VkAttachmentSampleCountInfoAMD>( |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.pNext); |
| |
| if (amd_sample_count) { |
| for (uint32_t index = 0; |
| index < cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount; |
| index++) { |
| if (cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[index] |
| .imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[index] |
| .imageView); |
| |
| if (image_view_state->samples != amd_sample_count->pColorAttachmentSamples[index]) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-pNext-06032", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by vkCmdBeginRenderingKHR(), but " |
| "VkAttachmentSampleCountInfo(AMD/NV)::pColorAttachmentSamples at index (%u) " |
| "does " |
| "not match the sample count of the imageView in VkRenderingInfoKHR::pColorAttachments.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), index); |
| } |
| } |
| } |
| |
| if ((cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment != nullptr) && |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView); |
| |
| if (image_view_state->samples != amd_sample_count->depthStencilAttachmentSamples) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-pNext-06033", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by vkCmdBeginRenderingKHR(), but " |
| "VkAttachmentSampleCountInfo(AMD/NV)::depthStencilAttachmentSamples does " |
| "not match the sample count of the imageView in VkRenderingInfoKHR::pDepthAttachment.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str()); |
| } |
| } |
| |
| if ((cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment != |
| nullptr) && |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = |
| Get<IMAGE_VIEW_STATE>(cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info |
| .pStencilAttachment->imageView); |
| |
| if (image_view_state->samples != amd_sample_count->depthStencilAttachmentSamples) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-pNext-06034", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by vkCmdBeginRenderingKHR(), but " |
| "VkAttachmentSampleCountInfo(AMD/NV)::depthStencilAttachmentSamples does " |
| "not match the sample count of the imageView in VkRenderingInfoKHR::pStencilAttachment.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str()); |
| } |
| } |
| } else { |
| for (uint32_t index = 0; |
| index < cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.colorAttachmentCount; |
| index++) { |
| if (cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[index] |
| .imageView != VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pColorAttachments[index] |
| .imageView); |
| |
| if (image_view_state->samples != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.rasterizationSamples) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-pNext-06035", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by vkCmdBeginRenderingKHR(), but the sample count of the image view at index (%u) of " |
| "VkRenderingInfoKHR::pColorAttachments does not match " |
| "VkCommandBufferInheritanceRenderingInfoKHR::rasterizationSamples.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str(), index); |
| } |
| } |
| } |
| |
| if ((cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment != nullptr) && |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = Get<IMAGE_VIEW_STATE>( |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView); |
| |
| if (image_view_state->samples != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.rasterizationSamples) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-pNext-06036", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by vkCmdBeginRenderingKHR(), but the sample count of the image view for " |
| "VkRenderingInfoKHR::pDepthAttachment does not match " |
| "VkCommandBufferInheritanceRenderingInfoKHR::rasterizationSamples.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str()); |
| } |
| } |
| |
| if ((cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment != |
| nullptr) && |
| cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView != |
| VK_NULL_HANDLE) { |
| auto image_view_state = |
| Get<IMAGE_VIEW_STATE>(cb_state->activeRenderPass->dynamic_rendering_begin_rendering_info |
| .pStencilAttachment->imageView); |
| |
| if (image_view_state->samples != |
| sub_cb_state->activeRenderPass->inheritance_rendering_info.rasterizationSamples) { |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->activeRenderPass->renderPass()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-pNext-06037", |
| "vkCmdExecuteCommands(): Secondary %s is executed within a %s instance scope begun " |
| "by vkCmdBeginRenderingKHR(), but the sample count of the image view for " |
| "VkRenderingInfoKHR::pStencilAttachment does not match " |
| "VkCommandBufferInheritanceRenderingInfoKHR::rasterizationSamples.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->activeRenderPass->renderPass()).c_str()); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // TODO(mlentine): Move more logic into this method |
| skip |= ValidateSecondaryCommandBufferState(cb_state.get(), sub_cb_state.get()); |
| skip |= ValidateCommandBufferState(sub_cb_state.get(), "vkCmdExecuteCommands()", 0, |
| "VUID-vkCmdExecuteCommands-pCommandBuffers-00089"); |
| if (!(sub_cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { |
| if (sub_cb_state->InUse()) { |
| skip |= LogError( |
| cb_state->commandBuffer(), "VUID-vkCmdExecuteCommands-pCommandBuffers-00091", |
| "vkCmdExecuteCommands(): Cannot execute pending %s without VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set.", |
| report_data->FormatHandle(sub_cb_state->commandBuffer()).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.get()))) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(sub_cb_state->commandBuffer()); |
| skip |= LogError(objlist, "VUID-vkCmdExecuteCommands-pCommandBuffers-00092", |
| "vkCmdExecuteCommands(): Cannot execute %s without VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT " |
| "set if previously executed in %s", |
| report_data->FormatHandle(sub_cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str()); |
| } |
| |
| const auto insert_pair = linked_command_buffers.insert(sub_cb_state.get()); |
| if (!insert_pair.second) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdExecuteCommands-pCommandBuffers-00093", |
| "vkCmdExecuteCommands(): Cannot duplicate %s in pCommandBuffers without " |
| "VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set.", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str()); |
| } |
| |
| if (cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT) { |
| // Warn that non-simultaneous secondary cmd buffer renders primary non-simultaneous |
| LogObjectList objlist(pCommandBuffers[i]); |
| objlist.add(cb_state->commandBuffer()); |
| skip |= LogWarning(objlist, kVUID_Core_DrawState_InvalidCommandBufferSimultaneousUse, |
| "vkCmdExecuteCommands(): Secondary %s does not have " |
| "VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set and will cause primary " |
| "%s to be treated as if it does not have " |
| "VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT set, even though it does.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str(), |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str()); |
| } |
| } |
| if (!cb_state->activeQueries.empty() && !enabled_features.core.inheritedQueries) { |
| skip |= LogError(pCommandBuffers[i], "VUID-vkCmdExecuteCommands-commandBuffer-00101", |
| "vkCmdExecuteCommands(): Secondary %s cannot be submitted with a query in flight and " |
| "inherited queries not supported on this device.", |
| report_data->FormatHandle(pCommandBuffers[i]).c_str()); |
| } |
| // 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 |
| const auto const_cb_state = std::static_pointer_cast<const CMD_BUFFER_STATE>(cb_state); |
| 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 = const_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_cb_subres_map = sub_layout_map_entry.second; |
| // Validate the initial_uses, that they match the current state of the primary cb, or absent a current state, |
| // that the match any initial_layout. |
| for (const auto &subres_layout : *sub_cb_subres_map) { |
| const auto &sub_layout = subres_layout.initial_layout; |
| const auto &subresource = subres_layout.subresource; |
| if (VK_IMAGE_LAYOUT_UNDEFINED == sub_layout) continue; // secondary doesn't care about current or initial |
| |
| // Look up the layout to compared to the intial layout of the sub command buffer (current else initial) |
| const auto *cb_layouts = cb_subres_map->GetSubresourceLayouts(subresource); |
| auto cb_layout = cb_layouts ? cb_layouts->current_layout : kInvalidLayout; |
| const char *layout_type = "current"; |
| if (cb_layout == kInvalidLayout) { |
| cb_layout = cb_layouts ? cb_layouts->initial_layout : kInvalidLayout; |
| layout_type = "initial"; |
| } |
| if ((cb_layout != kInvalidLayout) && (cb_layout != sub_layout)) { |
| skip |= LogError(pCommandBuffers[i], "UNASSIGNED-vkCmdExecuteCommands-commandBuffer-00001", |
| "%s: Executed secondary command buffer using %s (subresource: aspectMask 0x%X array layer %u, " |
| "mip level %u) which expects layout %s--instead, image %s layout is %s.", |
| "vkCmdExecuteCommands():", report_data->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)) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(sub_cb_state->commandBuffer()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-commandBuffer-01820", |
| "vkCmdExecuteCommands(): command buffer %s is protected while secondary command buffer %s is a unprotected", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(sub_cb_state->commandBuffer()).c_str()); |
| } else if ((cb_state->unprotected == true) && (sub_cb_state->unprotected == false)) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(sub_cb_state->commandBuffer()); |
| skip |= LogError( |
| objlist, "VUID-vkCmdExecuteCommands-commandBuffer-01821", |
| "vkCmdExecuteCommands(): command buffer %s is unprotected while secondary command buffer %s is a protected", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(sub_cb_state->commandBuffer()).c_str()); |
| } |
| if (active_occlusion_query && sub_cb_state->inheritanceInfo.occlusionQueryEnable != VK_TRUE) { |
| skip |= LogError(pCommandBuffers[i], "VUID-vkCmdExecuteCommands-commandBuffer-00102", |
| "vkCmdExecuteCommands(): command buffer %s has an active occlusion query, but secondary command " |
| "buffer %s was recorded with VkCommandBufferInheritanceInfo::occlusionQueryEnable set to VK_FALSE", |
| report_data->FormatHandle(cb_state->commandBuffer()).c_str(), |
| report_data->FormatHandle(sub_cb_state->commandBuffer()).c_str()); |
| } |
| } |
| |
| skip |= ValidateCmd(cb_state.get(), CMD_EXECUTECOMMANDS); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateMapMemory(VkDevice device, VkDeviceMemory mem, VkDeviceSize offset, VkDeviceSize size, |
| VkFlags flags, void **ppData) const { |
| bool skip = false; |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem); |
| if (mem_info) { |
| if ((phys_dev_mem_props.memoryTypes[mem_info->alloc_info.memoryTypeIndex].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) { |
| skip = LogError(mem, "VUID-vkMapMemory-memory-00682", |
| "Mapping Memory without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT set: %s.", |
| report_data->FormatHandle(mem).c_str()); |
| } |
| |
| if (mem_info->multi_instance) { |
| skip = LogError(mem, "VUID-vkMapMemory-memory-00683", |
| "Memory (%s) must not have been allocated with multiple instances -- either by supplying a deviceMask " |
| "with more than one bit set, or by allocation from a heap with the MULTI_INSTANCE heap flag set.", |
| report_data->FormatHandle(mem).c_str()); |
| } |
| |
| skip |= ValidateMapMemRange(mem_info.get(), offset, size); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateUnmapMemory(VkDevice device, VkDeviceMemory mem) const { |
| bool skip = false; |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem); |
| if (mem_info && !mem_info->mapped_range.size) { |
| // Valid Usage: memory must currently be mapped |
| skip |= LogError(mem, "VUID-vkUnmapMemory-memory-00689", "Unmapping Memory without memory being mapped: %s.", |
| report_data->FormatHandle(mem).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateMemoryIsMapped(const char *funcName, uint32_t memRangeCount, const VkMappedMemoryRange *pMemRanges) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < memRangeCount; ++i) { |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(pMemRanges[i].memory); |
| if (mem_info) { |
| // Makes sure the memory is already mapped |
| if (mem_info->mapped_range.size == 0) { |
| skip = LogError(pMemRanges[i].memory, "VUID-VkMappedMemoryRange-memory-00684", |
| "%s: Attempting to use memory (%s) that is not currently host mapped.", funcName, |
| report_data->FormatHandle(pMemRanges[i].memory).c_str()); |
| } |
| |
| if (pMemRanges[i].size == VK_WHOLE_SIZE) { |
| if (mem_info->mapped_range.offset > pMemRanges[i].offset) { |
| skip |= LogError(pMemRanges[i].memory, "VUID-VkMappedMemoryRange-size-00686", |
| "%s: Flush/Invalidate offset (" PRINTF_SIZE_T_SPECIFIER |
| ") is less than Memory Object's offset (" PRINTF_SIZE_T_SPECIFIER ").", |
| funcName, static_cast<size_t>(pMemRanges[i].offset), |
| static_cast<size_t>(mem_info->mapped_range.offset)); |
| } |
| } else { |
| const uint64_t data_end = (mem_info->mapped_range.size == VK_WHOLE_SIZE) |
| ? mem_info->alloc_info.allocationSize |
| : (mem_info->mapped_range.offset + mem_info->mapped_range.size); |
| if ((mem_info->mapped_range.offset > pMemRanges[i].offset) || |
| (data_end < (pMemRanges[i].offset + pMemRanges[i].size))) { |
| skip |= LogError(pMemRanges[i].memory, "VUID-VkMappedMemoryRange-size-00685", |
| "%s: Flush/Invalidate size or offset (" PRINTF_SIZE_T_SPECIFIER ", " PRINTF_SIZE_T_SPECIFIER |
| ") exceed the Memory Object's upper-bound (" PRINTF_SIZE_T_SPECIFIER ").", |
| funcName, static_cast<size_t>(pMemRanges[i].offset + pMemRanges[i].size), |
| static_cast<size_t>(pMemRanges[i].offset), static_cast<size_t>(data_end)); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateMappedMemoryRangeDeviceLimits(const char *func_name, uint32_t mem_range_count, |
| const VkMappedMemoryRange *mem_ranges) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < mem_range_count; ++i) { |
| const uint64_t atom_size = phys_dev_props.limits.nonCoherentAtomSize; |
| const VkDeviceSize offset = mem_ranges[i].offset; |
| const VkDeviceSize size = mem_ranges[i].size; |
| |
| if (SafeModulo(offset, atom_size) != 0) { |
| skip |= LogError(mem_ranges->memory, "VUID-VkMappedMemoryRange-offset-00687", |
| "%s: Offset in pMemRanges[%d] is 0x%" PRIxLEAST64 |
| ", which is not a multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize (0x%" PRIxLEAST64 ").", |
| func_name, i, offset, atom_size); |
| } |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem_ranges[i].memory); |
| if (mem_info) { |
| const auto allocation_size = mem_info->alloc_info.allocationSize; |
| if (size == VK_WHOLE_SIZE) { |
| const auto mapping_offset = mem_info->mapped_range.offset; |
| const auto mapping_size = mem_info->mapped_range.size; |
| const auto mapping_end = ((mapping_size == VK_WHOLE_SIZE) ? allocation_size : mapping_offset + mapping_size); |
| if (SafeModulo(mapping_end, atom_size) != 0 && mapping_end != allocation_size) { |
| skip |= LogError(mem_ranges->memory, "VUID-VkMappedMemoryRange-size-01389", |
| "%s: Size in pMemRanges[%d] is VK_WHOLE_SIZE and the mapping end (0x%" PRIxLEAST64 |
| " = 0x%" PRIxLEAST64 " + 0x%" PRIxLEAST64 |
| ") not a multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize (0x%" PRIxLEAST64 |
| ") and not equal to the end of the memory object (0x%" PRIxLEAST64 ").", |
| func_name, i, mapping_end, mapping_offset, mapping_size, atom_size, allocation_size); |
| } |
| } else { |
| const auto range_end = size + offset; |
| if (range_end != allocation_size && SafeModulo(size, atom_size) != 0) { |
| skip |= LogError(mem_ranges->memory, "VUID-VkMappedMemoryRange-size-01390", |
| "%s: Size in pMemRanges[%d] is 0x%" PRIxLEAST64 |
| ", which is not a multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize (0x%" PRIxLEAST64 |
| ") and offset + size (0x%" PRIxLEAST64 " + 0x%" PRIxLEAST64 " = 0x%" PRIxLEAST64 |
| ") not equal to the memory size (0x%" PRIxLEAST64 ").", |
| func_name, i, size, atom_size, offset, size, range_end, allocation_size); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateFlushMappedMemoryRanges(VkDevice device, uint32_t memRangeCount, |
| const VkMappedMemoryRange *pMemRanges) const { |
| bool skip = false; |
| skip |= ValidateMappedMemoryRangeDeviceLimits("vkFlushMappedMemoryRanges", memRangeCount, pMemRanges); |
| skip |= ValidateMemoryIsMapped("vkFlushMappedMemoryRanges", memRangeCount, pMemRanges); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateInvalidateMappedMemoryRanges(VkDevice device, uint32_t memRangeCount, |
| const VkMappedMemoryRange *pMemRanges) const { |
| bool skip = false; |
| skip |= ValidateMappedMemoryRangeDeviceLimits("vkInvalidateMappedMemoryRanges", memRangeCount, pMemRanges); |
| skip |= ValidateMemoryIsMapped("vkInvalidateMappedMemoryRanges", memRangeCount, pMemRanges); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDeviceMemoryCommitment(VkDevice device, VkDeviceMemory mem, VkDeviceSize *pCommittedMem) const { |
| bool skip = false; |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(mem); |
| |
| if (mem_info) { |
| if ((phys_dev_mem_props.memoryTypes[mem_info->alloc_info.memoryTypeIndex].propertyFlags & |
| VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) == 0) { |
| skip = LogError(mem, "VUID-vkGetDeviceMemoryCommitment-memory-00690", |
| "vkGetDeviceMemoryCommitment(): Querying commitment for memory without " |
| "VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT set: %s.", |
| report_data->FormatHandle(mem).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateBindImageMemory(uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos, |
| const char *api_name) const { |
| bool skip = false; |
| |
| bool bind_image_mem_2 = strcmp(api_name, "vkBindImageMemory()") != 0; |
| char error_prefix[128]; |
| strcpy(error_prefix, api_name); |
| |
| // Track all image sub resources if they are bound for bind_image_mem_2 |
| // uint32_t[3] is which index in pBindInfos for max 3 planes |
| // Non disjoint images act as a single plane |
| layer_data::unordered_map<VkImage, std::array<uint32_t, 3>> resources_bound; |
| |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| if (bind_image_mem_2 == true) { |
| sprintf(error_prefix, "%s pBindInfos[%u]", api_name, i); |
| } |
| |
| const VkBindImageMemoryInfo &bind_info = pBindInfos[i]; |
| auto image_state = Get<IMAGE_STATE>(bind_info.image); |
| if (image_state) { |
| // Track objects tied to memory |
| skip |= ValidateSetMemBinding(bind_info.memory, *image_state, error_prefix); |
| |
| const auto plane_info = LvlFindInChain<VkBindImagePlaneMemoryInfo>(bind_info.pNext); |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(bind_info.memory); |
| |
| // Need extra check for disjoint flag incase called without bindImage2 and don't want false positive errors |
| // no 'else' case as if that happens another VUID is already being triggered for it being invalid |
| if ((plane_info == nullptr) && (image_state->disjoint == false)) { |
| // Check non-disjoint images VkMemoryRequirements |
| |
| // All validation using the image_state->requirements for external AHB is check in android only section |
| if (image_state->IsExternalAHB() == false) { |
| const VkMemoryRequirements &mem_req = image_state->requirements[0]; |
| |
| // Validate memory requirements alignment |
| if (SafeModulo(bind_info.memoryOffset, mem_req.alignment) != 0) { |
| const char *validation_error; |
| if (bind_image_mem_2 == false) { |
| validation_error = "VUID-vkBindImageMemory-memoryOffset-01048"; |
| } else if (IsExtEnabled(device_extensions.vk_khr_sampler_ycbcr_conversion)) { |
| validation_error = "VUID-VkBindImageMemoryInfo-pNext-01616"; |
| } else { |
| validation_error = "VUID-VkBindImageMemoryInfo-memoryOffset-01613"; |
| } |
| skip |= |
| LogError(bind_info.image, validation_error, |
| "%s: memoryOffset is 0x%" PRIxLEAST64 |
| " but must be an integer multiple of the VkMemoryRequirements::alignment value 0x%" PRIxLEAST64 |
| ", returned from a call to vkGetImageMemoryRequirements with image.", |
| error_prefix, bind_info.memoryOffset, mem_req.alignment); |
| } |
| |
| if (mem_info) { |
| safe_VkMemoryAllocateInfo alloc_info = mem_info->alloc_info; |
| // Validate memory requirements size |
| if (mem_req.size > alloc_info.allocationSize - bind_info.memoryOffset) { |
| const char *validation_error; |
| if (bind_image_mem_2 == false) { |
| validation_error = "VUID-vkBindImageMemory-size-01049"; |
| } else if (IsExtEnabled(device_extensions.vk_khr_sampler_ycbcr_conversion)) { |
| validation_error = "VUID-VkBindImageMemoryInfo-pNext-01617"; |
| } else { |
| validation_error = "VUID-VkBindImageMemoryInfo-memory-01614"; |
| } |
| skip |= LogError(bind_info.image, validation_error, |
| "%s: memory size minus memoryOffset is 0x%" PRIxLEAST64 |
| " but must be at least as large as VkMemoryRequirements::size value 0x%" PRIxLEAST64 |
| ", returned from a call to vkGetImageMemoryRequirements with image.", |
| error_prefix, alloc_info.allocationSize - bind_info.memoryOffset, mem_req.size); |
| } |
| |
| // Validate memory type used |
| { |
| const char *validation_error; |
| if (bind_image_mem_2 == false) { |
| validation_error = "VUID-vkBindImageMemory-memory-01047"; |
| } else if (IsExtEnabled(device_extensions.vk_khr_sampler_ycbcr_conversion)) { |
| validation_error = "VUID-VkBindImageMemoryInfo-pNext-01615"; |
| } else { |
| validation_error = "VUID-VkBindImageMemoryInfo-memory-01612"; |
| } |
| skip |= ValidateMemoryTypes(mem_info.get(), mem_req.memoryTypeBits, error_prefix, validation_error); |
| } |
| } |
| } |
| |
| if (bind_image_mem_2 == true) { |
| // since its a non-disjoint image, finding VkImage in map is a duplicate |
| auto it = resources_bound.find(image_state->image()); |
| if (it == resources_bound.end()) { |
| std::array<uint32_t, 3> bound_index = {i, UINT32_MAX, UINT32_MAX}; |
| resources_bound.emplace(image_state->image(), bound_index); |
| } else { |
| skip |= LogError( |
| bind_info.image, "VUID-vkBindImageMemory2-pBindInfos-04006", |
| "%s: The same non-disjoint image resource is being bound twice at pBindInfos[%d] and pBindInfos[%d]", |
| error_prefix, it->second[0], i); |
| } |
| } |
| } else if ((plane_info != nullptr) && (image_state->disjoint == true)) { |
| // Check disjoint images VkMemoryRequirements for given plane |
| int plane = 0; |
| |
| // All validation using the image_state->plane*_requirements for external AHB is check in android only section |
| if (image_state->IsExternalAHB() == false) { |
| const VkImageAspectFlagBits aspect = plane_info->planeAspect; |
| switch (aspect) { |
| case VK_IMAGE_ASPECT_PLANE_0_BIT: |
| plane = 0; |
| break; |
| case VK_IMAGE_ASPECT_PLANE_1_BIT: |
| plane = 1; |
| break; |
| case VK_IMAGE_ASPECT_PLANE_2_BIT: |
| plane = 2; |
| break; |
| default: |
| assert(false); // parameter validation should have caught this |
| break; |
| } |
| const VkMemoryRequirements &disjoint_mem_req = image_state->requirements[plane]; |
| |
| // Validate memory requirements alignment |
| if (SafeModulo(bind_info.memoryOffset, disjoint_mem_req.alignment) != 0) { |
| skip |= LogError( |
| bind_info.image, "VUID-VkBindImageMemoryInfo-pNext-01620", |
| "%s: memoryOffset is 0x%" PRIxLEAST64 |
| " but must be an integer multiple of the VkMemoryRequirements::alignment value 0x%" PRIxLEAST64 |
| ", returned from a call to vkGetImageMemoryRequirements2 with disjoint image for aspect plane %s.", |
| error_prefix, bind_info.memoryOffset, disjoint_mem_req.alignment, string_VkImageAspectFlagBits(aspect)); |
| } |
| |
| if (mem_info) { |
| safe_VkMemoryAllocateInfo alloc_info = mem_info->alloc_info; |
| |
| // Validate memory requirements size |
| if (disjoint_mem_req.size > alloc_info.allocationSize - bind_info.memoryOffset) { |
| skip |= LogError( |
| bind_info.image, "VUID-VkBindImageMemoryInfo-pNext-01621", |
| "%s: memory size minus memoryOffset is 0x%" PRIxLEAST64 |
| " but must be at least as large as VkMemoryRequirements::size value 0x%" PRIxLEAST64 |
| ", returned from a call to vkGetImageMemoryRequirements with disjoint image for aspect plane %s.", |
| error_prefix, alloc_info.allocationSize - bind_info.memoryOffset, disjoint_mem_req.size, |
| string_VkImageAspectFlagBits(aspect)); |
| } |
| |
| // Validate memory type used |
| { |
| skip |= ValidateMemoryTypes(mem_info.get(), disjoint_mem_req.memoryTypeBits, error_prefix, |
| "VUID-VkBindImageMemoryInfo-pNext-01619"); |
| } |
| } |
| } |
| |
| auto it = resources_bound.find(image_state->image()); |
| if (it == resources_bound.end()) { |
| std::array<uint32_t, 3> bound_index = {UINT32_MAX, UINT32_MAX, UINT32_MAX}; |
| bound_index[plane] = i; |
| resources_bound.emplace(image_state->image(), bound_index); |
| } else { |
| if (it->second[plane] == UINT32_MAX) { |
| it->second[plane] = i; |
| } else { |
| skip |= LogError(bind_info.image, "VUID-vkBindImageMemory2-pBindInfos-04006", |
| "%s: The same disjoint image sub-resource for plane %d is being bound twice at " |
| "pBindInfos[%d] and pBindInfos[%d]", |
| error_prefix, plane, it->second[plane], i); |
| } |
| } |
| } |
| |
| if (mem_info) { |
| // Validate bound memory range information |
| // if memory is exported to an AHB then the mem_info->allocationSize must be zero and this check is not needed |
| if ((mem_info->IsExport() == false) || |
| ((mem_info->export_handle_type_flags & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) == |
| 0)) { |
| skip |= ValidateInsertImageMemoryRange(bind_info.image, mem_info.get(), bind_info.memoryOffset, error_prefix); |
| } |
| |
| // Validate dedicated allocation |
| if (mem_info->IsDedicatedImage()) { |
| if (enabled_features.dedicated_allocation_image_aliasing_features.dedicatedAllocationImageAliasing) { |
| auto current_image_state = Get<IMAGE_STATE>(bind_info.image); |
| if ((bind_info.memoryOffset != 0) || !current_image_state || |
| !current_image_state->IsCreateInfoDedicatedAllocationImageAliasingCompatible( |
| mem_info->dedicated->create_info.image)) { |
| const char *validation_error; |
| if (bind_image_mem_2 == false) { |
| validation_error = "VUID-vkBindImageMemory-memory-02629"; |
| } else { |
| validation_error = "VUID-VkBindImageMemoryInfo-memory-02629"; |
| } |
| LogObjectList objlist(bind_info.image); |
| objlist.add(bind_info.memory); |
| objlist.add(mem_info->dedicated->handle); |
| skip |= LogError( |
| objlist, validation_error, |
| "%s: for dedicated memory allocation %s, VkMemoryDedicatedAllocateInfo:: %s must compatible " |
| "with %s and memoryOffset 0x%" PRIxLEAST64 " must be zero.", |
| error_prefix, report_data->FormatHandle(bind_info.memory).c_str(), |
| report_data->FormatHandle(mem_info->dedicated->handle).c_str(), |
| report_data->FormatHandle(bind_info.image).c_str(), bind_info.memoryOffset); |
| } |
| } else { |
| if ((bind_info.memoryOffset != 0) || (mem_info->dedicated->handle.Cast<VkImage>() != bind_info.image)) { |
| const char *validation_error; |
| if (bind_image_mem_2 == false) { |
| validation_error = "VUID-vkBindImageMemory-memory-01509"; |
| } else { |
| validation_error = "VUID-VkBindImageMemoryInfo-memory-01509"; |
| } |
| LogObjectList objlist(bind_info.image); |
| objlist.add(bind_info.memory); |
| objlist.add(mem_info->dedicated->handle); |
| skip |= |
| LogError(objlist, validation_error, |
| "%s: for dedicated memory allocation %s, VkMemoryDedicatedAllocateInfo:: %s must be equal " |
| "to %s and memoryOffset 0x%" PRIxLEAST64 " must be zero.", |
| error_prefix, report_data->FormatHandle(bind_info.memory).c_str(), |
| report_data->FormatHandle(mem_info->dedicated->handle).c_str(), |
| report_data->FormatHandle(bind_info.image).c_str(), bind_info.memoryOffset); |
| } |
| } |
| } |
| |
| // Validate export memory handles |
| if ((mem_info->export_handle_type_flags != 0) && |
| ((mem_info->export_handle_type_flags & image_state->external_memory_handle) == 0)) { |
| const char *vuid = |
| bind_image_mem_2 ? "VUID-VkBindImageMemoryInfo-memory-02728" : "VUID-vkBindImageMemory-memory-02728"; |
| LogObjectList objlist(bind_info.image); |
| objlist.add(bind_info.memory); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) has an external handleType of %s which does not include at least " |
| "one handle from VkImage (%s) handleType %s.", |
| error_prefix, report_data->FormatHandle(bind_info.memory).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(mem_info->export_handle_type_flags).c_str(), |
| report_data->FormatHandle(bind_info.image).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(image_state->external_memory_handle).c_str()); |
| } |
| |
| // Validate import memory handles |
| if (mem_info->IsImportAHB() == true) { |
| skip |= ValidateImageImportedHandleANDROID(api_name, image_state->external_memory_handle, bind_info.memory, |
| bind_info.image); |
| } else if (mem_info->IsImport() == true) { |
| if ((mem_info->import_handle_type_flags & image_state->external_memory_handle) == 0) { |
| const char *vuid = nullptr; |
| if ((bind_image_mem_2) && |
| IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-VkBindImageMemoryInfo-memory-02989"; |
| } else if ((!bind_image_mem_2) && |
| IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-vkBindImageMemory-memory-02989"; |
| } else if ((bind_image_mem_2) && |
| !IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-VkBindImageMemoryInfo-memory-02729"; |
| } else if ((!bind_image_mem_2) && |
| !IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer)) { |
| vuid = "VUID-vkBindImageMemory-memory-02729"; |
| } |
| LogObjectList objlist(bind_info.image); |
| objlist.add(bind_info.memory); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was created with an import operation with handleType of %s " |
| "which is not set in the VkImage (%s) VkExternalMemoryImageCreateInfo::handleType (%s)", |
| api_name, report_data->FormatHandle(bind_info.memory).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(mem_info->import_handle_type_flags).c_str(), |
| report_data->FormatHandle(bind_info.image).c_str(), |
| string_VkExternalMemoryHandleTypeFlags(image_state->external_memory_handle).c_str()); |
| } |
| } |
| |
| // Validate mix of protected buffer and memory |
| if ((image_state->unprotected == false) && (mem_info->unprotected == true)) { |
| const char *vuid = |
| bind_image_mem_2 ? "VUID-VkBindImageMemoryInfo-None-01901" : "VUID-vkBindImageMemory-None-01901"; |
| LogObjectList objlist(bind_info.image); |
| objlist.add(bind_info.memory); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was not created with protected memory but the VkImage (%s) was " |
| "set to use protected memory.", |
| api_name, report_data->FormatHandle(bind_info.memory).c_str(), |
| report_data->FormatHandle(bind_info.image).c_str()); |
| } else if ((image_state->unprotected == true) && (mem_info->unprotected == false)) { |
| const char *vuid = |
| bind_image_mem_2 ? "VUID-VkBindImageMemoryInfo-None-01902" : "VUID-vkBindImageMemory-None-01902"; |
| LogObjectList objlist(bind_info.image); |
| objlist.add(bind_info.memory); |
| skip |= LogError(objlist, vuid, |
| "%s: The VkDeviceMemory (%s) was created with protected memory but the VkImage (%s) was not " |
| "set to use protected memory.", |
| api_name, report_data->FormatHandle(bind_info.memory).c_str(), |
| report_data->FormatHandle(bind_info.image).c_str()); |
| } |
| } |
| |
| const auto swapchain_info = LvlFindInChain<VkBindImageMemorySwapchainInfoKHR>(bind_info.pNext); |
| if (swapchain_info) { |
| if (bind_info.memory != VK_NULL_HANDLE) { |
| skip |= LogError(bind_info.image, "VUID-VkBindImageMemoryInfo-pNext-01631", "%s: %s is not VK_NULL_HANDLE.", |
| error_prefix, report_data->FormatHandle(bind_info.memory).c_str()); |
| } |
| if (image_state->create_from_swapchain != swapchain_info->swapchain) { |
| LogObjectList objlist(image_state->image()); |
| objlist.add(image_state->create_from_swapchain); |
| objlist.add(swapchain_info->swapchain); |
| skip |= LogError( |
| objlist, kVUID_Core_BindImageMemory_Swapchain, |
| "%s: %s is created by %s, but the image is bound by %s. The image should be created and bound by the same " |
| "swapchain", |
| error_prefix, report_data->FormatHandle(image_state->image()).c_str(), |
| report_data->FormatHandle(image_state->create_from_swapchain).c_str(), |
| report_data->FormatHandle(swapchain_info->swapchain).c_str()); |
| } |
| auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain_info->swapchain); |
| if (swapchain_state && swapchain_state->images.size() <= swapchain_info->imageIndex) { |
| skip |= LogError(bind_info.image, "VUID-VkBindImageMemorySwapchainInfoKHR-imageIndex-01644", |
| "%s: imageIndex (%i) is out of bounds of %s images (size: %i)", error_prefix, |
| swapchain_info->imageIndex, report_data->FormatHandle(swapchain_info->swapchain).c_str(), |
| static_cast<int>(swapchain_state->images.size())); |
| } |
| } else { |
| if (image_state->create_from_swapchain) { |
| skip |= LogError(bind_info.image, "VUID-VkBindImageMemoryInfo-image-01630", |
| "%s: pNext of VkBindImageMemoryInfo doesn't include VkBindImageMemorySwapchainInfoKHR.", |
| error_prefix); |
| } |
| if (!mem_info) { |
| skip |= LogError(bind_info.image, "VUID-VkBindImageMemoryInfo-pNext-01632", "%s: %s is invalid.", error_prefix, |
| report_data->FormatHandle(bind_info.memory).c_str()); |
| } |
| } |
| |
| const auto bind_image_memory_device_group_info = LvlFindInChain<VkBindImageMemoryDeviceGroupInfo>(bind_info.pNext); |
| if (bind_image_memory_device_group_info && bind_image_memory_device_group_info->splitInstanceBindRegionCount != 0) { |
| if (!(image_state->createInfo.flags & VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT)) { |
| skip |= LogError(bind_info.image, "VUID-VkBindImageMemoryInfo-pNext-01627", |
| "%s: pNext of VkBindImageMemoryInfo contains VkBindImageMemoryDeviceGroupInfo with " |
| "splitInstanceBindRegionCount (%" PRIi32 |
| ") not equal to 0 and %s is not created with " |
| "VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT.", |
| error_prefix, bind_image_memory_device_group_info->splitInstanceBindRegionCount, |
| report_data->FormatHandle(image_state->image()).c_str()); |
| } |
| uint32_t phy_dev_square = 1; |
| if (device_group_create_info.physicalDeviceCount > 0) { |
| phy_dev_square = device_group_create_info.physicalDeviceCount * device_group_create_info.physicalDeviceCount; |
| } |
| if (bind_image_memory_device_group_info->splitInstanceBindRegionCount != phy_dev_square) { |
| skip |= LogError( |
| bind_info.image, "VUID-VkBindImageMemoryDeviceGroupInfo-splitInstanceBindRegionCount-01636", |
| "%s: pNext of VkBindImageMemoryInfo contains VkBindImageMemoryDeviceGroupInfo with " |
| "splitInstanceBindRegionCount (%" PRIi32 |
| ") which is not 0 and different from the number of physical devices in the logical device squared (%" PRIu32 |
| ").", |
| error_prefix, bind_image_memory_device_group_info->splitInstanceBindRegionCount, phy_dev_square); |
| } |
| } |
| |
| if (plane_info) { |
| // Checks for disjoint bit in image |
| if (image_state->disjoint == false) { |
| skip |= LogError( |
| bind_info.image, "VUID-VkBindImageMemoryInfo-pNext-01618", |
| "%s: pNext of VkBindImageMemoryInfo contains VkBindImagePlaneMemoryInfo and %s is not created with " |
| "VK_IMAGE_CREATE_DISJOINT_BIT.", |
| error_prefix, report_data->FormatHandle(image_state->image()).c_str()); |
| } |
| |
| // Make sure planeAspect is only a single, valid plane |
| uint32_t planes = FormatPlaneCount(image_state->createInfo.format); |
| VkImageAspectFlags aspect = plane_info->planeAspect; |
| if ((2 == planes) && (aspect != VK_IMAGE_ASPECT_PLANE_0_BIT) && (aspect != VK_IMAGE_ASPECT_PLANE_1_BIT)) { |
| skip |= LogError( |
| bind_info.image, "VUID-VkBindImagePlaneMemoryInfo-planeAspect-02283", |
| "%s: Image %s VkBindImagePlaneMemoryInfo::planeAspect is %s but can only be VK_IMAGE_ASPECT_PLANE_0_BIT" |
| "or VK_IMAGE_ASPECT_PLANE_1_BIT.", |
| error_prefix, report_data->FormatHandle(image_state->image()).c_str(), |
| string_VkImageAspectFlags(aspect).c_str()); |
| } |
| if ((3 == planes) && (aspect != VK_IMAGE_ASPECT_PLANE_0_BIT) && (aspect != VK_IMAGE_ASPECT_PLANE_1_BIT) && |
| (aspect != VK_IMAGE_ASPECT_PLANE_2_BIT)) { |
| skip |= LogError( |
| bind_info.image, "VUID-VkBindImagePlaneMemoryInfo-planeAspect-02283", |
| "%s: Image %s VkBindImagePlaneMemoryInfo::planeAspect is %s but can only be VK_IMAGE_ASPECT_PLANE_0_BIT" |
| "or VK_IMAGE_ASPECT_PLANE_1_BIT or VK_IMAGE_ASPECT_PLANE_2_BIT.", |
| error_prefix, report_data->FormatHandle(image_state->image()).c_str(), |
| string_VkImageAspectFlags(aspect).c_str()); |
| } |
| } |
| } |
| |
| const auto bind_image_memory_device_group = LvlFindInChain<VkBindImageMemoryDeviceGroupInfo>(bind_info.pNext); |
| if (bind_image_memory_device_group) { |
| if (bind_image_memory_device_group->deviceIndexCount > 0 && |
| bind_image_memory_device_group->splitInstanceBindRegionCount > 0) { |
| skip |= LogError(bind_info.image, "VUID-VkBindImageMemoryDeviceGroupInfo-deviceIndexCount-01633", |
| "%s: VkBindImageMemoryDeviceGroupInfo in pNext of pBindInfos[%" PRIu32 |
| "] has both deviceIndexCount and splitInstanceBindRegionCount greater than 0.", |
| error_prefix, i); |
| } |
| } |
| } |
| |
| // Check to make sure all disjoint planes were bound |
| for (auto &resource : resources_bound) { |
| auto image_state = Get<IMAGE_STATE>(resource.first); |
| if (image_state->disjoint == true) { |
| uint32_t total_planes = FormatPlaneCount(image_state->createInfo.format); |
| for (uint32_t i = 0; i < total_planes; i++) { |
| if (resource.second[i] == UINT32_MAX) { |
| skip |= LogError(resource.first, "VUID-vkBindImageMemory2-pBindInfos-02858", |
| "%s: Plane %u of the disjoint image was not bound. All %d planes need to bound individually " |
| "in separate pBindInfos in a single call.", |
| api_name, i, total_planes); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory mem, |
| VkDeviceSize memoryOffset) const { |
| bool skip = false; |
| auto image_state = Get<IMAGE_STATE>(image); |
| if (image_state) { |
| // Checks for no disjoint bit |
| if (image_state->disjoint == true) { |
| skip |= |
| LogError(image, "VUID-vkBindImageMemory-image-01608", |
| "%s must not have been created with the VK_IMAGE_CREATE_DISJOINT_BIT (need to use vkBindImageMemory2).", |
| report_data->FormatHandle(image).c_str()); |
| } |
| } |
| |
| auto bind_info = LvlInitStruct<VkBindImageMemoryInfo>(); |
| bind_info.image = image; |
| bind_info.memory = mem; |
| bind_info.memoryOffset = memoryOffset; |
| skip |= ValidateBindImageMemory(1, &bind_info, "vkBindImageMemory()"); |
| return skip; |
| } |
| |
| void CoreChecks::PostCallRecordBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory mem, VkDeviceSize memoryOffset, |
| VkResult result) { |
| if (VK_SUCCESS != result) return; |
| |
| StateTracker::PostCallRecordBindImageMemory(device, image, mem, memoryOffset, result); |
| auto image_state = Get<IMAGE_STATE>(image); |
| if (image_state) { |
| image_state->SetInitialLayoutMap(); |
| } |
| } |
| |
| bool CoreChecks::PreCallValidateBindImageMemory2(VkDevice device, uint32_t bindInfoCount, |
| const VkBindImageMemoryInfo *pBindInfos) const { |
| return ValidateBindImageMemory(bindInfoCount, pBindInfos, "vkBindImageMemory2()"); |
| } |
| |
| void CoreChecks::PostCallRecordBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos, |
| VkResult result) { |
| if (VK_SUCCESS != result) return; |
| StateTracker::PostCallRecordBindImageMemory2(device, bindInfoCount, pBindInfos, result); |
| |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| auto image_state = Get<IMAGE_STATE>(pBindInfos[i].image); |
| if (image_state) { |
| image_state->SetInitialLayoutMap(); |
| } |
| } |
| } |
| |
| bool CoreChecks::PreCallValidateBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, |
| const VkBindImageMemoryInfo *pBindInfos) const { |
| return ValidateBindImageMemory(bindInfoCount, pBindInfos, "vkBindImageMemory2KHR()"); |
| } |
| |
| void CoreChecks::PostCallRecordBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos, |
| VkResult result) { |
| if (VK_SUCCESS != result) return; |
| StateTracker::PostCallRecordBindImageMemory2KHR(device, bindInfoCount, pBindInfos, result); |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| auto image_state = Get<IMAGE_STATE>(pBindInfos[i].image); |
| if (image_state) { |
| image_state->SetInitialLayoutMap(); |
| } |
| } |
| } |
| |
| bool CoreChecks::PreCallValidateSetEvent(VkDevice device, VkEvent event) const { |
| bool skip = false; |
| auto event_state = Get<EVENT_STATE>(event); |
| if (event_state) { |
| if (event_state->write_in_use) { |
| skip |= |
| LogError(event, kVUID_Core_DrawState_QueueForwardProgress, |
| "vkSetEvent(): %s that is already in use by a command buffer.", report_data->FormatHandle(event).c_str()); |
| } |
| if (event_state->flags & VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR) { |
| skip |= LogError(event, "VUID-vkSetEvent-event-03941", |
| "vkSetEvent(): %s was created with VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR.", |
| report_data->FormatHandle(event).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateResetEvent(VkDevice device, VkEvent event) const { |
| bool skip = false; |
| auto event_state = Get<EVENT_STATE>(event); |
| if (event_state) { |
| if (event_state->flags & VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR) { |
| skip |= LogError(event, "VUID-vkResetEvent-event-03823", |
| "vkResetEvent(): %s was created with VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR.", |
| report_data->FormatHandle(event).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetEventStatus(VkDevice device, VkEvent event) const { |
| bool skip = false; |
| auto event_state = Get<EVENT_STATE>(event); |
| if (event_state) { |
| if (event_state->flags & VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR) { |
| skip |= LogError(event, "VUID-vkGetEventStatus-event-03940", |
| "vkGetEventStatus(): %s was created with VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR.", |
| report_data->FormatHandle(event).c_str()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateSparseMemoryBind(const VkSparseMemoryBind *bind, const char *func_name, const char *parameter_name) const { |
| bool skip = false; |
| if (bind) { |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(bind->memory); |
| if (mem_info) { |
| if (phys_dev_mem_props.memoryTypes[mem_info->alloc_info.memoryTypeIndex].propertyFlags & |
| VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { |
| skip |= |
| LogError(bind->memory, "VUID-VkSparseMemoryBind-memory-01097", |
| "%s: %s memory type has VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT bit set.", func_name, parameter_name); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateQueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo *pBindInfo, |
| VkFence fence) const { |
| auto queue_data = Get<QUEUE_STATE>(queue); |
| auto fence_state = Get<FENCE_STATE>(fence); |
| bool skip = ValidateFenceForSubmit(fence_state.get(), "VUID-vkQueueBindSparse-fence-01114", |
| "VUID-vkQueueBindSparse-fence-01113", "VkQueueBindSparse()"); |
| if (skip) { |
| return true; |
| } |
| |
| const auto queue_flags = physical_device_state->queue_family_properties[queue_data->queueFamilyIndex].queueFlags; |
| if (!(queue_flags & VK_QUEUE_SPARSE_BINDING_BIT)) { |
| skip |= LogError(queue, "VUID-vkQueueBindSparse-queuetype", |
| "vkQueueBindSparse(): a non-memory-management capable queue -- VK_QUEUE_SPARSE_BINDING_BIT not set."); |
| } |
| |
| layer_data::unordered_set<VkSemaphore> signaled_semaphores; |
| layer_data::unordered_set<VkSemaphore> unsignaled_semaphores; |
| layer_data::unordered_set<VkSemaphore> internal_semaphores; |
| auto *vuid_error = IsExtEnabled(device_extensions.vk_khr_timeline_semaphore) ? "VUID-vkQueueBindSparse-pWaitSemaphores-03245" |
| : kVUID_Core_DrawState_QueueForwardProgress; |
| for (uint32_t bind_idx = 0; bind_idx < bindInfoCount; ++bind_idx) { |
| Location outer_loc(Func::vkQueueBindSparse, Struct::VkBindSparseInfo); |
| const VkBindSparseInfo &bind_info = pBindInfo[bind_idx]; |
| |
| auto timeline_semaphore_submit_info = LvlFindInChain<VkTimelineSemaphoreSubmitInfo>(pBindInfo->pNext); |
| for (uint32_t i = 0; i < bind_info.waitSemaphoreCount; ++i) { |
| VkSemaphore semaphore = bind_info.pWaitSemaphores[i]; |
| auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore); |
| if (!semaphore_state) { |
| continue; |
| } |
| switch (semaphore_state->type) { |
| case VK_SEMAPHORE_TYPE_TIMELINE: |
| if (!timeline_semaphore_submit_info) { |
| skip |= LogError(semaphore, "VUID-VkBindSparseInfo-pWaitSemaphores-03246", |
| "VkQueueBindSparse: pBindInfo[%u].pWaitSemaphores[%u] (%s) is a timeline semaphore, but " |
| "pBindInfo[%u] does not include an instance of VkTimelineSemaphoreSubmitInfo", |
| bind_idx, i, report_data->FormatHandle(semaphore).c_str(), bind_idx); |
| } else if (bind_info.waitSemaphoreCount != timeline_semaphore_submit_info->waitSemaphoreValueCount) { |
| skip |= LogError( |
| semaphore, "VUID-VkBindSparseInfo-pNext-03247", |
| "VkQueueBindSparse: pBindInfo[%u].pWaitSemaphores[%u] (%s) is a timeline semaphore, it contains " |
| "an instance of VkTimelineSemaphoreSubmitInfo, but waitSemaphoreValueCount (%u) is different " |
| "than pBindInfo[%u].waitSemaphoreCount (%u)", |
| bind_idx, i, report_data->FormatHandle(semaphore).c_str(), |
| timeline_semaphore_submit_info->waitSemaphoreValueCount, bind_idx, bind_info.waitSemaphoreCount); |
| } else { |
| auto loc = outer_loc.dot(Field::pWaitSemaphoreValues, i); |
| skip |= ValidateMaxTimelineSemaphoreValueDifference( |
| loc, *semaphore_state, timeline_semaphore_submit_info->pWaitSemaphoreValues[i]); |
| } |
| break; |
| case VK_SEMAPHORE_TYPE_BINARY: |
| if ((semaphore_state->Scope() == kSyncScopeInternal || internal_semaphores.count(semaphore))) { |
| if (unsignaled_semaphores.count(semaphore) || |
| (!signaled_semaphores.count(semaphore) && !semaphore_state->CanBeWaited())) { |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| skip |= |
| LogError(objlist, |
| semaphore_state->Scope() == kSyncScopeInternal ? vuid_error |
| : kVUID_Core_DrawState_QueueForwardProgress, |
| "vkQueueBindSparse(): Queue %s is waiting on pBindInfo[%u].pWaitSemaphores[%u] (%s) that " |
| "has no way to be " |
| "signaled.", |
| report_data->FormatHandle(queue).c_str(), bind_idx, i, |
| report_data->FormatHandle(semaphore).c_str()); |
| } else { |
| signaled_semaphores.erase(semaphore); |
| unsignaled_semaphores.insert(semaphore); |
| } |
| } else if (semaphore_state->Scope() == kSyncScopeExternalTemporary) { |
| internal_semaphores.insert(semaphore); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| for (uint32_t i = 0; i < bind_info.signalSemaphoreCount; ++i) { |
| VkSemaphore semaphore = bind_info.pSignalSemaphores[i]; |
| auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore); |
| if (!semaphore_state) { |
| continue; |
| } |
| switch (semaphore_state->type) { |
| case VK_SEMAPHORE_TYPE_TIMELINE: |
| if (!timeline_semaphore_submit_info) { |
| skip |= LogError(semaphore, "VUID-VkBindSparseInfo-pWaitSemaphores-03246", |
| "VkQueueBindSparse: pBindInfo[%u].pSignalSemaphores[%u] (%s) is a timeline semaphore, but " |
| "pBindInfo[%u] does not include an instance of VkTimelineSemaphoreSubmitInfo", |
| bind_idx, i, report_data->FormatHandle(semaphore).c_str(), bind_idx); |
| } else if (bind_info.signalSemaphoreCount != timeline_semaphore_submit_info->signalSemaphoreValueCount) { |
| skip |= LogError( |
| semaphore, "VUID-VkBindSparseInfo-pNext-03248", |
| "VkQueueBindSparse: pBindInfo[%u].pSignalSemaphores[%u] (%s) is a timeline semaphore, it contains " |
| "an instance of VkTimelineSemaphoreSubmitInfo, but signalSemaphoreValueCount (%u) is different " |
| "than pBindInfo[%u].signalSemaphoreCount (%u)", |
| bind_idx, i, report_data->FormatHandle(semaphore).c_str(), |
| timeline_semaphore_submit_info->signalSemaphoreValueCount, bind_idx, bind_info.signalSemaphoreCount); |
| } else if (timeline_semaphore_submit_info->pSignalSemaphoreValues[i] <= semaphore_state->Completed().payload) { |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| skip |= LogError(objlist, "VUID-VkBindSparseInfo-pSignalSemaphores-03249", |
| "VkQueueBindSparse: signal value (0x%" PRIx64 |
| ") in %s must be greater than current timeline semaphore %s value (0x%" PRIx64 |
| ") in pBindInfo[%u].pSignalSemaphores[%u]", |
| semaphore_state->Completed().payload, report_data->FormatHandle(queue).c_str(), |
| report_data->FormatHandle(semaphore).c_str(), |
| timeline_semaphore_submit_info->pSignalSemaphoreValues[i], bind_idx, i); |
| } else { |
| auto loc = outer_loc.dot(Field::pSignalSemaphoreValues, i); |
| skip |= ValidateMaxTimelineSemaphoreValueDifference( |
| loc, *semaphore_state, timeline_semaphore_submit_info->pSignalSemaphoreValues[i]); |
| } |
| break; |
| case VK_SEMAPHORE_TYPE_BINARY: |
| if (semaphore_state->Scope() == kSyncScopeInternal) { |
| if (signaled_semaphores.count(semaphore) || |
| (!(unsignaled_semaphores.count(semaphore)) && !semaphore_state->CanBeSignaled())) { |
| auto last_op = semaphore_state->LastOp(); |
| VkQueue other_queue = last_op && last_op->queue ? last_op->queue->Queue() : VK_NULL_HANDLE; |
| LogObjectList objlist(semaphore); |
| objlist.add(queue); |
| objlist.add(other_queue); |
| skip |= LogError( |
| objlist, kVUID_Core_DrawState_QueueForwardProgress, |
| "vkQueueBindSparse(): %s is signaling pBindInfo[%u].pSignalSemaphores[%u] (%s) that was " |
| "previously signaled by %s but has not since been waited on by any queue.", |
| report_data->FormatHandle(queue).c_str(), bind_idx, i, report_data->FormatHandle(semaphore).c_str(), |
| report_data->FormatHandle(other_queue).c_str()); |
| } else { |
| unsignaled_semaphores.erase(semaphore); |
| signaled_semaphores.insert(semaphore); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (bind_info.pBufferBinds) { |
| for (uint32_t buffer_idx = 0; buffer_idx < bind_info.bufferBindCount; ++buffer_idx) { |
| const VkSparseBufferMemoryBindInfo &buffer_bind = bind_info.pBufferBinds[buffer_idx]; |
| if (buffer_bind.pBinds) { |
| for (uint32_t buffer_bind_idx = 0; buffer_bind_idx < buffer_bind.bindCount; ++buffer_bind_idx) { |
| const VkSparseMemoryBind &memory_bind = buffer_bind.pBinds[buffer_bind_idx]; |
| std::stringstream parameter_name; |
| parameter_name << "pBindInfo[" << bind_idx << "].pBufferBinds[" << buffer_idx << " ].pBinds[" |
| << buffer_bind_idx << "]"; |
| ValidateSparseMemoryBind(&memory_bind, "vkQueueBindSparse()", parameter_name.str().c_str()); |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(memory_bind.memory); |
| if (mem_info) { |
| if (memory_bind.memoryOffset >= mem_info->alloc_info.allocationSize) { |
| skip |= |
| LogError(buffer_bind.buffer, "VUID-VkSparseMemoryBind-memoryOffset-01101", |
| "vkQueueBindSparse(): pBindInfo[%u].pBufferBinds[%u]: memoryOffset is not less than " |
| "the size of memory", |
| bind_idx, buffer_idx); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (bind_info.pImageOpaqueBinds) { |
| for (uint32_t image_opaque_idx = 0; image_opaque_idx < bind_info.bufferBindCount; ++image_opaque_idx) { |
| const VkSparseImageOpaqueMemoryBindInfo &image_opaque_bind = bind_info.pImageOpaqueBinds[image_opaque_idx]; |
| if (image_opaque_bind.pBinds) { |
| for (uint32_t image_opaque_bind_idx = 0; image_opaque_bind_idx < image_opaque_bind.bindCount; |
| ++image_opaque_bind_idx) { |
| const VkSparseMemoryBind &memory_bind = image_opaque_bind.pBinds[image_opaque_bind_idx]; |
| std::stringstream parameter_name; |
| parameter_name << "pBindInfo[" << bind_idx << "].pImageOpaqueBinds[" << image_opaque_idx << " ].pBinds[" |
| << image_opaque_bind_idx << "]"; |
| ValidateSparseMemoryBind(&memory_bind, "vkQueueBindSparse()", parameter_name.str().c_str()); |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(memory_bind.memory); |
| if (mem_info) { |
| if (memory_bind.memoryOffset >= mem_info->alloc_info.allocationSize) { |
| skip |= LogError( |
| image_opaque_bind.image, "VUID-VkSparseMemoryBind-memoryOffset-01101", |
| "vkQueueBindSparse(): pBindInfo[%u].pImageOpaqueBinds[%u]: memoryOffset is not less than " |
| "the size of memory", |
| bind_idx, image_opaque_idx); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (bind_info.pImageBinds) { |
| for (uint32_t image_idx = 0; image_idx < bind_info.imageBindCount; ++image_idx) { |
| const VkSparseImageMemoryBindInfo &image_bind = bind_info.pImageBinds[image_idx]; |
| auto image_state = Get<IMAGE_STATE>(image_bind.image); |
| |
| if (image_state && !(image_state->createInfo.flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT)) { |
| skip |= LogError(image_bind.image, "VUID-VkSparseImageMemoryBindInfo-image-02901", |
| "vkQueueBindSparse(): pBindInfo[%u].pImageBinds[%u]: image must have been created with " |
| "VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT set", |
| bind_idx, image_idx); |
| } |
| |
| if (image_bind.pBinds) { |
| for (uint32_t image_bind_idx = 0; image_bind_idx < image_bind.bindCount; ++image_bind_idx) { |
| const VkSparseImageMemoryBind &memory_bind = image_bind.pBinds[image_bind_idx]; |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(memory_bind.memory); |
| if (mem_info) { |
| if (memory_bind.memoryOffset >= mem_info->alloc_info.allocationSize) { |
| skip |= |
| LogError(image_bind.image, "VUID-VkSparseMemoryBind-memoryOffset-01101", |
| "vkQueueBindSparse(): pBindInfo[%u].pImageBinds[%u]: memoryOffset is not less than " |
| "the size of memory", |
| bind_idx, image_idx); |
| } |
| } |
| |
| if (image_state) { |
| if (memory_bind.subresource.mipLevel >= image_state->createInfo.mipLevels) { |
| skip |= LogError(image_bind.image, "VUID-VkSparseImageMemoryBindInfo-subresource-01722", |
| "vkQueueBindSparse(): pBindInfo[%" PRIu32 "].pImageBinds[%" PRIu32 |
| "].subresource.mipLevel (%" PRIu32 ") is not less than mipLevels (%" PRIu32 |
| ") of image pBindInfo[%" PRIu32 "].pImageBinds[%" PRIu32 "].image.", |
| bind_idx, image_idx, memory_bind.subresource.mipLevel, image_state->createInfo.mipLevels, |
| bind_idx, image_idx); |
| } |
| if (memory_bind.subresource.arrayLayer >= image_state->createInfo.arrayLayers) { |
| skip |= LogError(image_bind.image, "VUID-VkSparseImageMemoryBindInfo-subresource-01723", |
| "vkQueueBindSparse(): pBindInfo[%" PRIu32 "].pImageBinds[%" PRIu32 |
| "].subresource.arrayLayer (%" PRIu32 ") is not less than arrayLayers (%" PRIu32 |
| ") of image pBindInfo[%" PRIu32 "].pImageBinds[%" PRIu32 "].image.", |
| bind_idx, image_idx, memory_bind.subresource.arrayLayer, |
| image_state->createInfo.arrayLayers, |
| bind_idx, image_idx); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo *pSignalInfo, const char *api_name) const { |
| bool skip = false; |
| auto semaphore_state = Get<SEMAPHORE_STATE>(pSignalInfo->semaphore); |
| if (!semaphore_state) { |
| return skip; |
| } |
| if (semaphore_state->type != VK_SEMAPHORE_TYPE_TIMELINE) { |
| skip |= LogError(pSignalInfo->semaphore, "VUID-VkSemaphoreSignalInfo-semaphore-03257", |
| "%s(): semaphore %s must be of VK_SEMAPHORE_TYPE_TIMELINE type.", api_name, |
| report_data->FormatHandle(pSignalInfo->semaphore).c_str()); |
| return skip; |
| } |
| |
| const auto completed = semaphore_state->Completed(); |
| if (completed.payload >= pSignalInfo->value) { |
| skip |= LogError(pSignalInfo->semaphore, "VUID-VkSemaphoreSignalInfo-value-03258", |
| "%s(): value (%" PRIu64 ") must be greater than current semaphore %s value (%" PRIu64 ").", api_name, |
| pSignalInfo->value, report_data->FormatHandle(pSignalInfo->semaphore).c_str(), completed.payload); |
| return skip; |
| } else if (semaphore_state->HasPendingOps()) { |
| // look back for the last signal operation, but there could be pending waits with higher payloads behind it. |
| const auto last_op = semaphore_state->LastOp([](const SEMAPHORE_STATE::SemOp &op) { return op.IsSignal(); }); |
| if (last_op && pSignalInfo->value >= last_op->payload) { |
| skip |= LogError( |
| pSignalInfo->semaphore, "VUID-VkSemaphoreSignalInfo-value-03259", |
| "%s(): value (%" PRIu64 ") must be less than value of any pending signal operation (%" PRIu64 ") for semaphore %s.", |
| api_name, pSignalInfo->value, last_op->payload, report_data->FormatHandle(pSignalInfo->semaphore).c_str()); |
| } |
| } |
| if (!skip) { |
| Location loc(Func::vkSignalSemaphore, Struct::VkSemaphoreSignalInfo, Field::value); |
| skip |= ValidateMaxTimelineSemaphoreValueDifference(loc, *semaphore_state, pSignalInfo->value); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo *pSignalInfo) const { |
| return ValidateSignalSemaphore(device, pSignalInfo, "vkSignalSemaphore"); |
| } |
| |
| bool CoreChecks::PreCallValidateSignalSemaphoreKHR(VkDevice device, const VkSemaphoreSignalInfo *pSignalInfo) const { |
| return ValidateSignalSemaphore(device, pSignalInfo, "vkSignalSemaphoreKHR"); |
| } |
| |
| bool CoreChecks::ValidateImportSemaphore(VkSemaphore semaphore, const char *caller_name) const { |
| bool skip = false; |
| auto sema_node = Get<SEMAPHORE_STATE>(semaphore); |
| if (sema_node) { |
| skip |= ValidateObjectNotInUse(sema_node.get(), caller_name, kVUIDUndefined); |
| } |
| return skip; |
| } |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| bool CoreChecks::PreCallValidateImportSemaphoreWin32HandleKHR( |
| VkDevice device, const VkImportSemaphoreWin32HandleInfoKHR *pImportSemaphoreWin32HandleInfo) const { |
| return ValidateImportSemaphore(pImportSemaphoreWin32HandleInfo->semaphore, "vkImportSemaphoreWin32HandleKHR"); |
| } |
| |
| #endif // VK_USE_PLATFORM_WIN32_KHR |
| |
| bool CoreChecks::PreCallValidateImportSemaphoreFdKHR(VkDevice device, |
| const VkImportSemaphoreFdInfoKHR *pImportSemaphoreFdInfo) const { |
| return ValidateImportSemaphore(pImportSemaphoreFdInfo->semaphore, "vkImportSemaphoreFdKHR"); |
| } |
| |
| bool CoreChecks::ValidateImportFence(VkFence fence, const char *vuid, const char *caller_name) const { |
| auto fence_node = Get<FENCE_STATE>(fence); |
| bool skip = false; |
| if (fence_node && fence_node->Scope() == kSyncScopeInternal && fence_node->State() == FENCE_INFLIGHT) { |
| skip |= |
| LogError(fence, vuid, "%s: Fence %s that is currently in use.", caller_name, report_data->FormatHandle(fence).c_str()); |
| } |
| return skip; |
| } |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| bool CoreChecks::PreCallValidateImportFenceWin32HandleKHR( |
| VkDevice device, const VkImportFenceWin32HandleInfoKHR *pImportFenceWin32HandleInfo) const { |
| return ValidateImportFence(pImportFenceWin32HandleInfo->fence, "VUID-vkImportFenceWin32HandleKHR-fence-04448", |
| "vkImportFenceWin32HandleKHR()"); |
| } |
| #endif // VK_USE_PLATFORM_WIN32_KHR |
| |
| bool CoreChecks::PreCallValidateImportFenceFdKHR(VkDevice device, const VkImportFenceFdInfoKHR *pImportFenceFdInfo) const { |
| return ValidateImportFence(pImportFenceFdInfo->fence, "VUID-vkImportFenceFdKHR-fence-01463", "vkImportFenceFdKHR()"); |
| } |
| |
| static VkImageCreateInfo GetSwapchainImpliedImageCreateInfo(VkSwapchainCreateInfoKHR const *pCreateInfo) { |
| auto result = LvlInitStruct<VkImageCreateInfo>(); |
| |
| if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) { |
| result.flags |= VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT; |
| } |
| if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) result.flags |= VK_IMAGE_CREATE_PROTECTED_BIT; |
| if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { |
| result.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; |
| } |
| |
| result.imageType = VK_IMAGE_TYPE_2D; |
| result.format = pCreateInfo->imageFormat; |
| result.extent.width = pCreateInfo->imageExtent.width; |
| result.extent.height = pCreateInfo->imageExtent.height; |
| result.extent.depth = 1; |
| result.mipLevels = 1; |
| result.arrayLayers = pCreateInfo->imageArrayLayers; |
| result.samples = VK_SAMPLE_COUNT_1_BIT; |
| result.tiling = VK_IMAGE_TILING_OPTIMAL; |
| result.usage = pCreateInfo->imageUsage; |
| result.sharingMode = pCreateInfo->imageSharingMode; |
| result.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount; |
| result.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices; |
| result.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| |
| return result; |
| } |
| |
| bool CoreChecks::IsExtentInsideBounds(VkExtent2D extent, VkExtent2D min, VkExtent2D max) const { |
| if ((extent.width < min.width) || (extent.width > max.width) || (extent.height < min.height) || (extent.height > max.height)) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool CoreChecks::ValidateCreateSwapchain(const char *func_name, VkSwapchainCreateInfoKHR const *pCreateInfo, |
| const SURFACE_STATE *surface_state, const SWAPCHAIN_NODE *old_swapchain_state) const { |
| // All physical devices and queue families are required to be able to present to any native window on Android; require the |
| // application to have established support on any other platform. |
| if (!instance_extensions.vk_khr_android_surface) { |
| // restrict search only to queue families of VkDeviceQueueCreateInfos, not the whole physical device |
| bool is_supported = AnyOf<QUEUE_STATE>([this, surface_state](const QUEUE_STATE &queue_state) { |
| return surface_state->GetQueueSupport(physical_device, queue_state.queueFamilyIndex); |
| }); |
| |
| if (!is_supported) { |
| LogObjectList objs(device); |
| objs.add(surface_state->Handle()); |
| if (LogError(objs, "VUID-VkSwapchainCreateInfoKHR-surface-01270", |
| "%s: pCreateInfo->surface is not supported for presentation by this device.", func_name)) { |
| return true; |
| } |
| } |
| } |
| |
| if (old_swapchain_state) { |
| if (old_swapchain_state->createInfo.surface != pCreateInfo->surface) { |
| if (LogError(pCreateInfo->oldSwapchain, "VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933", |
| "%s: pCreateInfo->oldSwapchain's surface is not pCreateInfo->surface", func_name)) { |
| return true; |
| } |
| } |
| if (old_swapchain_state->retired) { |
| if (LogError(pCreateInfo->oldSwapchain, "VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933", |
| "%s: pCreateInfo->oldSwapchain is retired", func_name)) { |
| return true; |
| } |
| } |
| } |
| |
| if ((pCreateInfo->imageExtent.width == 0) || (pCreateInfo->imageExtent.height == 0)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageExtent-01689", |
| "%s: pCreateInfo->imageExtent = (%d, %d) which is illegal.", func_name, pCreateInfo->imageExtent.width, |
| pCreateInfo->imageExtent.height)) { |
| return true; |
| } |
| } |
| |
| VkSurfaceCapabilitiesKHR capabilities{}; |
| DispatchGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device_state->PhysDev(), pCreateInfo->surface, &capabilities); |
| bool skip = false; |
| VkSurfaceTransformFlagBitsKHR current_transform = capabilities.currentTransform; |
| if ((pCreateInfo->preTransform & current_transform) != pCreateInfo->preTransform) { |
| skip |= LogPerformanceWarning(physical_device, kVUID_Core_Swapchain_PreTransform, |
| "%s: pCreateInfo->preTransform (%s) doesn't match the currentTransform (%s) returned by " |
| "vkGetPhysicalDeviceSurfaceCapabilitiesKHR, the presentation engine will transform the image " |
| "content as part of the presentation operation.", |
| func_name, string_VkSurfaceTransformFlagBitsKHR(pCreateInfo->preTransform), |
| string_VkSurfaceTransformFlagBitsKHR(current_transform)); |
| } |
| |
| const VkPresentModeKHR present_mode = pCreateInfo->presentMode; |
| const bool shared_present_mode = (VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR == present_mode || |
| VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR == present_mode); |
| |
| // Validate pCreateInfo->minImageCount against VkSurfaceCapabilitiesKHR::{min|max}ImageCount: |
| // Shared Present Mode must have a minImageCount of 1 |
| if ((pCreateInfo->minImageCount < capabilities.minImageCount) && !shared_present_mode) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_khr_shared_presentable_image) |
| ? "VUID-VkSwapchainCreateInfoKHR-presentMode-02839" |
| : "VUID-VkSwapchainCreateInfoKHR-minImageCount-01271"; |
| if (LogError(device, vuid, |
| "%s called with minImageCount = %d, which is outside the bounds returned by " |
| "vkGetPhysicalDeviceSurfaceCapabilitiesKHR() (i.e. minImageCount = %d, maxImageCount = %d).", |
| func_name, pCreateInfo->minImageCount, capabilities.minImageCount, capabilities.maxImageCount)) { |
| return true; |
| } |
| } |
| |
| if ((capabilities.maxImageCount > 0) && (pCreateInfo->minImageCount > capabilities.maxImageCount)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-minImageCount-01272", |
| "%s called with minImageCount = %d, which is outside the bounds returned by " |
| "vkGetPhysicalDeviceSurfaceCapabilitiesKHR() (i.e. minImageCount = %d, maxImageCount = %d).", |
| func_name, pCreateInfo->minImageCount, capabilities.minImageCount, capabilities.maxImageCount)) { |
| return true; |
| } |
| } |
| |
| // Validate pCreateInfo->imageExtent against VkSurfaceCapabilitiesKHR::{current|min|max}ImageExtent: |
| if (!IsExtentInsideBounds(pCreateInfo->imageExtent, capabilities.minImageExtent, capabilities.maxImageExtent)) { |
| VkSurfaceCapabilitiesKHR cached_capabilities{}; |
| if (surface_state) { |
| cached_capabilities = surface_state->GetCapabilities(physical_device); |
| } else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) { |
| cached_capabilities = physical_device_state->surfaceless_query_state.capabilities; |
| } |
| if (!IsExtentInsideBounds(pCreateInfo->imageExtent, cached_capabilities.minImageExtent, |
| cached_capabilities.maxImageExtent)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageExtent-01274", |
| "%s called with imageExtent = (%" PRIu32 ",%" PRIu32 "), which is outside the bounds returned by " |
| "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): currentExtent = (%" PRIu32 ",%" PRIu32 |
| "), minImageExtent = (%" PRIu32 ",%" PRIu32 "), " |
| "maxImageExtent = (%" PRIu32 ",%" PRIu32 ").", |
| func_name, pCreateInfo->imageExtent.width, pCreateInfo->imageExtent.height, |
| capabilities.currentExtent.width, capabilities.currentExtent.height, capabilities.minImageExtent.width, |
| capabilities.minImageExtent.height, capabilities.maxImageExtent.width, |
| capabilities.maxImageExtent.height)) { |
| return true; |
| } |
| } |
| } |
| // pCreateInfo->preTransform should have exactly one bit set, and that bit must also be set in |
| // VkSurfaceCapabilitiesKHR::supportedTransforms. |
| if (!pCreateInfo->preTransform || (pCreateInfo->preTransform & (pCreateInfo->preTransform - 1)) || |
| !(pCreateInfo->preTransform & capabilities.supportedTransforms)) { |
| // This is an error situation; one for which we'd like to give the developer a helpful, multi-line error message. Build |
| // it up a little at a time, and then log it: |
| std::string error_string = ""; |
| char str[1024]; |
| // Here's the first part of the message: |
| sprintf(str, "%s called with a non-supported pCreateInfo->preTransform (i.e. %s). Supported values are:\n", func_name, |
| string_VkSurfaceTransformFlagBitsKHR(pCreateInfo->preTransform)); |
| error_string += str; |
| for (int i = 0; i < 32; i++) { |
| // Build up the rest of the message: |
| if ((1 << i) & capabilities.supportedTransforms) { |
| const char *new_str = string_VkSurfaceTransformFlagBitsKHR(static_cast<VkSurfaceTransformFlagBitsKHR>(1 << i)); |
| sprintf(str, " %s\n", new_str); |
| error_string += str; |
| } |
| } |
| // Log the message that we've built up: |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-preTransform-01279", "%s.", error_string.c_str())) return true; |
| } |
| |
| // pCreateInfo->compositeAlpha should have exactly one bit set, and that bit must also be set in |
| // VkSurfaceCapabilitiesKHR::supportedCompositeAlpha |
| if (!pCreateInfo->compositeAlpha || (pCreateInfo->compositeAlpha & (pCreateInfo->compositeAlpha - 1)) || |
| !((pCreateInfo->compositeAlpha) & capabilities.supportedCompositeAlpha)) { |
| // This is an error situation; one for which we'd like to give the developer a helpful, multi-line error message. Build |
| // it up a little at a time, and then log it: |
| std::string error_string = ""; |
| char str[1024]; |
| // Here's the first part of the message: |
| sprintf(str, "%s called with a non-supported pCreateInfo->compositeAlpha (i.e. %s). Supported values are:\n", func_name, |
| string_VkCompositeAlphaFlagBitsKHR(pCreateInfo->compositeAlpha)); |
| error_string += str; |
| for (int i = 0; i < 32; i++) { |
| // Build up the rest of the message: |
| if ((1 << i) & capabilities.supportedCompositeAlpha) { |
| const char *new_str = string_VkCompositeAlphaFlagBitsKHR(static_cast<VkCompositeAlphaFlagBitsKHR>(1 << i)); |
| sprintf(str, " %s\n", new_str); |
| error_string += str; |
| } |
| } |
| // Log the message that we've built up: |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280", "%s.", error_string.c_str())) return true; |
| } |
| // Validate pCreateInfo->imageArrayLayers against VkSurfaceCapabilitiesKHR::maxImageArrayLayers: |
| if (pCreateInfo->imageArrayLayers > capabilities.maxImageArrayLayers) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275", |
| "%s called with a non-supported imageArrayLayers (i.e. %d). Maximum value is %d.", func_name, |
| pCreateInfo->imageArrayLayers, capabilities.maxImageArrayLayers)) { |
| return true; |
| } |
| } |
| const VkImageUsageFlags image_usage = pCreateInfo->imageUsage; |
| // Validate pCreateInfo->imageUsage against VkSurfaceCapabilitiesKHR::supportedUsageFlags: |
| // Shared Present Mode uses different set of capabilities to check imageUsage support |
| if ((image_usage != (image_usage & capabilities.supportedUsageFlags)) && !shared_present_mode) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_khr_shared_presentable_image) |
| ? "VUID-VkSwapchainCreateInfoKHR-presentMode-01427" |
| : "VUID-VkSwapchainCreateInfoKHR-imageUsage-01276"; |
| if (LogError(device, vuid, |
| "%s called with a non-supported pCreateInfo->imageUsage (i.e. 0x%08x). Supported flag bits are 0x%08x.", |
| func_name, image_usage, capabilities.supportedUsageFlags)) { |
| return true; |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_surface_protected_capabilities) && |
| (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR)) { |
| VkPhysicalDeviceSurfaceInfo2KHR surface_info = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR}; |
| surface_info.surface = pCreateInfo->surface; |
| VkSurfaceProtectedCapabilitiesKHR surface_protected_capabilities = {VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR}; |
| VkSurfaceCapabilities2KHR surface_capabilities = {VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR}; |
| surface_capabilities.pNext = &surface_protected_capabilities; |
| DispatchGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device_state->PhysDev(), &surface_info, &surface_capabilities); |
| |
| if (!surface_protected_capabilities.supportsProtected) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-flags-03187", |
| "%s: pCreateInfo->flags contains VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR but the surface " |
| "capabilities does not have VkSurfaceProtectedCapabilitiesKHR.supportsProtected set to VK_TRUE.", |
| func_name)) { |
| return true; |
| } |
| } |
| } |
| |
| // Validate pCreateInfo values with the results of vkGetPhysicalDeviceSurfaceFormatsKHR(): |
| { |
| // Validate pCreateInfo->imageFormat against VkSurfaceFormatKHR::format: |
| bool found_format = false; |
| bool found_color_space = false; |
| bool found_match = false; |
| |
| std::vector<VkSurfaceFormatKHR> formats{}; |
| if (surface_state) { |
| formats = surface_state->GetFormats(physical_device); |
| } else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) { |
| formats = physical_device_state->surfaceless_query_state.formats; |
| } |
| for (const auto &format : formats) { |
| if (pCreateInfo->imageFormat == format.format) { |
| // Validate pCreateInfo->imageColorSpace against VkSurfaceFormatKHR::colorSpace: |
| found_format = true; |
| if (pCreateInfo->imageColorSpace == format.colorSpace) { |
| found_match = true; |
| break; |
| } |
| } else { |
| if (pCreateInfo->imageColorSpace == format.colorSpace) { |
| found_color_space = true; |
| } |
| } |
| } |
| if (!found_match) { |
| if (!found_format) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01273", |
| "%s called with a non-supported pCreateInfo->imageFormat (%s).", func_name, |
| string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } |
| if (!found_color_space) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01273", |
| "%s called with a non-supported pCreateInfo->imageColorSpace (%s).", func_name, |
| string_VkColorSpaceKHR(pCreateInfo->imageColorSpace))) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| // Validate pCreateInfo->presentMode against vkGetPhysicalDeviceSurfacePresentModesKHR(): |
| |
| std::vector<VkPresentModeKHR> present_modes{}; |
| if (surface_state) { |
| present_modes = surface_state->GetPresentModes(physical_device); |
| } else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) { |
| present_modes = physical_device_state->surfaceless_query_state.present_modes; |
| } |
| bool found_match = std::find(present_modes.begin(), present_modes.end(), present_mode) != present_modes.end(); |
| if (!found_match) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-presentMode-01281", |
| "%s called with a non-supported presentMode (i.e. %s).", func_name, string_VkPresentModeKHR(present_mode))) { |
| return true; |
| } |
| } |
| |
| // Validate state for shared presentable case |
| if (shared_present_mode) { |
| if (!IsExtEnabled(device_extensions.vk_khr_shared_presentable_image)) { |
| if (LogError( |
| device, kVUID_Core_DrawState_ExtensionNotEnabled, |
| "%s called with presentMode %s which requires the VK_KHR_shared_presentable_image extension, which has not " |
| "been enabled.", |
| func_name, string_VkPresentModeKHR(present_mode))) { |
| return true; |
| } |
| } else if (pCreateInfo->minImageCount != 1) { |
| if (LogError( |
| device, "VUID-VkSwapchainCreateInfoKHR-minImageCount-01383", |
| "%s called with presentMode %s, but minImageCount value is %d. For shared presentable image, minImageCount " |
| "must be 1.", |
| func_name, string_VkPresentModeKHR(present_mode), pCreateInfo->minImageCount)) { |
| return true; |
| } |
| } |
| |
| VkSharedPresentSurfaceCapabilitiesKHR shared_present_capabilities = LvlInitStruct<VkSharedPresentSurfaceCapabilitiesKHR>(); |
| VkSurfaceCapabilities2KHR capabilities2 = LvlInitStruct<VkSurfaceCapabilities2KHR>(&shared_present_capabilities); |
| VkPhysicalDeviceSurfaceInfo2KHR surface_info = LvlInitStruct<VkPhysicalDeviceSurfaceInfo2KHR>(); |
| surface_info.surface = pCreateInfo->surface; |
| DispatchGetPhysicalDeviceSurfaceCapabilities2KHR(physical_device_state->PhysDev(), &surface_info, &capabilities2); |
| |
| if (image_usage != (image_usage & shared_present_capabilities.sharedPresentSupportedUsageFlags)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageUsage-01384", |
| "%s called with a non-supported pCreateInfo->imageUsage (i.e. 0x%08x). Supported flag bits for %s " |
| "present mode are 0x%08x.", |
| func_name, image_usage, string_VkPresentModeKHR(pCreateInfo->presentMode), |
| shared_present_capabilities.sharedPresentSupportedUsageFlags)) { |
| return true; |
| } |
| } |
| } |
| |
| if ((pCreateInfo->imageSharingMode == VK_SHARING_MODE_CONCURRENT) && pCreateInfo->pQueueFamilyIndices) { |
| bool skip1 = ValidatePhysicalDeviceQueueFamilies(pCreateInfo->queueFamilyIndexCount, pCreateInfo->pQueueFamilyIndices, |
| "vkCreateBuffer", "pCreateInfo->pQueueFamilyIndices", |
| "VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"); |
| if (skip1) return true; |
| } |
| |
| // Validate pCreateInfo->imageUsage against GetPhysicalDeviceFormatProperties |
| const VkFormatProperties3KHR format_properties = GetPDFormatProperties(pCreateInfo->imageFormat); |
| const VkFormatFeatureFlags2KHR tiling_features = format_properties.optimalTilingFeatures; |
| |
| if (tiling_features == 0) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s: pCreateInfo->imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL has no supported format features on this " |
| "physical device.", |
| func_name, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } else if ((image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(tiling_features & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s: pCreateInfo->imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes " |
| "VK_IMAGE_USAGE_SAMPLED_BIT.", |
| func_name, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } else if ((image_usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(tiling_features & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s: pCreateInfo->imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes " |
| "VK_IMAGE_USAGE_STORAGE_BIT.", |
| func_name, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } else if ((image_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) && |
| !(tiling_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s: pCreateInfo->imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes " |
| "VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.", |
| func_name, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } else if ((image_usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && |
| !(tiling_features & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s: pCreateInfo->imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes " |
| "VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| func_name, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } else if ((image_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) && |
| !(tiling_features & |
| (VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR | VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR))) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s: pCreateInfo->imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes " |
| "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT or VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT.", |
| func_name, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } |
| |
| const VkImageCreateInfo image_create_info = GetSwapchainImpliedImageCreateInfo(pCreateInfo); |
| VkImageFormatProperties image_properties = {}; |
| const VkResult image_properties_result = DispatchGetPhysicalDeviceImageFormatProperties( |
| physical_device, image_create_info.format, image_create_info.imageType, image_create_info.tiling, image_create_info.usage, |
| image_create_info.flags, &image_properties); |
| |
| if (image_properties_result != VK_SUCCESS) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "vkGetPhysicalDeviceImageFormatProperties() unexpectedly failed, " |
| "when called for %s validation with following params: " |
| "format: %s, imageType: %s, " |
| "tiling: %s, usage: %s, " |
| "flags: %s.", |
| func_name, string_VkFormat(image_create_info.format), string_VkImageType(image_create_info.imageType), |
| string_VkImageTiling(image_create_info.tiling), string_VkImageUsageFlags(image_create_info.usage).c_str(), |
| string_VkImageCreateFlags(image_create_info.flags).c_str())) { |
| return true; |
| } |
| } |
| |
| // Validate pCreateInfo->imageArrayLayers against VkImageFormatProperties::maxArrayLayers |
| if (pCreateInfo->imageArrayLayers > image_properties.maxArrayLayers) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s called with a non-supported imageArrayLayers (i.e. %d). " |
| "Maximum value returned by vkGetPhysicalDeviceImageFormatProperties() is %d " |
| "for imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL", |
| func_name, pCreateInfo->imageArrayLayers, image_properties.maxArrayLayers, |
| string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } |
| |
| // Validate pCreateInfo->imageExtent against VkImageFormatProperties::maxExtent |
| if ((pCreateInfo->imageExtent.width > image_properties.maxExtent.width) || |
| (pCreateInfo->imageExtent.height > image_properties.maxExtent.height)) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-imageFormat-01778", |
| "%s called with imageExtent = (%d,%d), which is bigger than max extent (%d,%d)" |
| "returned by vkGetPhysicalDeviceImageFormatProperties(): " |
| "for imageFormat %s with tiling VK_IMAGE_TILING_OPTIMAL", |
| func_name, pCreateInfo->imageExtent.width, pCreateInfo->imageExtent.height, image_properties.maxExtent.width, |
| image_properties.maxExtent.height, string_VkFormat(pCreateInfo->imageFormat))) { |
| return true; |
| } |
| } |
| |
| if ((pCreateInfo->flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) && |
| device_group_create_info.physicalDeviceCount == 1) { |
| if (LogError(device, "VUID-VkSwapchainCreateInfoKHR-physicalDeviceCount-01429", |
| "%s called with flags containing VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR" |
| "but logical device was created with VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1", |
| func_name)) { |
| return true; |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) const { |
| auto surface_state = Get<SURFACE_STATE>(pCreateInfo->surface); |
| auto old_swapchain_state = Get<SWAPCHAIN_NODE>(pCreateInfo->oldSwapchain); |
| return ValidateCreateSwapchain("vkCreateSwapchainKHR()", pCreateInfo, surface_state.get(), old_swapchain_state.get()); |
| } |
| |
| void CoreChecks::PreCallRecordDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, |
| const VkAllocationCallbacks *pAllocator) { |
| if (swapchain) { |
| auto swapchain_data = Get<SWAPCHAIN_NODE>(swapchain); |
| if (swapchain_data) { |
| for (const auto &swapchain_image : swapchain_data->images) { |
| if (!swapchain_image.image_state) continue; |
| qfo_release_image_barrier_map.erase(swapchain_image.image_state->image()); |
| } |
| } |
| } |
| StateTracker::PreCallRecordDestroySwapchainKHR(device, swapchain, pAllocator); |
| } |
| |
| void CoreChecks::PostCallRecordGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount, |
| VkImage *pSwapchainImages, VkResult result) { |
| // This function will run twice. The first is to get pSwapchainImageCount. The second is to get pSwapchainImages. |
| // The first time in StateTracker::PostCallRecordGetSwapchainImagesKHR only generates the container's size. |
| // The second time in StateTracker::PostCallRecordGetSwapchainImagesKHR will create VKImage and IMAGE_STATE. |
| |
| // So GlobalImageLayoutMap saving new IMAGE_STATEs has to run in the second time. |
| // pSwapchainImages is not nullptr and it needs to wait until StateTracker::PostCallRecordGetSwapchainImagesKHR. |
| |
| uint32_t new_swapchain_image_index = 0; |
| if (((result == VK_SUCCESS) || (result == VK_INCOMPLETE)) && pSwapchainImages) { |
| auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain); |
| const auto image_vector_size = swapchain_state->images.size(); |
| |
| for (; new_swapchain_image_index < *pSwapchainImageCount; ++new_swapchain_image_index) { |
| if ((new_swapchain_image_index >= image_vector_size) || |
| !swapchain_state->images[new_swapchain_image_index].image_state) { |
| break; |
| }; |
| } |
| } |
| StateTracker::PostCallRecordGetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages, result); |
| |
| if (((result == VK_SUCCESS) || (result == VK_INCOMPLETE)) && pSwapchainImages) { |
| for (; new_swapchain_image_index < *pSwapchainImageCount; ++new_swapchain_image_index) { |
| auto image_state = Get<IMAGE_STATE>(pSwapchainImages[new_swapchain_image_index]); |
| image_state->SetInitialLayoutMap(); |
| } |
| } |
| } |
| |
| bool CoreChecks::PreCallValidateQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) const { |
| bool skip = false; |
| auto queue_state = Get<QUEUE_STATE>(queue); |
| |
| for (uint32_t i = 0; i < pPresentInfo->waitSemaphoreCount; ++i) { |
| auto semaphore_state = Get<SEMAPHORE_STATE>(pPresentInfo->pWaitSemaphores[i]); |
| if (semaphore_state && semaphore_state->type != VK_SEMAPHORE_TYPE_BINARY) { |
| skip |= LogError(pPresentInfo->pWaitSemaphores[i], "VUID-vkQueuePresentKHR-pWaitSemaphores-03267", |
| "vkQueuePresentKHR: pWaitSemaphores[%u] (%s) is not a VK_SEMAPHORE_TYPE_BINARY", i, |
| report_data->FormatHandle(pPresentInfo->pWaitSemaphores[i]).c_str()); |
| } |
| if (semaphore_state && !semaphore_state->CanBeWaited()) { |
| LogObjectList objlist(queue); |
| objlist.add(pPresentInfo->pWaitSemaphores[i]); |
| skip |= LogError(objlist, "VUID-vkQueuePresentKHR-pWaitSemaphores-03268", |
| "vkQueuePresentKHR: Queue %s is waiting on pWaitSemaphores[%u] (%s) that has no way to be signaled.", |
| report_data->FormatHandle(queue).c_str(), i, |
| report_data->FormatHandle(pPresentInfo->pWaitSemaphores[i]).c_str()); |
| } |
| } |
| |
| for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) { |
| auto swapchain_data = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]); |
| if (swapchain_data) { |
| // VU currently is 2-in-1, covers being a valid index and valid layout |
| const char *validation_error = IsExtEnabled(device_extensions.vk_khr_shared_presentable_image) |
| ? "VUID-VkPresentInfoKHR-pImageIndices-01430" |
| : "VUID-VkPresentInfoKHR-pImageIndices-01296"; |
| |
| // Check if index is even possible to be acquired to give better error message |
| if (pPresentInfo->pImageIndices[i] >= swapchain_data->images.size()) { |
| skip |= LogError( |
| pPresentInfo->pSwapchains[i], validation_error, |
| "vkQueuePresentKHR: pSwapchains[%u] image index is too large (%u). There are only %u images in this swapchain.", |
| i, pPresentInfo->pImageIndices[i], static_cast<uint32_t>(swapchain_data->images.size())); |
| } else if (!swapchain_data->images[pPresentInfo->pImageIndices[i]].image_state || |
| !swapchain_data->images[pPresentInfo->pImageIndices[i]].acquired) { |
| skip |= LogError(pPresentInfo->pSwapchains[i], validation_error, |
| "vkQueuePresentKHR: pSwapchains[%" PRIu32 "] image at index %" PRIu32 |
| " was not acquired from the swapchain.", |
| i, pPresentInfo->pImageIndices[i]); |
| } else { |
| const auto *image_state = swapchain_data->images[pPresentInfo->pImageIndices[i]].image_state; |
| assert(image_state); |
| |
| vector<VkImageLayout> layouts; |
| if (FindLayouts(*image_state, layouts)) { |
| for (auto layout : layouts) { |
| if ((layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) && |
| (!IsExtEnabled(device_extensions.vk_khr_shared_presentable_image) || |
| (layout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR))) { |
| skip |= LogError(queue, validation_error, |
| "vkQueuePresentKHR(): pSwapchains[%u] images passed to present must be in layout " |
| "VK_IMAGE_LAYOUT_PRESENT_SRC_KHR or " |
| "VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR but is in %s.", |
| i, string_VkImageLayout(layout)); |
| } |
| } |
| } |
| const auto *display_present_info = LvlFindInChain<VkDisplayPresentInfoKHR>(pPresentInfo->pNext); |
| if (display_present_info) { |
| if (display_present_info->srcRect.offset.x < 0 || display_present_info->srcRect.offset.y < 0 || |
| display_present_info->srcRect.offset.x + display_present_info->srcRect.extent.width > |
| image_state->createInfo.extent.width || |
| display_present_info->srcRect.offset.y + display_present_info->srcRect.extent.height > |
| image_state->createInfo.extent.height) { |
| skip |= LogError(queue, "VUID-VkDisplayPresentInfoKHR-srcRect-01257", |
| "vkQueuePresentKHR(): VkDisplayPresentInfoKHR::srcRect (offset (%" PRIu32 ", %" PRIu32 |
| "), extent (%" PRIu32 ", %" PRIu32 |
| ")) in the pNext chain of VkPresentInfoKHR is not a subset of the image begin presented " |
| "(extent (%" PRIu32 ", %" PRIu32 ")).", |
| display_present_info->srcRect.offset.x, display_present_info->srcRect.offset.y, |
| display_present_info->srcRect.extent.width, display_present_info->srcRect.extent.height, |
| image_state->createInfo.extent.width, image_state->createInfo.extent.height); |
| } |
| } |
| } |
| |
| // All physical devices and queue families are required to be able to present to any native window on Android |
| if (!instance_extensions.vk_khr_android_surface) { |
| auto surface_state = Get<SURFACE_STATE>(swapchain_data->createInfo.surface); |
| if (!surface_state->GetQueueSupport(physical_device, queue_state->queueFamilyIndex)) { |
| skip |= LogError( |
| pPresentInfo->pSwapchains[i], "VUID-vkQueuePresentKHR-pSwapchains-01292", |
| "vkQueuePresentKHR: Presenting pSwapchains[%u] image on queue that cannot present to this surface.", i); |
| } |
| } |
| } |
| } |
| if (pPresentInfo->pNext) { |
| // Verify ext struct |
| const auto *present_regions = LvlFindInChain<VkPresentRegionsKHR>(pPresentInfo->pNext); |
| if (present_regions) { |
| for (uint32_t i = 0; i < present_regions->swapchainCount; ++i) { |
| auto swapchain_data = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]); |
| assert(swapchain_data); |
| VkPresentRegionKHR region = present_regions->pRegions[i]; |
| for (uint32_t j = 0; j < region.rectangleCount; ++j) { |
| VkRectLayerKHR rect = region.pRectangles[j]; |
| // Swap offsets and extents for 90 or 270 degree preTransform rotation |
| if (swapchain_data->createInfo.preTransform & |
| (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR | |
| VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR | |
| VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR)) { |
| std::swap(rect.offset.x, rect.offset.y); |
| std::swap(rect.extent.width, rect.extent.height); |
| } |
| if ((rect.offset.x + rect.extent.width) > swapchain_data->createInfo.imageExtent.width) { |
| skip |= |
| LogError(pPresentInfo->pSwapchains[i], "VUID-VkRectLayerKHR-offset-04864", |
| "vkQueuePresentKHR(): For VkPresentRegionKHR down pNext chain, pRegion[%i].pRectangles[%i], " |
| "the sum of offset.x (%i) and extent.width (%i) after applying preTransform (%s) is greater " |
| "than the corresponding swapchain's imageExtent.width (%i).", |
| i, j, rect.offset.x, rect.extent.width, |
| string_VkSurfaceTransformFlagBitsKHR(swapchain_data->createInfo.preTransform), |
| swapchain_data->createInfo.imageExtent.width); |
| } |
| if ((rect.offset.y + rect.extent.height) > swapchain_data->createInfo.imageExtent.height) { |
| skip |= |
| LogError(pPresentInfo->pSwapchains[i], "VUID-VkRectLayerKHR-offset-04864", |
| "vkQueuePresentKHR(): For VkPresentRegionKHR down pNext chain, pRegion[%i].pRectangles[%i], " |
| "the sum of offset.y (%i) and extent.height (%i) after applying preTransform (%s) is greater " |
| "than the corresponding swapchain's imageExtent.height (%i).", |
| i, j, rect.offset.y, rect.extent.height, |
| string_VkSurfaceTransformFlagBitsKHR(swapchain_data->createInfo.preTransform), |
| swapchain_data->createInfo.imageExtent.height); |
| } |
| if (rect.layer > swapchain_data->createInfo.imageArrayLayers) { |
| skip |= LogError( |
| pPresentInfo->pSwapchains[i], "VUID-VkRectLayerKHR-layer-01262", |
| "vkQueuePresentKHR(): For VkPresentRegionKHR down pNext chain, pRegion[%i].pRectangles[%i], the layer " |
| "(%i) is greater than the corresponding swapchain's imageArrayLayers (%i).", |
| i, j, rect.layer, swapchain_data->createInfo.imageArrayLayers); |
| } |
| } |
| } |
| } |
| |
| const auto *present_times_info = LvlFindInChain<VkPresentTimesInfoGOOGLE>(pPresentInfo->pNext); |
| if (present_times_info) { |
| if (pPresentInfo->swapchainCount != present_times_info->swapchainCount) { |
| skip |= |
| LogError(pPresentInfo->pSwapchains[0], "VUID-VkPresentTimesInfoGOOGLE-swapchainCount-01247", |
| "vkQueuePresentKHR(): VkPresentTimesInfoGOOGLE.swapchainCount is %i but pPresentInfo->swapchainCount " |
| "is %i. For VkPresentTimesInfoGOOGLE down pNext chain of VkPresentInfoKHR, " |
| "VkPresentTimesInfoGOOGLE.swapchainCount must equal VkPresentInfoKHR.swapchainCount.", |
| present_times_info->swapchainCount, pPresentInfo->swapchainCount); |
| } |
| } |
| |
| const auto *present_id_info = LvlFindInChain<VkPresentIdKHR>(pPresentInfo->pNext); |
| if (present_id_info) { |
| if (!enabled_features.present_id_features.presentId) { |
| for (uint32_t i = 0; i < present_id_info->swapchainCount; i++) { |
| if (present_id_info->pPresentIds[i] != 0) { |
| skip |= |
| LogError(pPresentInfo->pSwapchains[0], "VUID-VkPresentInfoKHR-pNext-06235", |
| "vkQueuePresentKHR(): presentId feature is not enabled and VkPresentIdKHR::pPresentId[%" PRIu32 |
| "] = %" PRIu64 " when only NULL values are allowed", |
| i, present_id_info->pPresentIds[i]); |
| } |
| } |
| } |
| if (pPresentInfo->swapchainCount != present_id_info->swapchainCount) { |
| skip |= LogError(pPresentInfo->pSwapchains[0], "VUID-VkPresentIdKHR-swapchainCount-04998", |
| "vkQueuePresentKHR(): VkPresentIdKHR.swapchainCount is %" PRIu32 |
| " but pPresentInfo->swapchainCount is %" PRIu32 |
| ". VkPresentIdKHR.swapchainCount must be the same value as VkPresentInfoKHR::swapchainCount", |
| present_id_info->swapchainCount, pPresentInfo->swapchainCount); |
| } |
| for (uint32_t i = 0; i < present_id_info->swapchainCount; i++) { |
| auto swapchain_state = Get<SWAPCHAIN_NODE>(pPresentInfo->pSwapchains[i]); |
| if ((present_id_info->pPresentIds[i] != 0) && |
| (present_id_info->pPresentIds[i] <= swapchain_state->max_present_id)) { |
| skip |= LogError(pPresentInfo->pSwapchains[i], "VUID-VkPresentIdKHR-presentIds-04999", |
| "vkQueuePresentKHR(): VkPresentIdKHR.pPresentId[%" PRIu32 "] is %" PRIu64 |
| " and the largest presentId sent for this swapchain is %" PRIu64 |
| ". Each presentIds entry must be greater than any previous presentIds entry passed for the " |
| "associated pSwapchains entry", |
| i, present_id_info->pPresentIds[i], swapchain_state->max_present_id); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateSharedSwapchainsKHR(VkDevice device, uint32_t swapchainCount, |
| const VkSwapchainCreateInfoKHR *pCreateInfos, |
| const VkAllocationCallbacks *pAllocator, |
| VkSwapchainKHR *pSwapchains) const { |
| bool skip = false; |
| if (pCreateInfos) { |
| for (uint32_t i = 0; i < swapchainCount; i++) { |
| auto surface_state = Get<SURFACE_STATE>(pCreateInfos[i].surface); |
| auto old_swapchain_state = Get<SWAPCHAIN_NODE>(pCreateInfos[i].oldSwapchain); |
| std::stringstream func_name; |
| func_name << "vkCreateSharedSwapchainsKHR[" << swapchainCount << "]()"; |
| skip |= |
| ValidateCreateSwapchain(func_name.str().c_str(), &pCreateInfos[i], surface_state.get(), old_swapchain_state.get()); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateAcquireNextImage(VkDevice device, const AcquireVersion version, VkSwapchainKHR swapchain, uint64_t timeout, |
| VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex, const char *func_name, |
| const char *semaphore_type_vuid) const { |
| bool skip = false; |
| |
| auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore); |
| if (semaphore_state) { |
| if (semaphore_state->type != VK_SEMAPHORE_TYPE_BINARY) { |
| skip |= LogError(semaphore, semaphore_type_vuid, "%s: %s is not a VK_SEMAPHORE_TYPE_BINARY", func_name, |
| report_data->FormatHandle(semaphore).c_str()); |
| } else if (semaphore_state->Scope() == kSyncScopeInternal) { |
| auto last_op = semaphore_state->LastOp(); |
| // TODO: VUIDs 01779 and 01781 cover the case where there are pending wait or signal operations on the |
| // semaphore. But we don't currently have a good enough way to track when acquire & present operations |
| // are completed. So it is possible to get in a condition where the semaphore is doing |
| // acquire / wait / acquire and the first acquire (and thus the wait) have completed, but our state |
| // isn't aware of it yet. This results in MANY false positives. |
| if (!last_op && !semaphore_state->Completed().CanBeSignaled()) { |
| const char *vuid = version == ACQUIRE_VERSION_2 ? "VUID-VkAcquireNextImageInfoKHR-semaphore-01288" |
| : "VUID-vkAcquireNextImageKHR-semaphore-01286"; |
| skip |= LogError(semaphore, vuid, "%s: Semaphore must not be currently signaled.", func_name); |
| } |
| } |
| } |
| |
| auto fence_state = Get<FENCE_STATE>(fence); |
| if (fence_state) { |
| skip |= ValidateFenceForSubmit(fence_state.get(), "VUID-vkAcquireNextImageKHR-fence-01287", |
| "VUID-vkAcquireNextImageKHR-fence-01287", "vkAcquireNextImageKHR()"); |
| } |
| |
| auto swapchain_data = Get<SWAPCHAIN_NODE>(swapchain); |
| if (swapchain_data) { |
| if (swapchain_data->retired) { |
| const char *vuid = version == ACQUIRE_VERSION_2 ? "VUID-VkAcquireNextImageInfoKHR-swapchain-01675" |
| : "VUID-vkAcquireNextImageKHR-swapchain-01285"; |
| skip |= LogError(swapchain, vuid, |
| "%s: This swapchain has been retired. The application can still present any images it " |
| "has acquired, but cannot acquire any more.", |
| func_name); |
| } |
| |
| const uint32_t acquired_images = swapchain_data->acquired_images; |
| const uint32_t swapchain_image_count = static_cast<uint32_t>(swapchain_data->images.size()); |
| |
| VkSurfaceCapabilitiesKHR caps{}; |
| if (swapchain_data->surface) { |
| caps = swapchain_data->surface->GetCapabilities(physical_device); |
| } else if (IsExtEnabled(instance_extensions.vk_google_surfaceless_query)) { |
| caps = physical_device_state->surfaceless_query_state.capabilities; |
| } |
| const auto min_image_count = caps.minImageCount; |
| const bool too_many_already_acquired = acquired_images > swapchain_image_count - min_image_count; |
| if (timeout == UINT64_MAX && too_many_already_acquired) { |
| const char *vuid = version == ACQUIRE_VERSION_2 ? "VUID-vkAcquireNextImage2KHR-swapchain-01803" |
| : "VUID-vkAcquireNextImageKHR-swapchain-01802"; |
| const uint32_t acquirable = swapchain_image_count - min_image_count + 1; |
| skip |= LogError(swapchain, vuid, |
| "%s: Application has already previously acquired %" PRIu32 " image%s from swapchain. Only %" PRIu32 |
| " %s available to be acquired using a timeout of UINT64_MAX (given the swapchain has %" PRIu32 |
| ", and VkSurfaceCapabilitiesKHR::minImageCount is %" PRIu32 ").", |
| func_name, acquired_images, acquired_images > 1 ? "s" : "", acquirable, acquirable > 1 ? "are" : "is", |
| swapchain_image_count, min_image_count); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, |
| VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex) const { |
| return ValidateAcquireNextImage(device, ACQUIRE_VERSION_1, swapchain, timeout, semaphore, fence, pImageIndex, |
| "vkAcquireNextImageKHR", "VUID-vkAcquireNextImageKHR-semaphore-03265"); |
| } |
| |
| bool CoreChecks::PreCallValidateAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR *pAcquireInfo, |
| uint32_t *pImageIndex) const { |
| bool skip = false; |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(pAcquireInfo->deviceMask, pAcquireInfo->swapchain, |
| "VUID-VkAcquireNextImageInfoKHR-deviceMask-01290"); |
| skip |= ValidateDeviceMaskToZero(pAcquireInfo->deviceMask, pAcquireInfo->swapchain, |
| "VUID-VkAcquireNextImageInfoKHR-deviceMask-01291"); |
| skip |= ValidateAcquireNextImage(device, ACQUIRE_VERSION_2, pAcquireInfo->swapchain, pAcquireInfo->timeout, |
| pAcquireInfo->semaphore, pAcquireInfo->fence, pImageIndex, "vkAcquireNextImage2KHR", |
| "VUID-VkAcquireNextImageInfoKHR-semaphore-03266"); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateWaitForPresentKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t presentId, uint64_t timeout) const { |
| bool skip = false; |
| if (!enabled_features.present_wait_features.presentWait) { |
| skip |= LogError(swapchain, "VUID-vkWaitForPresentKHR-presentWait-06234", |
| "vkWaitForPresentKHR(): VkWaitForPresent called but presentWait feature is not enabled"); |
| } |
| auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain); |
| if (swapchain_state) { |
| if (swapchain_state->retired) { |
| skip |= LogError(swapchain, "VUID-vkWaitForPresentKHR-swapchain-04997", |
| "vkWaitForPresentKHR() called with a retired swapchain."); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, |
| const VkAllocationCallbacks *pAllocator) const { |
| auto surface_state = Get<SURFACE_STATE>(surface); |
| bool skip = false; |
| if ((surface_state) && (surface_state->swapchain)) { |
| skip |= LogError(instance, "VUID-vkDestroySurfaceKHR-surface-01266", |
| "vkDestroySurfaceKHR() called before its associated VkSwapchainKHR was destroyed."); |
| } |
| return skip; |
| } |
| |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceWaylandPresentationSupportKHR(VkPhysicalDevice physicalDevice, |
| uint32_t queueFamilyIndex, |
| struct wl_display *display) const { |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex, |
| "VUID-vkGetPhysicalDeviceWaylandPresentationSupportKHR-queueFamilyIndex-01306", |
| "vkGetPhysicalDeviceWaylandPresentationSupportKHR", "queueFamilyIndex"); |
| } |
| #endif // VK_USE_PLATFORM_WAYLAND_KHR |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice physicalDevice, |
| uint32_t queueFamilyIndex) const { |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex, |
| "VUID-vkGetPhysicalDeviceWin32PresentationSupportKHR-queueFamilyIndex-01309", |
| "vkGetPhysicalDeviceWin32PresentationSupportKHR", "queueFamilyIndex"); |
| } |
| #endif // VK_USE_PLATFORM_WIN32_KHR |
| |
| #ifdef VK_USE_PLATFORM_XCB_KHR |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceXcbPresentationSupportKHR(VkPhysicalDevice physicalDevice, |
| uint32_t queueFamilyIndex, xcb_connection_t *connection, |
| xcb_visualid_t visual_id) const { |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex, |
| "VUID-vkGetPhysicalDeviceXcbPresentationSupportKHR-queueFamilyIndex-01312", |
| "vkGetPhysicalDeviceXcbPresentationSupportKHR", "queueFamilyIndex"); |
| } |
| #endif // VK_USE_PLATFORM_XCB_KHR |
| |
| #ifdef VK_USE_PLATFORM_XLIB_KHR |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceXlibPresentationSupportKHR(VkPhysicalDevice physicalDevice, |
| uint32_t queueFamilyIndex, Display *dpy, |
| VisualID visualID) const { |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex, |
| "VUID-vkGetPhysicalDeviceXlibPresentationSupportKHR-queueFamilyIndex-01315", |
| "vkGetPhysicalDeviceXlibPresentationSupportKHR", "queueFamilyIndex"); |
| } |
| #endif // VK_USE_PLATFORM_XLIB_KHR |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, |
| VkSurfaceKHR surface, VkBool32 *pSupported) const { |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| return ValidateQueueFamilyIndex(pd_state.get(), queueFamilyIndex, |
| "VUID-vkGetPhysicalDeviceSurfaceSupportKHR-queueFamilyIndex-01269", |
| "vkGetPhysicalDeviceSurfaceSupportKHR", "queueFamilyIndex"); |
| } |
| |
| bool CoreChecks::ValidateDescriptorUpdateTemplate(const char *func_name, |
| const VkDescriptorUpdateTemplateCreateInfo *pCreateInfo) const { |
| bool skip = false; |
| auto layout = Get<cvdescriptorset::DescriptorSetLayout>(pCreateInfo->descriptorSetLayout); |
| if (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET == pCreateInfo->templateType && !layout) { |
| skip |= LogError(pCreateInfo->descriptorSetLayout, "VUID-VkDescriptorUpdateTemplateCreateInfo-templateType-00350", |
| "%s: Invalid pCreateInfo->descriptorSetLayout (%s)", func_name, |
| report_data->FormatHandle(pCreateInfo->descriptorSetLayout).c_str()); |
| } else if (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR == pCreateInfo->templateType) { |
| auto bind_point = pCreateInfo->pipelineBindPoint; |
| bool valid_bp = (bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) || (bind_point == VK_PIPELINE_BIND_POINT_COMPUTE) || |
| (bind_point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR); |
| if (!valid_bp) { |
| skip |= |
| LogError(device, "VUID-VkDescriptorUpdateTemplateCreateInfo-templateType-00351", |
| "%s: Invalid pCreateInfo->pipelineBindPoint (%" PRIu32 ").", func_name, static_cast<uint32_t>(bind_point)); |
| } |
| auto pipeline_layout = Get<PIPELINE_LAYOUT_STATE>(pCreateInfo->pipelineLayout); |
| if (!pipeline_layout) { |
| skip |= LogError(pCreateInfo->pipelineLayout, "VUID-VkDescriptorUpdateTemplateCreateInfo-templateType-00352", |
| "%s: Invalid pCreateInfo->pipelineLayout (%s)", func_name, |
| report_data->FormatHandle(pCreateInfo->pipelineLayout).c_str()); |
| } else { |
| const uint32_t pd_set = pCreateInfo->set; |
| if ((pd_set >= pipeline_layout->set_layouts.size()) || !pipeline_layout->set_layouts[pd_set] || |
| !pipeline_layout->set_layouts[pd_set]->IsPushDescriptor()) { |
| skip |= LogError(pCreateInfo->pipelineLayout, "VUID-VkDescriptorUpdateTemplateCreateInfo-templateType-00353", |
| "%s: pCreateInfo->set (%" PRIu32 |
| ") does not refer to the push descriptor set layout for pCreateInfo->pipelineLayout (%s).", |
| func_name, pd_set, report_data->FormatHandle(pCreateInfo->pipelineLayout).c_str()); |
| } |
| } |
| } else if (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET == pCreateInfo->templateType) { |
| for (const auto &binding : layout->GetBindings()) { |
| if (binding.descriptorType == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) { |
| skip |= LogError( |
| device, "VUID-VkDescriptorUpdateTemplateCreateInfo-templateType-04615", |
| "%s: pCreateInfo->templateType is VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, but " |
| "pCreateInfo->descriptorSetLayout contains a binding with descriptor type VK_DESCRIPTOR_TYPE_MUTABLE_VALVE.", |
| func_name); |
| } |
| } |
| } |
| for (uint32_t i = 0; i < pCreateInfo->descriptorUpdateEntryCount; ++i) { |
| const auto &descriptor_update = pCreateInfo->pDescriptorUpdateEntries[i]; |
| if (descriptor_update.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { |
| if (descriptor_update.dstArrayElement & 3) { |
| skip |= LogError(pCreateInfo->pipelineLayout, "VUID-VkDescriptorUpdateTemplateEntry-descriptor-02226", |
| "%s: pCreateInfo->pDescriptorUpdateEntries[%" PRIu32 |
| "] has descriptorType VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, but dstArrayElement (%" PRIu32 ") is not a " |
| "multiple of 4).", |
| func_name, i, descriptor_update.dstArrayElement); |
| } |
| if (descriptor_update.descriptorCount & 3) { |
| skip |= LogError(pCreateInfo->pipelineLayout, "VUID-VkDescriptorUpdateTemplateEntry-descriptor-02227", |
| "%s: pCreateInfo->pDescriptorUpdateEntries[%" PRIu32 |
| "] has descriptorType VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, but descriptorCount (%" PRIu32 ")is not a " |
| "multiple of 4).", |
| func_name, i, descriptor_update.descriptorCount); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateDescriptorUpdateTemplate(VkDevice device, |
| const VkDescriptorUpdateTemplateCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkDescriptorUpdateTemplate *pDescriptorUpdateTemplate) const { |
| bool skip = ValidateDescriptorUpdateTemplate("vkCreateDescriptorUpdateTemplate()", pCreateInfo); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateDescriptorUpdateTemplateKHR(VkDevice device, |
| const VkDescriptorUpdateTemplateCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkDescriptorUpdateTemplate *pDescriptorUpdateTemplate) const { |
| bool skip = ValidateDescriptorUpdateTemplate("vkCreateDescriptorUpdateTemplateKHR()", pCreateInfo); |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateUpdateDescriptorSetWithTemplate(VkDescriptorSet descriptorSet, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate, |
| const void *pData) const { |
| bool skip = false; |
| auto template_state = Get<UPDATE_TEMPLATE_STATE>(descriptorUpdateTemplate); |
| // Object tracker will report errors for invalid descriptorUpdateTemplate values, avoiding a crash in release builds |
| // but retaining the assert as template support is new enough to want to investigate these in debug builds. |
| assert(template_state); |
| // TODO: Validate template push descriptor updates |
| if (template_state->create_info.templateType == VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET) { |
| skip = ValidateUpdateDescriptorSetsWithTemplateKHR(descriptorSet, template_state.get(), pData); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateUpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate, |
| const void *pData) const { |
| return ValidateUpdateDescriptorSetWithTemplate(descriptorSet, descriptorUpdateTemplate, pData); |
| } |
| |
| bool CoreChecks::PreCallValidateUpdateDescriptorSetWithTemplateKHR(VkDevice device, VkDescriptorSet descriptorSet, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate, |
| const void *pData) const { |
| return ValidateUpdateDescriptorSetWithTemplate(descriptorSet, descriptorUpdateTemplate, pData); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdPushDescriptorSetWithTemplateKHR(VkCommandBuffer commandBuffer, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate, |
| VkPipelineLayout layout, uint32_t set, |
| const void *pData) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| const char *const func_name = "vkPushDescriptorSetWithTemplateKHR()"; |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_PUSHDESCRIPTORSETWITHTEMPLATEKHR); |
| |
| auto layout_data = Get<PIPELINE_LAYOUT_STATE>(layout); |
| const auto dsl = layout_data ? layout_data->GetDsl(set) : nullptr; |
| // Validate the set index points to a push descriptor set and is in range |
| if (dsl) { |
| if (!dsl->IsPushDescriptor()) { |
| skip = LogError(layout, "VUID-vkCmdPushDescriptorSetKHR-set-00365", |
| "%s: Set index %" PRIu32 " does not match push descriptor set layout index for %s.", func_name, set, |
| report_data->FormatHandle(layout).c_str()); |
| } |
| } else if (layout_data && (set >= layout_data->set_layouts.size())) { |
| skip = LogError(layout, "VUID-vkCmdPushDescriptorSetKHR-set-00364", |
| "%s: Set index %" PRIu32 " is outside of range for %s (set < %" PRIu32 ").", func_name, set, |
| report_data->FormatHandle(layout).c_str(), static_cast<uint32_t>(layout_data->set_layouts.size())); |
| } |
| |
| auto template_state = Get<UPDATE_TEMPLATE_STATE>(descriptorUpdateTemplate); |
| if (template_state) { |
| const auto &template_ci = template_state->create_info; |
| static const std::map<VkPipelineBindPoint, std::string> bind_errors = { |
| std::make_pair(VK_PIPELINE_BIND_POINT_GRAPHICS, "VUID-vkCmdPushDescriptorSetWithTemplateKHR-commandBuffer-00366"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_COMPUTE, "VUID-vkCmdPushDescriptorSetWithTemplateKHR-commandBuffer-00366"), |
| std::make_pair(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, |
| "VUID-vkCmdPushDescriptorSetWithTemplateKHR-commandBuffer-00366")}; |
| skip |= ValidatePipelineBindPoint(cb_state.get(), template_ci.pipelineBindPoint, func_name, bind_errors); |
| |
| if (template_ci.templateType != VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR) { |
| skip |= LogError(cb_state->commandBuffer(), kVUID_Core_PushDescriptorUpdate_TemplateType, |
| "%s: descriptorUpdateTemplate %s was not created with flag " |
| "VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR.", |
| func_name, report_data->FormatHandle(descriptorUpdateTemplate).c_str()); |
| } |
| if (template_ci.set != set) { |
| skip |= LogError(cb_state->commandBuffer(), kVUID_Core_PushDescriptorUpdate_Template_SetMismatched, |
| "%s: descriptorUpdateTemplate %s created with set %" PRIu32 |
| " does not match command parameter set %" PRIu32 ".", |
| func_name, report_data->FormatHandle(descriptorUpdateTemplate).c_str(), template_ci.set, set); |
| } |
| auto template_layout = Get<PIPELINE_LAYOUT_STATE>(template_ci.pipelineLayout); |
| if (!CompatForSet(set, layout_data.get(), template_layout.get())) { |
| LogObjectList objlist(cb_state->commandBuffer()); |
| objlist.add(descriptorUpdateTemplate); |
| objlist.add(template_ci.pipelineLayout); |
| objlist.add(layout); |
| skip |= LogError(objlist, kVUID_Core_PushDescriptorUpdate_Template_LayoutMismatched, |
| "%s: descriptorUpdateTemplate %s created with %s is incompatible with command parameter " |
| "%s for set %" PRIu32, |
| func_name, report_data->FormatHandle(descriptorUpdateTemplate).c_str(), |
| report_data->FormatHandle(template_ci.pipelineLayout).c_str(), |
| report_data->FormatHandle(layout).c_str(), set); |
| } |
| } |
| |
| if (dsl && template_state) { |
| // Create an empty proxy in order to use the existing descriptor set update validation |
| cvdescriptorset::DescriptorSet proxy_ds(VK_NULL_HANDLE, nullptr, dsl, 0, this); |
| // Decode the template into a set of write updates |
| cvdescriptorset::DecodedTemplateUpdate decoded_template(this, VK_NULL_HANDLE, template_state.get(), pData, |
| dsl->GetDescriptorSetLayout()); |
| // Validate the decoded update against the proxy_ds |
| skip |= ValidatePushDescriptorsUpdate(&proxy_ds, static_cast<uint32_t>(decoded_template.desc_writes.size()), |
| decoded_template.desc_writes.data(), func_name); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(VkPhysicalDevice physicalDevice, uint32_t planeIndex, |
| const char *api_name) const { |
| bool skip = false; |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| if (pd_state->vkGetPhysicalDeviceDisplayPlanePropertiesKHR_called) { |
| if (planeIndex >= pd_state->display_plane_property_count) { |
| skip |= LogError(physicalDevice, "VUID-vkGetDisplayPlaneSupportedDisplaysKHR-planeIndex-01249", |
| "%s(): planeIndex (%u) must be in the range [0, %d] that was returned by " |
| "vkGetPhysicalDeviceDisplayPlanePropertiesKHR " |
| "or vkGetPhysicalDeviceDisplayPlaneProperties2KHR. Do you have the plane index hardcoded?", |
| api_name, planeIndex, pd_state->display_plane_property_count - 1); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice, uint32_t planeIndex, |
| uint32_t *pDisplayCount, VkDisplayKHR *pDisplays) const { |
| bool skip = false; |
| skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, planeIndex, |
| "vkGetDisplayPlaneSupportedDisplaysKHR"); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, |
| uint32_t planeIndex, |
| VkDisplayPlaneCapabilitiesKHR *pCapabilities) const { |
| bool skip = false; |
| skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, planeIndex, "vkGetDisplayPlaneCapabilitiesKHR"); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physicalDevice, |
| const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo, |
| VkDisplayPlaneCapabilities2KHR *pCapabilities) const { |
| bool skip = false; |
| skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, pDisplayPlaneInfo->planeIndex, |
| "vkGetDisplayPlaneCapabilities2KHR"); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateDisplayPlaneSurfaceKHR(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkSurfaceKHR *pSurface) const { |
| bool skip = false; |
| const VkDisplayModeKHR display_mode = pCreateInfo->displayMode; |
| const uint32_t plane_index = pCreateInfo->planeIndex; |
| |
| if (pCreateInfo->alphaMode == VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR) { |
| const float global_alpha = pCreateInfo->globalAlpha; |
| if ((global_alpha > 1.0f) || (global_alpha < 0.0f)) { |
| skip |= LogError( |
| display_mode, "VUID-VkDisplaySurfaceCreateInfoKHR-alphaMode-01254", |
| "vkCreateDisplayPlaneSurfaceKHR(): alphaMode is VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR but globalAlpha is %f.", |
| global_alpha); |
| } |
| } |
| |
| auto dm_state = Get<DISPLAY_MODE_STATE>(display_mode); |
| if (dm_state != nullptr) { |
| // Get physical device from VkDisplayModeKHR state tracking |
| const VkPhysicalDevice physical_device = dm_state->physical_device; |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physical_device); |
| VkPhysicalDeviceProperties device_properties = {}; |
| DispatchGetPhysicalDeviceProperties(physical_device, &device_properties); |
| |
| const uint32_t width = pCreateInfo->imageExtent.width; |
| const uint32_t height = pCreateInfo->imageExtent.height; |
| if (width >= device_properties.limits.maxImageDimension2D) { |
| skip |= LogError(display_mode, "VUID-VkDisplaySurfaceCreateInfoKHR-width-01256", |
| "vkCreateDisplayPlaneSurfaceKHR(): width (%" PRIu32 |
| ") exceeds device limit maxImageDimension2D (%" PRIu32 ").", |
| width, device_properties.limits.maxImageDimension2D); |
| } |
| if (height >= device_properties.limits.maxImageDimension2D) { |
| skip |= LogError(display_mode, "VUID-VkDisplaySurfaceCreateInfoKHR-width-01256", |
| "vkCreateDisplayPlaneSurfaceKHR(): height (%" PRIu32 |
| ") exceeds device limit maxImageDimension2D (%" PRIu32 ").", |
| height, device_properties.limits.maxImageDimension2D); |
| } |
| |
| if (pd_state->vkGetPhysicalDeviceDisplayPlanePropertiesKHR_called) { |
| if (plane_index >= pd_state->display_plane_property_count) { |
| skip |= |
| LogError(display_mode, "VUID-VkDisplaySurfaceCreateInfoKHR-planeIndex-01252", |
| "vkCreateDisplayPlaneSurfaceKHR(): planeIndex (%u) must be in the range [0, %d] that was returned by " |
| "vkGetPhysicalDeviceDisplayPlanePropertiesKHR " |
| "or vkGetPhysicalDeviceDisplayPlaneProperties2KHR. Do you have the plane index hardcoded?", |
| plane_index, pd_state->display_plane_property_count - 1); |
| } else { |
| // call here once we know the plane index used is a valid plane index |
| VkDisplayPlaneCapabilitiesKHR plane_capabilities; |
| DispatchGetDisplayPlaneCapabilitiesKHR(physical_device, display_mode, plane_index, &plane_capabilities); |
| |
| if ((pCreateInfo->alphaMode & plane_capabilities.supportedAlpha) == 0) { |
| skip |= LogError(display_mode, "VUID-VkDisplaySurfaceCreateInfoKHR-alphaMode-01255", |
| "vkCreateDisplayPlaneSurfaceKHR(): alphaMode is %s but planeIndex %u supportedAlpha (0x%x) " |
| "does not support the mode.", |
| string_VkDisplayPlaneAlphaFlagBitsKHR(pCreateInfo->alphaMode), plane_index, |
| plane_capabilities.supportedAlpha); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, |
| const VkDebugMarkerMarkerInfoEXT *pMarkerInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| return ValidateCmd(cb_state.get(), CMD_DEBUGMARKERBEGINEXT); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| return ValidateCmd(cb_state.get(), CMD_DEBUGMARKERENDEXT); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, |
| VkQueryControlFlags flags, uint32_t index) const { |
| if (disabled[query_validation]) return false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| QueryObject query_obj(queryPool, query, index); |
| const char *cmd_name = "vkCmdBeginQueryIndexedEXT()"; |
| struct BeginQueryIndexedVuids : ValidateBeginQueryVuids { |
| BeginQueryIndexedVuids() : ValidateBeginQueryVuids() { |
| vuid_queue_flags = "VUID-vkCmdBeginQueryIndexedEXT-commandBuffer-cmdpool"; |
| vuid_queue_feedback = "VUID-vkCmdBeginQueryIndexedEXT-queryType-02338"; |
| vuid_queue_occlusion = "VUID-vkCmdBeginQueryIndexedEXT-queryType-00803"; |
| vuid_precise = "VUID-vkCmdBeginQueryIndexedEXT-queryType-00800"; |
| vuid_query_count = "VUID-vkCmdBeginQueryIndexedEXT-query-00802"; |
| vuid_profile_lock = "VUID-vkCmdBeginQueryIndexedEXT-queryPool-03223"; |
| vuid_scope_not_first = "VUID-vkCmdBeginQueryIndexedEXT-queryPool-03224"; |
| vuid_scope_in_rp = "VUID-vkCmdBeginQueryIndexedEXT-queryPool-03225"; |
| vuid_dup_query_type = "VUID-vkCmdBeginQueryIndexedEXT-queryPool-04753"; |
| vuid_protected_cb = "VUID-vkCmdBeginQueryIndexedEXT-commandBuffer-01885"; |
| vuid_multiview_query = "VUID-vkCmdBeginQueryIndexedEXT-query-00808"; |
| vuid_graphics_support = "VUID-vkCmdBeginQueryIndexedEXT-queryType-00804"; |
| vuid_compute_support = "VUID-vkCmdBeginQueryIndexedEXT-queryType-00805"; |
| } |
| }; |
| BeginQueryIndexedVuids vuids; |
| bool skip = ValidateBeginQuery(cb_state.get(), query_obj, flags, index, CMD_BEGINQUERYINDEXEDEXT, &vuids); |
| |
| // Extension specific VU's |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query_obj.pool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) { |
| if (IsExtEnabled(device_extensions.vk_ext_transform_feedback) && |
| (index >= phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams)) { |
| skip |= LogError( |
| cb_state->commandBuffer(), "VUID-vkCmdBeginQueryIndexedEXT-queryType-02339", |
| "%s: index %" PRIu32 |
| " must be less than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackStreams %" PRIu32 ".", |
| cmd_name, index, phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams); |
| } |
| } else if (index != 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdBeginQueryIndexedEXT-queryType-02340", |
| "%s: index %" PRIu32 |
| " must be zero if %s was not created with type VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.", |
| cmd_name, index, report_data->FormatHandle(queryPool).c_str()); |
| } |
| return skip; |
| } |
| |
| void CoreChecks::PreCallRecordCmdBeginQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, |
| VkQueryControlFlags flags, uint32_t index) { |
| if (disabled[query_validation]) return; |
| QueryObject query_obj = {queryPool, query, index}; |
| EnqueueVerifyBeginQuery(commandBuffer, query_obj, CMD_BEGINQUERYINDEXEDEXT); |
| } |
| |
| void CoreChecks::PreCallRecordCmdEndQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, |
| uint32_t index) { |
| if (disabled[query_validation]) return; |
| auto cb_state = GetWrite<CMD_BUFFER_STATE>(commandBuffer); |
| QueryObject query_obj = {queryPool, query, index}; |
| query_obj.endCommandIndex = cb_state->commandCount - 1; |
| EnqueueVerifyEndQuery(*cb_state, query_obj); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, |
| uint32_t index) const { |
| if (disabled[query_validation]) return false; |
| QueryObject query_obj = {queryPool, query, index}; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| struct EndQueryIndexedVuids : ValidateEndQueryVuids { |
| EndQueryIndexedVuids() : ValidateEndQueryVuids() { |
| vuid_queue_flags = "VUID-vkCmdEndQueryIndexedEXT-commandBuffer-cmdpool"; |
| vuid_active_queries = "VUID-vkCmdEndQueryIndexedEXT-None-02342"; |
| vuid_protected_cb = "VUID-vkCmdEndQueryIndexedEXT-commandBuffer-02344"; |
| } |
| }; |
| EndQueryIndexedVuids vuids; |
| bool skip = false; |
| skip |= ValidateCmdEndQuery(cb_state.get(), query_obj, index, CMD_ENDQUERYINDEXEDEXT, &vuids); |
| |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if (query_pool_state) { |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| const uint32_t available_query_count = query_pool_state->createInfo.queryCount; |
| if (query >= available_query_count) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdEndQueryIndexedEXT-query-02343", |
| "vkCmdEndQueryIndexedEXT(): query index (%" PRIu32 |
| ") is greater or equal to the queryPool size (%" PRIu32 ").", |
| index, available_query_count); |
| } |
| if (query_pool_ci.queryType == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) { |
| if (IsExtEnabled(device_extensions.vk_ext_transform_feedback) && |
| (index >= phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams)) { |
| skip |= LogError( |
| cb_state->commandBuffer(), "VUID-vkCmdEndQueryIndexedEXT-queryType-02346", |
| "vkCmdEndQueryIndexedEXT(): index %" PRIu32 |
| " must be less than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackStreams %" PRIu32 ".", |
| index, phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams); |
| } |
| } else if (index != 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdEndQueryIndexedEXT-queryType-02347", |
| "vkCmdEndQueryIndexedEXT(): index %" PRIu32 |
| " must be zero if %s was not created with type VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT.", |
| index, report_data->FormatHandle(queryPool).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDiscardRectangleEXT(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, |
| uint32_t discardRectangleCount, |
| const VkRect2D *pDiscardRectangles) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| // Minimal validation for command buffer state |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETDISCARDRECTANGLEEXT, VK_TRUE, nullptr, nullptr); |
| skip |= ForbidInheritedViewportScissor( |
| commandBuffer, cb_state.get(), "VUID-vkCmdSetDiscardRectangleEXT-viewportScissor2D-04788", CMD_SETDISCARDRECTANGLEEXT); |
| for (uint32_t i = 0; i < discardRectangleCount; ++i) { |
| if (pDiscardRectangles[i].offset.x < 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetDiscardRectangleEXT-x-00587", |
| "vkCmdSetDiscardRectangleEXT(): pDiscardRectangles[%" PRIu32 "].x (%" PRIi32 ") is negative.", i, |
| pDiscardRectangles[i].offset.x); |
| } |
| if (pDiscardRectangles[i].offset.y < 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetDiscardRectangleEXT-x-00587", |
| "vkCmdSetDiscardRectangleEXT(): pDiscardRectangles[%" PRIu32 "].y (%" PRIi32 ") is negative.", i, |
| pDiscardRectangles[i].offset.y); |
| } |
| } |
| if (firstDiscardRectangle + discardRectangleCount > phys_dev_ext_props.discard_rectangle_props.maxDiscardRectangles) { |
| skip |= |
| LogError(cb_state->commandBuffer(), "VUID-vkCmdSetDiscardRectangleEXT-firstDiscardRectangle-00585", |
| "vkCmdSetDiscardRectangleEXT(): firstDiscardRectangle (%" PRIu32 ") + discardRectangleCount (%" PRIu32 |
| ") is not less than VkPhysicalDeviceDiscardRectanglePropertiesEXT::maxDiscardRectangles (%" PRIu32 ".", |
| firstDiscardRectangle, discardRectangleCount, phys_dev_ext_props.discard_rectangle_props.maxDiscardRectangles); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetSampleLocationsEXT(VkCommandBuffer commandBuffer, |
| const VkSampleLocationsInfoEXT *pSampleLocationsInfo) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| // Minimal validation for command buffer state |
| skip |= ValidateExtendedDynamicState(*cb_state, CMD_SETSAMPLELOCATIONSEXT, VK_TRUE, nullptr, nullptr); |
| skip |= ValidateSampleLocationsInfo(pSampleLocationsInfo, "vkCmdSetSampleLocationsEXT"); |
| |
| const auto lv_bind_point = ConvertToLvlBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS); |
| const auto *pipe = cb_state->lastBound[lv_bind_point].pipeline_state; |
| if (pipe != nullptr) { |
| // Check same error with different log messages |
| const auto *multisample_state = pipe->MultisampleState(); |
| if (multisample_state == nullptr) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetSampleLocationsEXT-sampleLocationsPerPixel-01529", |
| "vkCmdSetSampleLocationsEXT(): pSampleLocationsInfo->sampleLocationsPerPixel must be equal to " |
| "rasterizationSamples, but the bound graphics pipeline was created without a multisample state"); |
| } else if (multisample_state->rasterizationSamples != pSampleLocationsInfo->sampleLocationsPerPixel) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetSampleLocationsEXT-sampleLocationsPerPixel-01529", |
| "vkCmdSetSampleLocationsEXT(): pSampleLocationsInfo->sampleLocationsPerPixel (%s) is not equal to " |
| "the last bound pipeline's rasterizationSamples (%s)", |
| string_VkSampleCountFlagBits(pSampleLocationsInfo->sampleLocationsPerPixel), |
| string_VkSampleCountFlagBits(multisample_state->rasterizationSamples)); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCreateSamplerYcbcrConversion(const char *func_name, |
| const VkSamplerYcbcrConversionCreateInfo *create_info) const { |
| bool skip = false; |
| const VkFormat conversion_format = create_info->format; |
| |
| // Need to check for external format conversion first as it allows for non-UNORM format |
| bool external_format = false; |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| const VkExternalFormatANDROID *ext_format_android = LvlFindInChain<VkExternalFormatANDROID>(create_info->pNext); |
| if ((nullptr != ext_format_android) && (0 != ext_format_android->externalFormat)) { |
| external_format = true; |
| if (VK_FORMAT_UNDEFINED != create_info->format) { |
| return LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-format-01904", |
| "%s: CreateInfo format is not VK_FORMAT_UNDEFINED while " |
| "there is a chained VkExternalFormatANDROID struct with a non-zero externalFormat.", |
| func_name); |
| } |
| } |
| #endif // VK_USE_PLATFORM_ANDROID_KHR |
| |
| if ((external_format == false) && (FormatIsUNORM(conversion_format) == false)) { |
| const char *vuid = IsExtEnabled(device_extensions.vk_android_external_memory_android_hardware_buffer) |
| ? "VUID-VkSamplerYcbcrConversionCreateInfo-format-04061" |
| : "VUID-VkSamplerYcbcrConversionCreateInfo-format-04060"; |
| skip |= |
| LogError(device, vuid, |
| "%s: CreateInfo format (%s) is not an UNORM format and there is no external format conversion being created.", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| |
| // Gets VkFormatFeatureFlags according to Sampler Ycbcr Conversion Format Features |
| // (vkspec.html#potential-format-features) |
| VkFormatFeatureFlags2KHR format_features = ~0ULL; |
| if (conversion_format == VK_FORMAT_UNDEFINED) { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| // only check for external format inside VK_FORMAT_UNDEFINED check to prevent unnecessary extra errors from no format |
| // features being supported |
| if (external_format == true) { |
| auto it = ahb_ext_formats_map.find(ext_format_android->externalFormat); |
| if (it != ahb_ext_formats_map.end()) { |
| format_features = it->second; |
| } |
| } |
| #endif // VK_USE_PLATFORM_ANDROID_KHR |
| } else { |
| format_features = GetPotentialFormatFeatures(conversion_format); |
| } |
| |
| // Check all VUID that are based off of VkFormatFeatureFlags |
| // These can't be in StatelessValidation due to needing possible External AHB state for feature support |
| if (((format_features & VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR) == 0) && |
| ((format_features & VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT_KHR) == 0)) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-format-01650", |
| "%s: Format %s does not support either VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT or " |
| "VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| if ((format_features & VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT_KHR) == 0) { |
| if (FormatIsXChromaSubsampled(conversion_format) && create_info->xChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01651", |
| "%s: Format %s does not support VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT so xChromaOffset can't " |
| "be VK_CHROMA_LOCATION_COSITED_EVEN", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| if (FormatIsYChromaSubsampled(conversion_format) && create_info->yChromaOffset == VK_CHROMA_LOCATION_COSITED_EVEN) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01651", |
| "%s: Format %s does not support VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT so yChromaOffset can't " |
| "be VK_CHROMA_LOCATION_COSITED_EVEN", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| } |
| if ((format_features & VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR) == 0) { |
| if (FormatIsXChromaSubsampled(conversion_format) && create_info->xChromaOffset == VK_CHROMA_LOCATION_MIDPOINT) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01652", |
| "%s: Format %s does not support VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT so xChromaOffset can't " |
| "be VK_CHROMA_LOCATION_MIDPOINT", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| if (FormatIsYChromaSubsampled(conversion_format) && create_info->yChromaOffset == VK_CHROMA_LOCATION_MIDPOINT) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-xChromaOffset-01652", |
| "%s: Format %s does not support VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT so yChromaOffset can't " |
| "be VK_CHROMA_LOCATION_MIDPOINT", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| } |
| if (((format_features & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR) == |
| 0) && |
| (create_info->forceExplicitReconstruction == VK_TRUE)) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-forceExplicitReconstruction-01656", |
| "%s: Format %s does not support " |
| "VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT so " |
| "forceExplicitReconstruction must be VK_FALSE", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| if (((format_features & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR) == 0) && |
| (create_info->chromaFilter == VK_FILTER_LINEAR)) { |
| skip |= LogError(device, "VUID-VkSamplerYcbcrConversionCreateInfo-chromaFilter-01657", |
| "%s: Format %s does not support VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT so " |
| "chromaFilter must not be VK_FILTER_LINEAR", |
| func_name, string_VkFormat(conversion_format)); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkSamplerYcbcrConversion *pYcbcrConversion) const { |
| return ValidateCreateSamplerYcbcrConversion("vkCreateSamplerYcbcrConversion()", pCreateInfo); |
| } |
| |
| bool CoreChecks::PreCallValidateCreateSamplerYcbcrConversionKHR(VkDevice device, |
| const VkSamplerYcbcrConversionCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkSamplerYcbcrConversion *pYcbcrConversion) const { |
| return ValidateCreateSamplerYcbcrConversion("vkCreateSamplerYcbcrConversionKHR()", pCreateInfo); |
| } |
| |
| bool CoreChecks::PreCallValidateCreateSampler(VkDevice device, const VkSamplerCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkSampler *pSampler) const { |
| bool skip = false; |
| |
| auto num_samplers = Count<SAMPLER_STATE>(); |
| if (num_samplers >= phys_dev_props.limits.maxSamplerAllocationCount) { |
| skip |= LogError( |
| device, "VUID-vkCreateSampler-maxSamplerAllocationCount-04110", |
| "vkCreateSampler(): Number of currently valid sampler objects (%zu) is not less than the maximum allowed (%u).", |
| num_samplers, phys_dev_props.limits.maxSamplerAllocationCount); |
| } |
| |
| if (enabled_features.core11.samplerYcbcrConversion == VK_TRUE) { |
| const VkSamplerYcbcrConversionInfo *conversion_info = LvlFindInChain<VkSamplerYcbcrConversionInfo>(pCreateInfo->pNext); |
| if (conversion_info != nullptr) { |
| const VkSamplerYcbcrConversion sampler_ycbcr_conversion = conversion_info->conversion; |
| auto ycbcr_state = Get<SAMPLER_YCBCR_CONVERSION_STATE>(sampler_ycbcr_conversion); |
| if ((ycbcr_state->format_features & |
| VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR) == 0) { |
| const VkFilter chroma_filter = ycbcr_state->chromaFilter; |
| if (pCreateInfo->minFilter != chroma_filter) { |
| skip |= LogError( |
| device, "VUID-VkSamplerCreateInfo-minFilter-01645", |
| "VkCreateSampler: VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT is " |
| "not supported for SamplerYcbcrConversion's (%s) format %s so minFilter (%s) needs to be equal to " |
| "chromaFilter (%s)", |
| report_data->FormatHandle(sampler_ycbcr_conversion).c_str(), string_VkFormat(ycbcr_state->format), |
| string_VkFilter(pCreateInfo->minFilter), string_VkFilter(chroma_filter)); |
| } |
| if (pCreateInfo->magFilter != chroma_filter) { |
| skip |= LogError( |
| device, "VUID-VkSamplerCreateInfo-minFilter-01645", |
| "VkCreateSampler: VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT is " |
| "not supported for SamplerYcbcrConversion's (%s) format %s so minFilter (%s) needs to be equal to " |
| "chromaFilter (%s)", |
| report_data->FormatHandle(sampler_ycbcr_conversion).c_str(), string_VkFormat(ycbcr_state->format), |
| string_VkFilter(pCreateInfo->minFilter), string_VkFilter(chroma_filter)); |
| } |
| } |
| // At this point there is a known sampler YCbCr conversion enabled |
| const auto *sampler_reduction = LvlFindInChain<VkSamplerReductionModeCreateInfo>(pCreateInfo->pNext); |
| if (sampler_reduction != nullptr) { |
| if (sampler_reduction->reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE) { |
| skip |= LogError(device, "VUID-VkSamplerCreateInfo-None-01647", |
| "A sampler YCbCr Conversion is being used creating this sampler so the sampler reduction mode " |
| "must be VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE."); |
| } |
| } |
| } |
| } |
| |
| if (pCreateInfo->borderColor == VK_BORDER_COLOR_INT_CUSTOM_EXT || |
| pCreateInfo->borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT) { |
| if (!enabled_features.custom_border_color_features.customBorderColors) { |
| skip |= |
| LogError(device, "VUID-VkSamplerCreateInfo-customBorderColors-04085", |
| "vkCreateSampler(): A custom border color was specified without enabling the custom border color feature"); |
| } |
| auto custom_create_info = LvlFindInChain<VkSamplerCustomBorderColorCreateInfoEXT>(pCreateInfo->pNext); |
| if (custom_create_info) { |
| if (custom_create_info->format == VK_FORMAT_UNDEFINED && |
| !enabled_features.custom_border_color_features.customBorderColorWithoutFormat) { |
| skip |= LogError(device, "VUID-VkSamplerCustomBorderColorCreateInfoEXT-format-04014", |
| "vkCreateSampler(): A custom border color was specified as VK_FORMAT_UNDEFINED without the " |
| "customBorderColorWithoutFormat feature being enabled"); |
| } |
| } |
| if (custom_border_color_sampler_count >= phys_dev_ext_props.custom_border_color_props.maxCustomBorderColorSamplers) { |
| skip |= LogError(device, "VUID-VkSamplerCreateInfo-None-04012", |
| "vkCreateSampler(): Creating a sampler with a custom border color will exceed the " |
| "maxCustomBorderColorSamplers limit of %d", |
| phys_dev_ext_props.custom_border_color_props.maxCustomBorderColorSamplers); |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) { |
| if ((VK_FALSE == enabled_features.portability_subset_features.samplerMipLodBias) && pCreateInfo->mipLodBias != 0) { |
| skip |= LogError(device, "VUID-VkSamplerCreateInfo-samplerMipLodBias-04467", |
| "vkCreateSampler (portability error): mip LOD bias not supported."); |
| } |
| } |
| |
| // If any of addressModeU, addressModeV or addressModeW are VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE, the |
| // VK_KHR_sampler_mirror_clamp_to_edge extension or promoted feature must be enabled |
| if ((device_extensions.vk_khr_sampler_mirror_clamp_to_edge != kEnabledByCreateinfo) && |
| (enabled_features.core12.samplerMirrorClampToEdge == VK_FALSE)) { |
| // Use 'else' because getting 3 large error messages is redundant and assume developer, if set all 3, will notice and fix |
| // all at once |
| if (pCreateInfo->addressModeU == VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE) { |
| skip |= |
| LogError(device, "VUID-VkSamplerCreateInfo-addressModeU-01079", |
| "vkCreateSampler(): addressModeU is set to VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE but the " |
| "VK_KHR_sampler_mirror_clamp_to_edge extension or samplerMirrorClampToEdge feature has not been enabled."); |
| } else if (pCreateInfo->addressModeV == VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE) { |
| skip |= |
| LogError(device, "VUID-VkSamplerCreateInfo-addressModeU-01079", |
| "vkCreateSampler(): addressModeV is set to VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE but the " |
| "VK_KHR_sampler_mirror_clamp_to_edge extension or samplerMirrorClampToEdge feature has not been enabled."); |
| } else if (pCreateInfo->addressModeW == VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE) { |
| skip |= |
| LogError(device, "VUID-VkSamplerCreateInfo-addressModeU-01079", |
| "vkCreateSampler(): addressModeW is set to VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE but the " |
| "VK_KHR_sampler_mirror_clamp_to_edge extension or samplerMirrorClampToEdge feature has not been enabled."); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo *pInfo, |
| const char *apiName) const { |
| bool skip = false; |
| |
| if (!enabled_features.core12.bufferDeviceAddress && !enabled_features.buffer_device_address_ext_features.bufferDeviceAddress) { |
| skip |= LogError(pInfo->buffer, "VUID-vkGetBufferDeviceAddress-bufferDeviceAddress-03324", |
| "%s: The bufferDeviceAddress feature must: be enabled.", apiName); |
| } |
| |
| if (physical_device_count > 1 && !enabled_features.core12.bufferDeviceAddressMultiDevice && |
| !enabled_features.buffer_device_address_ext_features.bufferDeviceAddressMultiDevice) { |
| skip |= LogError(pInfo->buffer, "VUID-vkGetBufferDeviceAddress-device-03325", |
| "%s: If device was created with multiple physical devices, then the " |
| "bufferDeviceAddressMultiDevice feature must: be enabled.", |
| apiName); |
| } |
| |
| auto buffer_state = Get<BUFFER_STATE>(pInfo->buffer); |
| if (buffer_state) { |
| if (!(buffer_state->createInfo.flags & VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT)) { |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), apiName, "VUID-VkBufferDeviceAddressInfo-buffer-02600"); |
| } |
| |
| skip |= ValidateBufferUsageFlags(buffer_state.get(), VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, true, |
| "VUID-VkBufferDeviceAddressInfo-buffer-02601", apiName, |
| "VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT"); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetBufferDeviceAddressEXT(VkDevice device, const VkBufferDeviceAddressInfo *pInfo) const { |
| return ValidateGetBufferDeviceAddress(device, static_cast<const VkBufferDeviceAddressInfo *>(pInfo), |
| "vkGetBufferDeviceAddressEXT"); |
| } |
| |
| bool CoreChecks::PreCallValidateGetBufferDeviceAddressKHR(VkDevice device, const VkBufferDeviceAddressInfo *pInfo) const { |
| return ValidateGetBufferDeviceAddress(device, static_cast<const VkBufferDeviceAddressInfo *>(pInfo), |
| "vkGetBufferDeviceAddressKHR"); |
| } |
| |
| bool CoreChecks::PreCallValidateGetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo *pInfo) const { |
| return ValidateGetBufferDeviceAddress(device, static_cast<const VkBufferDeviceAddressInfo *>(pInfo), |
| "vkGetBufferDeviceAddress"); |
| } |
| |
| bool CoreChecks::ValidateGetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo *pInfo, |
| const char *apiName) const { |
| bool skip = false; |
| |
| if (!enabled_features.core12.bufferDeviceAddress) { |
| skip |= LogError(pInfo->buffer, "VUID-vkGetBufferOpaqueCaptureAddress-None-03326", |
| "%s(): The bufferDeviceAddress feature must: be enabled.", apiName); |
| } |
| |
| if (physical_device_count > 1 && !enabled_features.core12.bufferDeviceAddressMultiDevice) { |
| skip |= LogError(pInfo->buffer, "VUID-vkGetBufferOpaqueCaptureAddress-device-03327", |
| "%s(): If device was created with multiple physical devices, then the " |
| "bufferDeviceAddressMultiDevice feature must: be enabled.", |
| apiName); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetBufferOpaqueCaptureAddressKHR(VkDevice device, const VkBufferDeviceAddressInfo *pInfo) const { |
| return ValidateGetBufferOpaqueCaptureAddress(device, static_cast<const VkBufferDeviceAddressInfo *>(pInfo), |
| "vkGetBufferOpaqueCaptureAddressKHR"); |
| } |
| |
| bool CoreChecks::PreCallValidateGetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo *pInfo) const { |
| return ValidateGetBufferOpaqueCaptureAddress(device, static_cast<const VkBufferDeviceAddressInfo *>(pInfo), |
| "vkGetBufferOpaqueCaptureAddress"); |
| } |
| |
| bool CoreChecks::ValidateGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo, |
| const char *apiName) const { |
| bool skip = false; |
| |
| if (!enabled_features.core12.bufferDeviceAddress) { |
| skip |= LogError(pInfo->memory, "VUID-vkGetDeviceMemoryOpaqueCaptureAddress-None-03334", |
| "%s(): The bufferDeviceAddress feature must: be enabled.", apiName); |
| } |
| |
| if (physical_device_count > 1 && !enabled_features.core12.bufferDeviceAddressMultiDevice) { |
| skip |= LogError(pInfo->memory, "VUID-vkGetDeviceMemoryOpaqueCaptureAddress-device-03335", |
| "%s(): If device was created with multiple physical devices, then the " |
| "bufferDeviceAddressMultiDevice feature must: be enabled.", |
| apiName); |
| } |
| |
| auto mem_info = Get<DEVICE_MEMORY_STATE>(pInfo->memory); |
| if (mem_info) { |
| auto chained_flags_struct = LvlFindInChain<VkMemoryAllocateFlagsInfo>(mem_info->alloc_info.pNext); |
| if (!chained_flags_struct || !(chained_flags_struct->flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT)) { |
| skip |= LogError(pInfo->memory, "VUID-VkDeviceMemoryOpaqueCaptureAddressInfo-memory-03336", |
| "%s(): memory must have been allocated with VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT.", apiName); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetDeviceMemoryOpaqueCaptureAddressKHR(VkDevice device, |
| const VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo) const { |
| return ValidateGetDeviceMemoryOpaqueCaptureAddress(device, static_cast<const VkDeviceMemoryOpaqueCaptureAddressInfo *>(pInfo), |
| "vkGetDeviceMemoryOpaqueCaptureAddressKHR"); |
| } |
| |
| bool CoreChecks::PreCallValidateGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, |
| const VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo) const { |
| return ValidateGetDeviceMemoryOpaqueCaptureAddress(device, static_cast<const VkDeviceMemoryOpaqueCaptureAddressInfo *>(pInfo), |
| "vkGetDeviceMemoryOpaqueCaptureAddress"); |
| } |
| |
| bool CoreChecks::ValidateQueryRange(VkDevice device, VkQueryPool queryPool, uint32_t totalCount, uint32_t firstQuery, |
| uint32_t queryCount, const char *vuid_badfirst, const char *vuid_badrange, |
| const char *apiName) const { |
| bool skip = false; |
| |
| if (firstQuery >= totalCount) { |
| skip |= LogError(device, vuid_badfirst, |
| "%s(): firstQuery (%" PRIu32 ") greater than or equal to query pool count (%" PRIu32 ") for %s", apiName, |
| firstQuery, totalCount, report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| if ((firstQuery + queryCount) > totalCount) { |
| skip |= LogError(device, vuid_badrange, |
| "%s(): Query range [%" PRIu32 ", %" PRIu32 ") goes beyond query pool count (%" PRIu32 ") for %s", apiName, |
| firstQuery, firstQuery + queryCount, totalCount, report_data->FormatHandle(queryPool).c_str()); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, |
| const char *apiName) const { |
| if (disabled[query_validation]) return false; |
| |
| bool skip = false; |
| |
| if (!enabled_features.core12.hostQueryReset) { |
| skip |= LogError(device, "VUID-vkResetQueryPool-None-02665", "%s(): Host query reset not enabled for device", apiName); |
| } |
| |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if (query_pool_state) { |
| skip |= ValidateQueryRange(device, queryPool, query_pool_state->createInfo.queryCount, firstQuery, queryCount, |
| "VUID-vkResetQueryPool-firstQuery-02666", "VUID-vkResetQueryPool-firstQuery-02667", apiName); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateResetQueryPoolEXT(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, |
| uint32_t queryCount) const { |
| return ValidateResetQueryPool(device, queryPool, firstQuery, queryCount, "vkResetQueryPoolEXT"); |
| } |
| |
| bool CoreChecks::PreCallValidateResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, |
| uint32_t queryCount) const { |
| return ValidateResetQueryPool(device, queryPool, firstQuery, queryCount, "vkResetQueryPool"); |
| } |
| |
| VkResult CoreChecks::CoreLayerCreateValidationCacheEXT(VkDevice device, const VkValidationCacheCreateInfoEXT *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkValidationCacheEXT *pValidationCache) { |
| *pValidationCache = ValidationCache::Create(pCreateInfo); |
| return *pValidationCache ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| void CoreChecks::CoreLayerDestroyValidationCacheEXT(VkDevice device, VkValidationCacheEXT validationCache, |
| const VkAllocationCallbacks *pAllocator) { |
| delete CastFromHandle<ValidationCache *>(validationCache); |
| } |
| |
| VkResult CoreChecks::CoreLayerGetValidationCacheDataEXT(VkDevice device, VkValidationCacheEXT validationCache, size_t *pDataSize, |
| void *pData) { |
| size_t in_size = *pDataSize; |
| CastFromHandle<ValidationCache *>(validationCache)->Write(pDataSize, pData); |
| return (pData && *pDataSize != in_size) ? VK_INCOMPLETE : VK_SUCCESS; |
| } |
| |
| VkResult CoreChecks::CoreLayerMergeValidationCachesEXT(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, |
| const VkValidationCacheEXT *pSrcCaches) { |
| bool skip = false; |
| auto dst = CastFromHandle<ValidationCache *>(dstCache); |
| VkResult result = VK_SUCCESS; |
| for (uint32_t i = 0; i < srcCacheCount; i++) { |
| auto src = CastFromHandle<const ValidationCache *>(pSrcCaches[i]); |
| if (src == dst) { |
| skip |= LogError(device, "VUID-vkMergeValidationCachesEXT-dstCache-01536", |
| "vkMergeValidationCachesEXT: dstCache (0x%" PRIx64 ") must not appear in pSrcCaches array.", |
| HandleToUint64(dstCache)); |
| result = VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| if (!skip) { |
| dst->Merge(src); |
| } |
| } |
| |
| return result; |
| } |
| |
| bool CoreChecks::ValidateCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask, CMD_TYPE cmd_type) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| skip |= ValidateExtendedDynamicState(*cb_state, cmd_type, VK_TRUE, nullptr, nullptr); |
| skip |= ValidateDeviceMaskToPhysicalDeviceCount(deviceMask, commandBuffer, "VUID-vkCmdSetDeviceMask-deviceMask-00108"); |
| skip |= ValidateDeviceMaskToZero(deviceMask, commandBuffer, "VUID-vkCmdSetDeviceMask-deviceMask-00109"); |
| skip |= |
| ValidateDeviceMaskToCommandBuffer(cb_state.get(), deviceMask, commandBuffer, "VUID-vkCmdSetDeviceMask-deviceMask-00110"); |
| if (cb_state->activeRenderPass) { |
| skip |= ValidateDeviceMaskToRenderPass(cb_state.get(), deviceMask, "VUID-vkCmdSetDeviceMask-deviceMask-00111"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) const { |
| return ValidateCmdSetDeviceMask(commandBuffer, deviceMask, CMD_SETDEVICEMASK); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDeviceMaskKHR(VkCommandBuffer commandBuffer, uint32_t deviceMask) const { |
| return ValidateCmdSetDeviceMask(commandBuffer, deviceMask, CMD_SETDEVICEMASKKHR); |
| } |
| |
| bool CoreChecks::ValidateGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t *pValue, |
| const char *apiName) const { |
| bool skip = false; |
| auto semaphore_state = Get<SEMAPHORE_STATE>(semaphore); |
| if (semaphore_state && semaphore_state->type != VK_SEMAPHORE_TYPE_TIMELINE) { |
| skip |= LogError(semaphore, "VUID-vkGetSemaphoreCounterValue-semaphore-03255", |
| "%s(): semaphore %s must be of VK_SEMAPHORE_TYPE_TIMELINE type", apiName, |
| report_data->FormatHandle(semaphore).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetSemaphoreCounterValueKHR(VkDevice device, VkSemaphore semaphore, uint64_t *pValue) const { |
| return ValidateGetSemaphoreCounterValue(device, semaphore, pValue, "vkGetSemaphoreCounterValueKHR"); |
| } |
| bool CoreChecks::PreCallValidateGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t *pValue) const { |
| return ValidateGetSemaphoreCounterValue(device, semaphore, pValue, "vkGetSemaphoreCounterValue"); |
| } |
| bool CoreChecks::ValidateQueryPoolStride(const std::string &vuid_not_64, const std::string &vuid_64, const VkDeviceSize stride, |
| const char *parameter_name, const uint64_t parameter_value, |
| const VkQueryResultFlags flags) const { |
| bool skip = false; |
| if (flags & VK_QUERY_RESULT_64_BIT) { |
| static const int condition_multiples = 0b0111; |
| if ((stride & condition_multiples) || (parameter_value & condition_multiples)) { |
| skip |= LogError(device, vuid_64, "stride %" PRIx64 " or %s %" PRIx64 " is invalid.", stride, parameter_name, |
| parameter_value); |
| } |
| } else { |
| static const int condition_multiples = 0b0011; |
| if ((stride & condition_multiples) || (parameter_value & condition_multiples)) { |
| skip |= LogError(device, vuid_not_64, "stride %" PRIx64 " or %s %" PRIx64 " is invalid.", stride, parameter_name, |
| parameter_value); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdDrawStrideWithStruct(VkCommandBuffer commandBuffer, const std::string &vuid, const uint32_t stride, |
| const char *struct_name, const uint32_t struct_size) const { |
| bool skip = false; |
| static const int condition_multiples = 0b0011; |
| if ((stride & condition_multiples) || (stride < struct_size)) { |
| skip |= LogError(commandBuffer, vuid, "stride %d is invalid or less than sizeof(%s) %d.", stride, struct_name, struct_size); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdDrawStrideWithBuffer(VkCommandBuffer commandBuffer, const std::string &vuid, const uint32_t stride, |
| const char *struct_name, const uint32_t struct_size, const uint32_t drawCount, |
| const VkDeviceSize offset, const BUFFER_STATE *buffer_state) const { |
| bool skip = false; |
| uint64_t validation_value = stride * (drawCount - 1) + offset + struct_size; |
| if (validation_value > buffer_state->createInfo.size) { |
| skip |= LogError(commandBuffer, vuid, |
| "stride[%d] * (drawCount[%d] - 1) + offset[%" PRIx64 "] + sizeof(%s)[%d] = %" PRIx64 |
| " is greater than the size[%" PRIx64 "] of %s.", |
| stride, drawCount, offset, struct_name, struct_size, validation_value, buffer_state->createInfo.size, |
| report_data->FormatHandle(buffer_state->buffer()).c_str()); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateReleaseProfilingLockKHR(VkDevice device) const { |
| bool skip = false; |
| |
| if (!performance_lock_acquired) { |
| skip |= LogError(device, "VUID-vkReleaseProfilingLockKHR-device-03235", |
| "vkReleaseProfilingLockKHR(): The profiling lock of device must have been held via a previous successful " |
| "call to vkAcquireProfilingLockKHR."); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreatePrivateDataSlotEXT(VkDevice device, const VkPrivateDataSlotCreateInfoEXT *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkPrivateDataSlotEXT *pPrivateDataSlot) const { |
| bool skip = false; |
| if (!enabled_features.core13.privateData) { |
| skip |= LogError(device, "VUID-vkCreatePrivateDataSlot-privateData-04564", |
| "vkCreatePrivateDataSlotEXT(): The privateData feature must be enabled."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkPrivateDataSlot *pPrivateDataSlot) const { |
| bool skip = false; |
| if (!enabled_features.core13.privateData) { |
| skip |= LogError(device, "VUID-vkCreatePrivateDataSlot-privateData-04564", |
| "vkCreatePrivateDataSlot(): The privateData feature must be enabled."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetCheckpointNV(VkCommandBuffer commandBuffer, const void *pCheckpointMarker) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETCHECKPOINTNV, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateWriteAccelerationStructuresPropertiesKHR(VkDevice device, uint32_t accelerationStructureCount, |
| const VkAccelerationStructureKHR *pAccelerationStructures, |
| VkQueryType queryType, size_t dataSize, void *pData, |
| size_t stride) const { |
| bool skip = false; |
| for (uint32_t i = 0; i < accelerationStructureCount; ++i) { |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pAccelerationStructures[i]); |
| const auto &as_info = as_state->build_info_khr; |
| if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR) { |
| if (!(as_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) { |
| skip |= LogError(device, "VUID-vkWriteAccelerationStructuresPropertiesKHR-accelerationStructures-03431", |
| "vkWriteAccelerationStructuresPropertiesKHR: All acceleration structures (%s) in " |
| "pAccelerationStructures must have been built with" |
| "VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR if queryType is " |
| "VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR.", |
| report_data->FormatHandle(as_state->acceleration_structure()).c_str()); |
| } |
| } |
| if (as_state) { |
| skip |= |
| ValidateHostVisibleMemoryIsBoundToBuffer(as_state->buffer_state.get(), "vkWriteAccelerationStructuresPropertiesKHR", |
| "VUID-vkWriteAccelerationStructuresPropertiesKHR-buffer-03733"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWriteAccelerationStructuresPropertiesKHR( |
| VkCommandBuffer commandBuffer, uint32_t accelerationStructureCount, const VkAccelerationStructureKHR *pAccelerationStructures, |
| VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| skip |= ValidateCmd(cb_state.get(), CMD_WRITEACCELERATIONSTRUCTURESPROPERTIESKHR); |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| if (query_pool_ci.queryType != queryType) { |
| skip |= LogError( |
| device, "VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-queryPool-02493", |
| "vkCmdWriteAccelerationStructuresPropertiesKHR: queryPool must have been created with a queryType matching queryType."); |
| } |
| for (uint32_t i = 0; i < accelerationStructureCount; ++i) { |
| if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR) { |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pAccelerationStructures[i]); |
| if (!(as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) { |
| skip |= LogError( |
| device, "VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-accelerationStructures-03431", |
| "vkCmdWriteAccelerationStructuresPropertiesKHR: All acceleration structures in pAccelerationStructures " |
| "must have been built with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR if queryType is " |
| "VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR."); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdWriteAccelerationStructuresPropertiesNV(VkCommandBuffer commandBuffer, |
| uint32_t accelerationStructureCount, |
| const VkAccelerationStructureNV *pAccelerationStructures, |
| VkQueryType queryType, VkQueryPool queryPool, |
| uint32_t firstQuery) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| skip |= ValidateCmd(cb_state.get(), CMD_WRITEACCELERATIONSTRUCTURESPROPERTIESNV); |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| const auto &query_pool_ci = query_pool_state->createInfo; |
| if (query_pool_ci.queryType != queryType) { |
| skip |= LogError( |
| device, "VUID-vkCmdWriteAccelerationStructuresPropertiesNV-queryPool-03755", |
| "vkCmdWriteAccelerationStructuresPropertiesNV: queryPool must have been created with a queryType matching queryType."); |
| } |
| for (uint32_t i = 0; i < accelerationStructureCount; ++i) { |
| if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV) { |
| auto as_state = Get<ACCELERATION_STRUCTURE_STATE>(pAccelerationStructures[i]); |
| if (!(as_state->build_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) { |
| skip |= |
| LogError(device, "VUID-vkCmdWriteAccelerationStructuresPropertiesNV-pAccelerationStructures-06215", |
| "vkCmdWriteAccelerationStructuresPropertiesNV: All acceleration structures in pAccelerationStructures " |
| "must have been built with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR if queryType is " |
| "VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV."); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| uint32_t CoreChecks::CalcTotalShaderGroupCount(const PIPELINE_STATE *pipelineState) const { |
| const auto &create_info = pipelineState->GetUnifiedCreateInfo().raytracing; |
| uint32_t total = create_info.groupCount; |
| |
| if (create_info.pLibraryInfo) { |
| for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) { |
| auto library_pipeline_state = Get<PIPELINE_STATE>(create_info.pLibraryInfo->pLibraries[i]); |
| total += CalcTotalShaderGroupCount(library_pipeline_state.get()); |
| } |
| } |
| |
| return total; |
| } |
| |
| bool CoreChecks::PreCallValidateGetRayTracingShaderGroupHandlesKHR(VkDevice device, VkPipeline pipeline, uint32_t firstGroup, |
| uint32_t groupCount, size_t dataSize, void *pData) const { |
| bool skip = false; |
| auto pipeline_state = Get<PIPELINE_STATE>(pipeline); |
| if (pipeline_state->GetPipelineCreateFlags() & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) { |
| skip |= LogError( |
| device, "VUID-vkGetRayTracingShaderGroupHandlesKHR-pipeline-03482", |
| "vkGetRayTracingShaderGroupHandlesKHR: pipeline must have not been created with VK_PIPELINE_CREATE_LIBRARY_BIT_KHR."); |
| } |
| if (dataSize < (phys_dev_ext_props.ray_tracing_propsKHR.shaderGroupHandleSize * groupCount)) { |
| skip |= LogError(device, "VUID-vkGetRayTracingShaderGroupHandlesKHR-dataSize-02420", |
| "vkGetRayTracingShaderGroupHandlesKHR: dataSize (%zu) must be at least " |
| "VkPhysicalDeviceRayTracingPipelinePropertiesKHR::shaderGroupHandleSize * groupCount.", |
| dataSize); |
| } |
| |
| uint32_t total_group_count = CalcTotalShaderGroupCount(pipeline_state.get()); |
| |
| if (firstGroup >= total_group_count) { |
| skip |= |
| LogError(device, "VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-04050", |
| "vkGetRayTracingShaderGroupHandlesKHR: firstGroup must be less than the number of shader groups in pipeline."); |
| } |
| if ((firstGroup + groupCount) > total_group_count) { |
| skip |= LogError( |
| device, "VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-02419", |
| "vkGetRayTracingShaderGroupHandlesKHR: The sum of firstGroup and groupCount must be less than or equal the number " |
| "of shader groups in pipeline."); |
| } |
| return skip; |
| } |
| bool CoreChecks::PreCallValidateGetRayTracingCaptureReplayShaderGroupHandlesKHR(VkDevice device, VkPipeline pipeline, |
| uint32_t firstGroup, uint32_t groupCount, |
| size_t dataSize, void *pData) const { |
| bool skip = false; |
| if (dataSize < (phys_dev_ext_props.ray_tracing_propsKHR.shaderGroupHandleCaptureReplaySize * groupCount)) { |
| skip |= LogError(device, "VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-dataSize-03484", |
| "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR: dataSize (%zu) must be at least " |
| "VkPhysicalDeviceRayTracingPipelinePropertiesKHR::shaderGroupHandleCaptureReplaySize * groupCount.", |
| dataSize); |
| } |
| auto pipeline_state = Get<PIPELINE_STATE>(pipeline); |
| if (!pipeline_state) { |
| return skip; |
| } |
| const auto &create_info = pipeline_state->GetUnifiedCreateInfo().raytracing; |
| if (firstGroup >= create_info.groupCount) { |
| skip |= LogError(device, "VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-firstGroup-04051", |
| "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR: firstGroup must be less than the number of shader " |
| "groups in pipeline."); |
| } |
| if ((firstGroup + groupCount) > create_info.groupCount) { |
| skip |= LogError(device, "VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-firstGroup-03483", |
| "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR: The sum of firstGroup and groupCount must be less " |
| "than or equal to the number of shader groups in pipeline."); |
| } |
| if (!(create_info.flags & VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR)) { |
| skip |= LogError(device, "VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-pipeline-03607", |
| "pipeline must have been created with a flags that included " |
| "VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR."); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBuildAccelerationStructuresIndirectKHR(VkCommandBuffer commandBuffer, uint32_t infoCount, |
| const VkAccelerationStructureBuildGeometryInfoKHR *pInfos, |
| const VkDeviceAddress *pIndirectDeviceAddresses, |
| const uint32_t *pIndirectStrides, |
| const uint32_t *const *ppMaxPrimitiveCounts) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_BUILDACCELERATIONSTRUCTURESINDIRECTKHR); |
| skip |= ValidateCmdRayQueryState(cb_state.get(), CMD_BUILDACCELERATIONSTRUCTURESINDIRECTKHR, |
| VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR); |
| for (uint32_t i = 0; i < infoCount; ++i) { |
| auto src_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfos[i].srcAccelerationStructure); |
| auto dst_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfos[i].dstAccelerationStructure); |
| skip |= ValidateMemoryIsBoundToBuffer(dst_as_state->buffer_state.get(), "vkCmdBuildAccelerationStructuresIndirectKHR", |
| "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03707"); |
| if (pInfos[i].mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) { |
| skip |= ValidateMemoryIsBoundToBuffer(src_as_state->buffer_state.get(), "vkCmdBuildAccelerationStructuresIndirectKHR", |
| "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03708"); |
| if (src_as_state == nullptr || !src_as_state->built || |
| !(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03667", |
| "vkCmdBuildAccelerationStructuresIndirectKHR(): For each element of pInfos, if its mode member is " |
| "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its srcAccelerationStructure member must have " |
| "been built before with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR set in " |
| "VkAccelerationStructureBuildGeometryInfoKHR::flags."); |
| } |
| if (pInfos[i].geometryCount != src_as_state->build_info_khr.geometryCount) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03758", |
| "vkCmdBuildAccelerationStructuresIndirectKHR(): For each element of pInfos, if its mode member is " |
| "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR," |
| " its geometryCount member must have the same value which was specified when " |
| "srcAccelerationStructure was last built."); |
| } |
| if (pInfos[i].flags != src_as_state->build_info_khr.flags) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03759", |
| "vkCmdBuildAccelerationStructuresIndirectKHR(): For each element of pInfos, if its mode member is" |
| " VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its flags member must have the same value which" |
| " was specified when srcAccelerationStructure was last built."); |
| } |
| if (pInfos[i].type != src_as_state->build_info_khr.type) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03760", |
| "vkCmdBuildAccelerationStructuresIndirectKHR(): For each element of pInfos, if its mode member is" |
| " VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, its type member must have the same value which" |
| " was specified when srcAccelerationStructure was last built."); |
| } |
| } |
| if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) { |
| if (!dst_as_state || |
| (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR && |
| dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03700", |
| "vkCmdBuildAccelerationStructuresIndirectKHR(): For each element of pInfos, if its type member is " |
| "VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, its dstAccelerationStructure member must have " |
| "been created with a value of VkAccelerationStructureCreateInfoKHR::type equal to either " |
| "VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR or VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR."); |
| } |
| } |
| if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) { |
| if (!dst_as_state || |
| (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR && |
| dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) { |
| skip |= LogError(device, "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03699", |
| "vkCmdBuildAccelerationStructuresIndirectKHR():For each element of pInfos, if its type member is " |
| "VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, its dstAccelerationStructure member must have been " |
| "created with a value of VkAccelerationStructureCreateInfoKHR::type equal to either " |
| "VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR or VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR."); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCopyAccelerationStructureInfoKHR(const VkCopyAccelerationStructureInfoKHR *pInfo, |
| const char *api_name) const { |
| bool skip = false; |
| if (pInfo->mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR) { |
| auto src_as_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->src); |
| if (!(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) { |
| skip |= LogError(device, "VUID-VkCopyAccelerationStructureInfoKHR-src-03411", |
| "(%s): src must have been built with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR" |
| "if mode is VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR.", |
| api_name); |
| } |
| } |
| auto src_accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->src); |
| if (src_accel_state) { |
| auto buffer_state = Get<BUFFER_STATE>(src_accel_state->create_infoKHR.buffer); |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), api_name, "VUID-VkCopyAccelerationStructureInfoKHR-buffer-03718"); |
| } |
| auto dst_accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->dst); |
| if (dst_accel_state) { |
| auto buffer_state = Get<BUFFER_STATE>(dst_accel_state->create_infoKHR.buffer); |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), api_name, "VUID-VkCopyAccelerationStructureInfoKHR-buffer-03719"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyAccelerationStructureKHR(VkCommandBuffer commandBuffer, |
| const VkCopyAccelerationStructureInfoKHR *pInfo) const { |
| bool skip = false; |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| skip |= ValidateCmd(cb_state.get(), CMD_COPYACCELERATIONSTRUCTUREKHR); |
| if (pInfo) { |
| skip |= ValidateCopyAccelerationStructureInfoKHR(pInfo, "vkCmdCopyAccelerationStructureKHR"); |
| auto src_accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->src); |
| if (src_accel_state) { |
| skip |= ValidateMemoryIsBoundToBuffer(src_accel_state->buffer_state.get(), "vkCmdCopyAccelerationStructureKHR", |
| "VUID-vkCmdCopyAccelerationStructureKHR-buffer-03737"); |
| } |
| auto dst_accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->dst); |
| if (dst_accel_state) { |
| skip |= ValidateMemoryIsBoundToBuffer(dst_accel_state->buffer_state.get(), "vkCmdCopyAccelerationStructureKHR", |
| "VUID-vkCmdCopyAccelerationStructureKHR-buffer-03738"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCopyAccelerationStructureKHR(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| const VkCopyAccelerationStructureInfoKHR *pInfo) const { |
| bool skip = false; |
| if (pInfo) { |
| skip |= ValidateCopyAccelerationStructureInfoKHR(pInfo, "vkCopyAccelerationStructureKHR"); |
| auto src_accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->src); |
| if (src_accel_state) { |
| skip |= ValidateHostVisibleMemoryIsBoundToBuffer(src_accel_state->buffer_state.get(), "vkCopyAccelerationStructureKHR", |
| "VUID-vkCopyAccelerationStructureKHR-buffer-03727"); |
| } |
| auto dst_accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->dst); |
| if (dst_accel_state) { |
| skip |= ValidateHostVisibleMemoryIsBoundToBuffer(dst_accel_state->buffer_state.get(), "vkCopyAccelerationStructureKHR", |
| "VUID-vkCopyAccelerationStructureKHR-buffer-03728"); |
| } |
| } |
| return skip; |
| } |
| bool CoreChecks::PreCallValidateCmdCopyAccelerationStructureToMemoryKHR( |
| VkCommandBuffer commandBuffer, const VkCopyAccelerationStructureToMemoryInfoKHR *pInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_COPYACCELERATIONSTRUCTURETOMEMORYKHR); |
| |
| auto accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->src); |
| if (accel_state) { |
| auto buffer_state = Get<BUFFER_STATE>(accel_state->create_infoKHR.buffer); |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), "vkCmdCopyAccelerationStructureToMemoryKHR", |
| "VUID-vkCmdCopyAccelerationStructureToMemoryKHR-None-03559"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCopyMemoryToAccelerationStructureKHR( |
| VkDevice device, VkDeferredOperationKHR deferredOperation, const VkCopyMemoryToAccelerationStructureInfoKHR *pInfo) const { |
| bool skip = false; |
| |
| auto accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->dst); |
| if (accel_state) { |
| skip |= ValidateHostVisibleMemoryIsBoundToBuffer(accel_state->buffer_state.get(), "vkCopyMemoryToAccelerationStructureKHR", |
| "VUID-vkCopyMemoryToAccelerationStructureKHR-buffer-03730"); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdCopyMemoryToAccelerationStructureKHR( |
| VkCommandBuffer commandBuffer, const VkCopyMemoryToAccelerationStructureInfoKHR *pInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), CMD_COPYMEMORYTOACCELERATIONSTRUCTUREKHR); |
| |
| auto accel_state = Get<ACCELERATION_STRUCTURE_STATE_KHR>(pInfo->dst); |
| if (accel_state) { |
| skip |= ValidateMemoryIsBoundToBuffer(accel_state->buffer_state.get(), "vkCmdCopyAccelerationStructureToMemoryKHR", |
| "VUID-vkCmdCopyMemoryToAccelerationStructureKHR-buffer-03745"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBindTransformFeedbackBuffersEXT(VkCommandBuffer commandBuffer, uint32_t firstBinding, |
| uint32_t bindingCount, const VkBuffer *pBuffers, |
| const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes) const { |
| bool skip = false; |
| char const *const cmd_name = "CmdBindTransformFeedbackBuffersEXT"; |
| if (!enabled_features.transform_feedback_features.transformFeedback) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindTransformFeedbackBuffersEXT-transformFeedback-02355", |
| "%s: transformFeedback feature is not enabled.", cmd_name); |
| } |
| |
| { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (cb_state->transform_feedback_active) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBindTransformFeedbackBuffersEXT-None-02365", |
| "%s: transform feedback is active.", cmd_name); |
| } |
| } |
| |
| for (uint32_t i = 0; i < bindingCount; ++i) { |
| auto buffer_state = Get<BUFFER_STATE>(pBuffers[i]); |
| assert(buffer_state != nullptr); |
| |
| if (pOffsets[i] >= buffer_state->createInfo.size) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBindTransformFeedbackBuffersEXT-pOffsets-02358", |
| "%s: pOffset[%" PRIu32 "](0x%" PRIxLEAST64 |
| ") is greater than or equal to the size of pBuffers[%" PRIu32 "](0x%" PRIxLEAST64 ").", |
| cmd_name, i, pOffsets[i], i, buffer_state->createInfo.size); |
| } |
| |
| if ((buffer_state->createInfo.usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT) == 0) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBindTransformFeedbackBuffersEXT-pBuffers-02360", |
| "%s: pBuffers[%" PRIu32 "] (%s)" |
| " was not created with the VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT flag.", |
| cmd_name, i, report_data->FormatHandle(pBuffers[i]).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) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBindTransformFeedbackBuffersEXT-pSizes-02362", |
| "%s: pSizes[%" PRIu32 "](0x%" PRIxLEAST64 ") is greater than the size of pBuffers[%" PRIu32 |
| "](0x%" PRIxLEAST64 ").", |
| cmd_name, i, pSizes[i], i, buffer_state->createInfo.size); |
| } else if (pOffsets[i] + pSizes[i] > buffer_state->createInfo.size) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBindTransformFeedbackBuffersEXT-pOffsets-02363", |
| "%s: The sum of pOffsets[%" PRIu32 "](Ox%" PRIxLEAST64 ") and pSizes[%" PRIu32 "](0x%" PRIxLEAST64 |
| ") is greater than the size of pBuffers[%" PRIu32 "](0x%" PRIxLEAST64 ").", |
| cmd_name, i, pOffsets[i], i, pSizes[i], i, buffer_state->createInfo.size); |
| } |
| } |
| |
| skip |= |
| ValidateMemoryIsBoundToBuffer(buffer_state.get(), cmd_name, "VUID-vkCmdBindTransformFeedbackBuffersEXT-pBuffers-02364"); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginTransformFeedbackEXT(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, |
| uint32_t counterBufferCount, const VkBuffer *pCounterBuffers, |
| const VkDeviceSize *pCounterBufferOffsets) const { |
| bool skip = false; |
| char const *const cmd_name = "CmdBeginTransformFeedbackEXT"; |
| if (!enabled_features.transform_feedback_features.transformFeedback) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBeginTransformFeedbackEXT-transformFeedback-02366", |
| "%s: transformFeedback feature is not enabled.", cmd_name); |
| } |
| |
| { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (cb_state) { |
| if (cb_state->transform_feedback_active) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBeginTransformFeedbackEXT-None-02367", |
| "%s: transform feedback is active.", cmd_name); |
| } |
| if (cb_state->activeRenderPass) { |
| 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(commandBuffer, "VUID-vkCmdBeginTransformFeedbackEXT-None-02373", |
| "%s: active render pass (%s) has multiview enabled.", cmd_name, |
| report_data->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(commandBuffer, "VUID-vkCmdBeginTransformFeedbackEXT-pCounterBuffer-02371", |
| "%s: pCounterBuffers is NULL and pCounterBufferOffsets is not NULL.", cmd_name); |
| } |
| } else { |
| for (uint32_t i = 0; i < counterBufferCount; ++i) { |
| if (pCounterBuffers[i] != VK_NULL_HANDLE) { |
| auto buffer_state = Get<BUFFER_STATE>(pCounterBuffers[i]); |
| assert(buffer_state != nullptr); |
| |
| if (pCounterBufferOffsets != nullptr && pCounterBufferOffsets[i] + 4 > buffer_state->createInfo.size) { |
| skip |= |
| LogError(buffer_state->buffer(), "VUID-vkCmdBeginTransformFeedbackEXT-pCounterBufferOffsets-02370", |
| "%s: pCounterBuffers[%" PRIu32 "](%s) is not large enough to hold 4 bytes at pCounterBufferOffsets[%" PRIu32 "](0x%" PRIx64 ").", |
| cmd_name, i, report_data->FormatHandle(pCounterBuffers[i]).c_str(), i, pCounterBufferOffsets[i]); |
| } |
| |
| if ((buffer_state->createInfo.usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT) == 0) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBeginTransformFeedbackEXT-pCounterBuffers-02372", |
| "%s: pCounterBuffers[%" PRIu32 "] (%s) was not created with the VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT flag.", |
| cmd_name, i, report_data->FormatHandle(pCounterBuffers[i]).c_str()); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdEndTransformFeedbackEXT(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, |
| uint32_t counterBufferCount, const VkBuffer *pCounterBuffers, |
| const VkDeviceSize *pCounterBufferOffsets) const { |
| bool skip = false; |
| char const *const cmd_name = "CmdEndTransformFeedbackEXT"; |
| if (!enabled_features.transform_feedback_features.transformFeedback) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndTransformFeedbackEXT-transformFeedback-02374", |
| "%s: transformFeedback feature is not enabled.", cmd_name); |
| } |
| |
| { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (!cb_state->transform_feedback_active) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndTransformFeedbackEXT-None-02375", "%s: transform feedback is not active.", |
| cmd_name); |
| } |
| } |
| |
| // 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(commandBuffer, "VUID-vkCmdEndTransformFeedbackEXT-pCounterBuffer-02379", |
| "%s: pCounterBuffers is NULL and pCounterBufferOffsets is not NULL.", cmd_name); |
| } |
| } else { |
| for (uint32_t i = 0; i < counterBufferCount; ++i) { |
| if (pCounterBuffers[i] != VK_NULL_HANDLE) { |
| auto buffer_state = Get<BUFFER_STATE>(pCounterBuffers[i]); |
| assert(buffer_state != nullptr); |
| |
| if (pCounterBufferOffsets != nullptr && pCounterBufferOffsets[i] + 4 > buffer_state->createInfo.size) { |
| skip |= |
| LogError(buffer_state->buffer(), "VUID-vkCmdEndTransformFeedbackEXT-pCounterBufferOffsets-02378", |
| "%s: pCounterBuffers[%" PRIu32 "](%s) is not large enough to hold 4 bytes at pCounterBufferOffsets[%" PRIu32 "](0x%" PRIx64 ").", |
| cmd_name, i, report_data->FormatHandle(pCounterBuffers[i]).c_str(), i, pCounterBufferOffsets[i]); |
| } |
| |
| if ((buffer_state->createInfo.usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT) == 0) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdEndTransformFeedbackEXT-pCounterBuffers-02380", |
| "%s: pCounterBuffers[%" PRIu32 "] (%s) was not created with the VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT flag.", |
| cmd_name, i, report_data->FormatHandle(pCounterBuffers[i]).c_str()); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| // Used for all vkCmdSet* functions |
| // Some calls are behind a feature bit that needs to be enabled |
| bool CoreChecks::ValidateExtendedDynamicState(const CMD_BUFFER_STATE &cb_state, const CMD_TYPE cmd_type, VkBool32 feature, |
| const char *vuid, const char *feature_name) const { |
| bool skip = false; |
| skip |= ValidateCmd(&cb_state, cmd_type); |
| |
| if (!feature) { |
| const char *func_name = CommandTypeString(cmd_type); |
| skip |= LogError(cb_state.Handle(), vuid, "%s(): %s feature is not enabled.", func_name, feature_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetLogicOpEXT(VkCommandBuffer commandBuffer, VkLogicOp logicOp) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETLOGICOPEXT, |
| enabled_features.extended_dynamic_state2_features.extendedDynamicState2LogicOp, |
| "VUID-vkCmdSetLogicOpEXT-None-04867", "extendedDynamicState2LogicOp"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPatchControlPointsEXT(VkCommandBuffer commandBuffer, uint32_t patchControlPoints) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip |= |
| ValidateExtendedDynamicState(*cb_state, CMD_SETPATCHCONTROLPOINTSEXT, |
| enabled_features.extended_dynamic_state2_features.extendedDynamicState2PatchControlPoints, |
| "VUID-vkCmdSetPatchControlPointsEXT-None-04873", "extendedDynamicState2PatchControlPoints"); |
| |
| if (patchControlPoints > phys_dev_props.limits.maxTessellationPatchSize) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdSetPatchControlPointsEXT-patchControlPoints-04874", |
| "vkCmdSetPatchControlPointsEXT: The value of patchControlPoints must be less than " |
| "VkPhysicalDeviceLimits::maxTessellationPatchSize"); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetRasterizerDiscardEnableEXT(VkCommandBuffer commandBuffer, |
| VkBool32 rasterizerDiscardEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETRASTERIZERDISCARDENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetRasterizerDiscardEnable-None-04871", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, |
| VkBool32 rasterizerDiscardEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETRASTERIZERDISCARDENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthBiasEnableEXT(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHBIASENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetDepthBiasEnable-None-04872", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHBIASENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPrimitiveRestartEnableEXT(VkCommandBuffer commandBuffer, |
| VkBool32 primitiveRestartEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETPRIMITIVERESTARTENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetPrimitiveRestartEnable-None-04866", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETPRIMITIVERESTARTENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetCullModeEXT(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETCULLMODEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetCullMode-None-03384", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETCULLMODE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetFrontFaceEXT(VkCommandBuffer commandBuffer, VkFrontFace frontFace) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETFRONTFACEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetFrontFace-None-03383", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETFRONTFACE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPrimitiveTopologyEXT(VkCommandBuffer commandBuffer, |
| VkPrimitiveTopology primitiveTopology) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETPRIMITIVETOPOLOGYEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetPrimitiveTopology-None-03347", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, |
| VkPrimitiveTopology primitiveTopology) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETPRIMITIVETOPOLOGY, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetViewportWithCountEXT(VkCommandBuffer commandBuffer, uint32_t viewportCount, |
| const VkViewport *pViewports) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip = ValidateExtendedDynamicState(*cb_state, CMD_SETVIEWPORTWITHCOUNTEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetViewportWithCount-None-03393", "extendedDynamicState"); |
| skip |= ForbidInheritedViewportScissor(commandBuffer, cb_state.get(), "VUID-vkCmdSetViewportWithCount-commandBuffer-04819", |
| CMD_SETVIEWPORTWITHCOUNTEXT); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, |
| const VkViewport *pViewports) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip = ValidateExtendedDynamicState(*cb_state, CMD_SETVIEWPORTWITHCOUNT, VK_TRUE, nullptr, nullptr); |
| skip |= ForbidInheritedViewportScissor(commandBuffer, cb_state.get(), "VUID-vkCmdSetViewportWithCount-commandBuffer-04819", |
| CMD_SETVIEWPORTWITHCOUNT); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetScissorWithCountEXT(VkCommandBuffer commandBuffer, uint32_t scissorCount, |
| const VkRect2D *pScissors) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip = ValidateExtendedDynamicState(*cb_state, CMD_SETSCISSORWITHCOUNTEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetScissorWithCount-None-03396", "extendedDynamicState"); |
| skip |= ForbidInheritedViewportScissor(commandBuffer, cb_state.get(), "VUID-vkCmdSetScissorWithCount-commandBuffer-04820", |
| CMD_SETSCISSORWITHCOUNTEXT); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, |
| const VkRect2D *pScissors) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| bool skip = false; |
| skip = ValidateExtendedDynamicState(*cb_state, CMD_SETSCISSORWITHCOUNT, VK_TRUE, nullptr, nullptr); |
| skip |= ForbidInheritedViewportScissor(commandBuffer, cb_state.get(), "VUID-vkCmdSetScissorWithCount-commandBuffer-04820", |
| CMD_SETSCISSORWITHCOUNT); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateCmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, |
| const VkBuffer *pBuffers, const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes, |
| const VkDeviceSize *pStrides, CMD_TYPE cmd_type) const { |
| const char *api_call = CommandTypeString(cmd_type); |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| assert(cb_state); |
| |
| bool skip = false; |
| skip |= ValidateCmd(cb_state.get(), cmd_type); |
| for (uint32_t i = 0; i < bindingCount; ++i) { |
| auto buffer_state = Get<BUFFER_STATE>(pBuffers[i]); |
| if (buffer_state) { |
| skip |= ValidateBufferUsageFlags(buffer_state.get(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, true, |
| "VUID-vkCmdBindVertexBuffers2-pBuffers-03359", api_call, |
| "VK_BUFFER_USAGE_VERTEX_BUFFER_BIT"); |
| skip |= ValidateMemoryIsBoundToBuffer(buffer_state.get(), api_call, "VUID-vkCmdBindVertexBuffers2-pBuffers-03360"); |
| |
| if (pOffsets[i] >= buffer_state->createInfo.size) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBindVertexBuffers2-pOffsets-03357", |
| "%s offset (0x%" PRIxLEAST64 ") is beyond the end of the buffer.", api_call, pOffsets[i]); |
| } |
| if (pSizes && pOffsets[i] + pSizes[i] > buffer_state->createInfo.size) { |
| skip |= LogError(buffer_state->buffer(), "VUID-vkCmdBindVertexBuffers2-pSizes-03358", |
| "%s size (0x%" PRIxLEAST64 ") is beyond the end of the buffer.", api_call, pSizes[i]); |
| } |
| } |
| } |
| |
| 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 { |
| bool skip = ValidateCmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides, |
| CMD_BINDVERTEXBUFFERS2EXT); |
| 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 { |
| bool skip = ValidateCmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides, |
| CMD_BINDVERTEXBUFFERS2); |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthTestEnableEXT(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHTESTENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetDepthTestEnable-None-03352", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHTESTENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthWriteEnableEXT(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHWRITEENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetDepthWriteEnable-None-03354", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHWRITEENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthCompareOpEXT(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHCOMPAREOPEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetDepthCompareOp-None-03353", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHCOMPAREOP, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthBoundsTestEnableEXT(VkCommandBuffer commandBuffer, |
| VkBool32 depthBoundsTestEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHBOUNDSTESTENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetDepthBoundsTestEnable-None-03349", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETDEPTHBOUNDSTESTENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilTestEnableEXT(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILTESTENABLEEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetStencilTestEnable-None-03350", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILTESTENABLE, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilOpEXT(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, |
| VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILOPEXT, |
| enabled_features.extended_dynamic_state_features.extendedDynamicState, |
| "VUID-vkCmdSetStencilOp-None-03351", "extendedDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, |
| VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETSTENCILOP, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCreateEvent(VkDevice device, const VkEventCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, VkEvent *pEvent) const { |
| bool skip = false; |
| if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) { |
| if (VK_FALSE == enabled_features.portability_subset_features.events) { |
| skip |= LogError(device, "VUID-vkCreateEvent-events-04468", |
| "vkCreateEvent: events are not supported via VK_KHR_portability_subset"); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetRayTracingPipelineStackSizeKHR(VkCommandBuffer commandBuffer, |
| uint32_t pipelineStackSize) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETRAYTRACINGPIPELINESTACKSIZEKHR, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateGetRayTracingShaderGroupStackSizeKHR(VkDevice device, VkPipeline pipeline, uint32_t group, |
| VkShaderGroupShaderKHR groupShader) const { |
| bool skip = false; |
| auto pipeline_state = Get<PIPELINE_STATE>(pipeline); |
| if (pipeline_state) { |
| if (pipeline_state->GetPipelineType() != VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) { |
| skip |= LogError(device, "VUID-vkGetRayTracingShaderGroupStackSizeKHR-pipeline-04622", |
| "vkGetRayTracingShaderGroupStackSizeKHR: Pipeline must be a ray-tracing pipeline, but is a %s pipeline.", |
| GetPipelineTypeName(pipeline_state->GetPipelineType())); |
| } else if (group >= pipeline_state->GetUnifiedCreateInfo().raytracing.groupCount) { |
| skip |= |
| LogError(device, "VUID-vkGetRayTracingShaderGroupStackSizeKHR-group-03608", |
| "vkGetRayTracingShaderGroupStackSizeKHR: The value of group must be less than the number of shader groups " |
| "in pipeline."); |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetFragmentShadingRateKHR(VkCommandBuffer commandBuffer, const VkExtent2D *pFragmentSize, |
| const VkFragmentShadingRateCombinerOpKHR combinerOps[2]) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| const char *cmd_name = "vkCmdSetFragmentShadingRateKHR()"; |
| bool skip = false; |
| skip |= |
| ValidateExtendedDynamicState(*cb_state, CMD_SETFRAGMENTSHADINGRATEKHR, |
| enabled_features.fragment_shading_rate_features.pipelineFragmentShadingRate || |
| enabled_features.fragment_shading_rate_features.primitiveFragmentShadingRate || |
| enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate, |
| "VUID-vkCmdSetFragmentShadingRateKHR-pipelineFragmentShadingRate-04509", |
| "pipelineFragmentShadingRate, primitiveFragmentShadingRate, or attachmentFragmentShadingRate"); |
| |
| if (!enabled_features.fragment_shading_rate_features.pipelineFragmentShadingRate && pFragmentSize->width != 1) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pipelineFragmentShadingRate-04507", |
| "vkCmdSetFragmentShadingRateKHR: Pipeline fragment width of %u has been specified in %s, but " |
| "pipelineFragmentShadingRate is not enabled", |
| pFragmentSize->width, cmd_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.pipelineFragmentShadingRate && pFragmentSize->height != 1) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pipelineFragmentShadingRate-04508", |
| "vkCmdSetFragmentShadingRateKHR: Pipeline fragment height of %u has been specified in %s, but " |
| "pipelineFragmentShadingRate is not enabled", |
| pFragmentSize->height, cmd_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.primitiveFragmentShadingRate && |
| combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-primitiveFragmentShadingRate-04510", |
| "vkCmdSetFragmentShadingRateKHR: First combiner operation of %s has been specified in %s, but " |
| "primitiveFragmentShadingRate is not enabled", |
| string_VkFragmentShadingRateCombinerOpKHR(combinerOps[0]), cmd_name); |
| } |
| |
| if (!enabled_features.fragment_shading_rate_features.attachmentFragmentShadingRate && |
| combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-attachmentFragmentShadingRate-04511", |
| "vkCmdSetFragmentShadingRateKHR: Second combiner operation of %s has been specified in %s, but " |
| "attachmentFragmentShadingRate is not enabled", |
| string_VkFragmentShadingRateCombinerOpKHR(combinerOps[1]), cmd_name); |
| } |
| |
| if (!phys_dev_ext_props.fragment_shading_rate_props.fragmentShadingRateNonTrivialCombinerOps && |
| (combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR && |
| combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR)) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-fragmentSizeNonTrivialCombinerOps-04512", |
| "vkCmdSetFragmentShadingRateKHR: First combiner operation of %s has been specified in %s, but " |
| "fragmentShadingRateNonTrivialCombinerOps is " |
| "not supported", |
| string_VkFragmentShadingRateCombinerOpKHR(combinerOps[0]), cmd_name); |
| } |
| |
| if (!phys_dev_ext_props.fragment_shading_rate_props.fragmentShadingRateNonTrivialCombinerOps && |
| (combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR && |
| combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR)) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-fragmentSizeNonTrivialCombinerOps-04512", |
| "vkCmdSetFragmentShadingRateKHR: Second combiner operation of %s has been specified in %s, but " |
| "fragmentShadingRateNonTrivialCombinerOps " |
| "is not supported", |
| string_VkFragmentShadingRateCombinerOpKHR(combinerOps[1]), cmd_name); |
| } |
| |
| if (pFragmentSize->width == 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pFragmentSize-04513", |
| "vkCmdSetFragmentShadingRateKHR: Fragment width of %u has been specified in %s.", pFragmentSize->width, |
| cmd_name); |
| } |
| |
| if (pFragmentSize->height == 0) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pFragmentSize-04514", |
| "vkCmdSetFragmentShadingRateKHR: Fragment height of %u has been specified in %s.", pFragmentSize->height, |
| cmd_name); |
| } |
| |
| if (pFragmentSize->width != 0 && !IsPowerOfTwo(pFragmentSize->width)) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pFragmentSize-04515", |
| "vkCmdSetFragmentShadingRateKHR: Non-power-of-two fragment width of %u has been specified in %s.", |
| pFragmentSize->width, cmd_name); |
| } |
| |
| if (pFragmentSize->height != 0 && !IsPowerOfTwo(pFragmentSize->height)) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pFragmentSize-04516", |
| "vkCmdSetFragmentShadingRateKHR: Non-power-of-two fragment height of %u has been specified in %s.", |
| pFragmentSize->height, cmd_name); |
| } |
| |
| if (pFragmentSize->width > 4) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pFragmentSize-04517", |
| "vkCmdSetFragmentShadingRateKHR: Fragment width of %u specified in %s is too large.", pFragmentSize->width, |
| cmd_name); |
| } |
| |
| if (pFragmentSize->height > 4) { |
| skip |= LogError(cb_state->commandBuffer(), "VUID-vkCmdSetFragmentShadingRateKHR-pFragmentSize-04518", |
| "vkCmdSetFragmentShadingRateKHR: Fragment height of %u specified in %s is too large", |
| pFragmentSize->height, cmd_name); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, uint32_t attachmentCount, |
| const VkBool32 *pColorWriteEnables) const { |
| bool skip = false; |
| |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| skip |= |
| ValidateExtendedDynamicState(*cb_state, CMD_SETCOLORWRITEENABLEEXT, enabled_features.color_write_features.colorWriteEnable, |
| "VUID-vkCmdSetColorWriteEnableEXT-None-04803", "colorWriteEnable"); |
| |
| if (attachmentCount > phys_dev_props.limits.maxColorAttachments) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdSetColorWriteEnableEXT-attachmentCount-06656", |
| "vkCmdSetColorWriteEnableEXT(): attachmentCount (%" PRIu32 |
| ") is greater than the VkPhysicalDeviceLimits::maxColorAttachments limit (%" PRIu32 ").", |
| attachmentCount, phys_dev_props.limits.maxColorAttachments); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetVertexInputEXT( |
| VkCommandBuffer commandBuffer, uint32_t vertexBindingDescriptionCount, |
| const VkVertexInputBindingDescription2EXT *pVertexBindingDescriptions, uint32_t vertexAttributeDescriptionCount, |
| const VkVertexInputAttributeDescription2EXT *pVertexAttributeDescriptions) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETVERTEXINPUTEXT, |
| enabled_features.vertex_input_dynamic_state_features.vertexInputDynamicState, |
| "VUID-vkCmdSetVertexInputEXT-None-04790", "vertexInputDynamicState"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetCoarseSampleOrderNV(VkCommandBuffer commandBuffer, VkCoarseSampleOrderTypeNV sampleOrderType, |
| uint32_t customSampleOrderCount, |
| const VkCoarseSampleOrderCustomNV *pCustomSampleOrders) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETCOARSESAMPLEORDERNV, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetFragmentShadingRateEnumNV(VkCommandBuffer commandBuffer, VkFragmentShadingRateNV shadingRate, |
| const VkFragmentShadingRateCombinerOpKHR combinerOps[2]) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState( |
| *cb_state, CMD_SETFRAGMENTSHADINGRATEENUMNV, enabled_features.fragment_shading_rate_enums_features.fragmentShadingRateEnums, |
| "VUID-vkCmdSetFragmentShadingRateEnumNV-fragmentShadingRateEnums-04579", "fragmentShadingRateEnums"); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPerformanceMarkerINTEL(VkCommandBuffer commandBuffer, |
| const VkPerformanceMarkerInfoINTEL *pMarkerInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETPERFORMANCEMARKERINTEL, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPerformanceStreamMarkerINTEL(VkCommandBuffer commandBuffer, |
| const VkPerformanceStreamMarkerInfoINTEL *pMarkerInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETPERFORMANCEOVERRIDEINTEL, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdSetPerformanceOverrideINTEL(VkCommandBuffer commandBuffer, |
| const VkPerformanceOverrideInfoINTEL *pOverrideInfo) const { |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| return ValidateExtendedDynamicState(*cb_state, CMD_SETCOARSESAMPLEORDERNV, VK_TRUE, nullptr, nullptr); |
| } |
| |
| bool CoreChecks::PreCallValidateCmdBeginConditionalRenderingEXT( |
| VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT *pConditionalRenderingBegin) const { |
| bool skip = false; |
| |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (cb_state && cb_state->conditional_rendering_active) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdBeginConditionalRenderingEXT-None-01980", |
| "vkCmdBeginConditionalRenderingEXT(): Conditional rendering is already active."); |
| } |
| |
| if (pConditionalRenderingBegin) { |
| auto buffer_state = Get<BUFFER_STATE>(pConditionalRenderingBegin->buffer); |
| if (buffer_state) { |
| if ((buffer_state->createInfo.usage & VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT) == 0) { |
| skip |= LogError(commandBuffer, "VUID-VkConditionalRenderingBeginInfoEXT-buffer-01982", |
| "vkCmdBeginConditionalRenderingEXT(): pConditionalRenderingBegin->buffer (%s) was not create with " |
| "VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT bit.", |
| report_data->FormatHandle(pConditionalRenderingBegin->buffer).c_str()); |
| } |
| if (pConditionalRenderingBegin->offset + 4 > buffer_state->createInfo.size) { |
| skip |= LogError(commandBuffer, "VUID-VkConditionalRenderingBeginInfoEXT-offset-01983", |
| "vkCmdBeginConditionalRenderingEXT(): pConditionalRenderingBegin->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 { |
| bool skip = false; |
| |
| auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer); |
| if (cb_state) { |
| if (!cb_state->conditional_rendering_active) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndConditionalRenderingEXT-None-01985", |
| "vkCmdBeginConditionalRenderingEXT(): Conditional rendering is not active."); |
| } |
| if (!cb_state->conditional_rendering_inside_render_pass && cb_state->activeRenderPass != nullptr) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndConditionalRenderingEXT-None-01986", |
| "vkCmdBeginConditionalRenderingEXT(): 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->activeSubpass) { |
| skip |= LogError(commandBuffer, "VUID-vkCmdEndConditionalRenderingEXT-None-01987", |
| "vkCmdBeginConditionalRenderingEXT(): Conditional rendering was begun in subpass %" PRIu32 |
| ", but the current subpass is %" PRIu32 ".", |
| cb_state->conditional_rendering_subpass, cb_state->activeSubpass); |
| } |
| } |
| |
| return skip; |
| } |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| bool CoreChecks::PreCallValidateAcquireFullScreenExclusiveModeEXT(VkDevice device, VkSwapchainKHR swapchain) const { |
| bool skip = false; |
| |
| auto swapchain_state = Get<SWAPCHAIN_NODE>(swapchain); |
| if (swapchain_state) { |
| if (swapchain_state->retired) { |
| skip |= LogError(device, "VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02674", |
| "vkAcquireFullScreenExclusiveModeEXT(): swapchain %s is retired.", |
| report_data->FormatHandle(swapchain).c_str()); |
| } |
| const auto *surface_full_screen_exclusive_info = LvlFindInChain<VkSurfaceFullScreenExclusiveInfoEXT>(swapchain_state->createInfo.pNext); |
| if (!surface_full_screen_exclusive_info || |
| surface_full_screen_exclusive_info->fullScreenExclusive != VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT) { |
| skip |= LogError(device, "VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02675", |
| "vkAcquireFullScreenExclusiveModeEXT(): swapchain %s was not created with VkSurfaceFullScreenExclusiveInfoEXT in " |
| "the pNext chain with fullScreenExclusive equal to VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT.", |
| report_data->FormatHandle(swapchain).c_str()); |
| } |
| } |
| |
| return skip; |
| } |
| #endif |
| |
| bool CoreChecks::ValidatePhysicalDeviceSurfaceSupport(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, const char *vuid, |
| const char *func_name) const { |
| bool skip = false; |
| |
| auto pd_state = Get<PHYSICAL_DEVICE_STATE>(physicalDevice); |
| auto surface_state = Get<SURFACE_STATE>(surface); |
| if (pd_state && surface_state) { |
| bool is_supported = false; |
| for (uint32_t i = 0; i < pd_state->queue_family_properties.size(); i++) { |
| if (surface_state->GetQueueSupport(physicalDevice, i)) { |
| is_supported = true; |
| break; |
| } |
| } |
| if (!is_supported) { |
| skip |= LogError(physicalDevice, vuid, "%s(): surface is not supported by the physicalDevice.", func_name); |
| } |
| } |
| |
| return skip; |
| } |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| |
| bool CoreChecks::PreCallValidateGetDeviceGroupSurfacePresentModes2EXT(VkDevice device, |
| const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, |
| VkDeviceGroupPresentModeFlagsKHR *pModes) const { |
| bool skip = false; |
| |
| if (physical_device_count == 1) { |
| ValidationObject *device_object = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); |
| skip |= ValidatePhysicalDeviceSurfaceSupport(device_object->physical_device, pSurfaceInfo->surface, |
| "VUID-vkGetDeviceGroupSurfacePresentModes2EXT-pSurfaceInfo-06213", |
| "vkGetDeviceGroupSurfacePresentModes2EXT"); |
| } else { |
| for (uint32_t i = 0; i < physical_device_count; ++i) { |
| skip |= ValidatePhysicalDeviceSurfaceSupport(device_group_create_info.pPhysicalDevices[i], pSurfaceInfo->surface, |
| "VUID-vkGetDeviceGroupSurfacePresentModes2EXT-pSurfaceInfo-06213", |
| "vkGetDeviceGroupSurfacePresentModes2EXT"); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfacePresentModes2EXT(VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, |
| uint32_t *pPresentModeCount, |
| VkPresentModeKHR *pPresentModes) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, pSurfaceInfo->surface, |
| "VUID-vkGetPhysicalDeviceSurfacePresentModes2EXT-pSurfaceInfo-06210", |
| "vkGetPhysicalDeviceSurfacePresentModes2EXT"); |
| |
| return skip; |
| } |
| |
| #endif |
| |
| bool CoreChecks::PreCallValidateGetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, |
| VkDeviceGroupPresentModeFlagsKHR *pModes) const { |
| bool skip = false; |
| |
| if (physical_device_count == 1) { |
| ValidationObject *device_object = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); |
| skip |= ValidatePhysicalDeviceSurfaceSupport(device_object->physical_device, surface, |
| "VUID-vkGetDeviceGroupSurfacePresentModesKHR-surface-06212", |
| "vkGetDeviceGroupSurfacePresentModesKHR"); |
| } else { |
| for (uint32_t i = 0; i < physical_device_count; ++i) { |
| skip |= ValidatePhysicalDeviceSurfaceSupport(device_group_create_info.pPhysicalDevices[i], surface, |
| "VUID-vkGetDeviceGroupSurfacePresentModesKHR-surface-06212", |
| "vkGetDeviceGroupSurfacePresentModesKHR"); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
| uint32_t *pRectCount, VkRect2D *pRects) const { |
| bool skip = false; |
| |
| skip |= |
| ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface, "VUID-vkGetPhysicalDevicePresentRectanglesKHR-surface-06211", |
| "vkGetPhysicalDevicePresentRectanglesKHR"); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceCapabilities2EXT(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
| VkSurfaceCapabilities2EXT *pSurfaceCapabilities) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface, |
| "VUID-vkGetPhysicalDeviceSurfaceCapabilities2EXT-surface-06211", |
| "vkGetPhysicalDeviceSurfaceCapabilities2EXT"); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, |
| VkSurfaceCapabilities2KHR *pSurfaceCapabilities) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, pSurfaceInfo->surface, |
| "VUID-vkGetPhysicalDeviceSurfaceCapabilities2KHR-pSurfaceInfo-06210", |
| "vkGetPhysicalDeviceSurfaceCapabilities2KHR"); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
| VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface, |
| "VUID-vkGetPhysicalDeviceSurfaceCapabilitiesKHR-surface-06211", |
| "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, |
| uint32_t *pSurfaceFormatCount, |
| VkSurfaceFormat2KHR *pSurfaceFormats) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, pSurfaceInfo->surface, |
| "VUID-vkGetPhysicalDeviceSurfaceFormats2KHR-pSurfaceInfo-06210", |
| "vkGetPhysicalDeviceSurfaceFormats2KHR"); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
| uint32_t *pSurfaceFormatCount, |
| VkSurfaceFormatKHR *pSurfaceFormats) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface, "VUID-vkGetPhysicalDeviceSurfaceFormatsKHR-surface-06211", |
| "vkGetPhysicalDeviceSurfaceFormatsKHR"); |
| |
| return skip; |
| } |
| |
| bool CoreChecks::PreCallValidateGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
| uint32_t *pPresentModeCount, |
| VkPresentModeKHR *pPresentModes) const { |
| bool skip = false; |
| |
| skip |= ValidatePhysicalDeviceSurfaceSupport(physicalDevice, surface, |
| "VUID-vkGetPhysicalDeviceSurfacePresentModesKHR-surface-06211", |
| "vkGetPhysicalDeviceSurfacePresentModesKHR"); |
| |
| return skip; |
| } |
| |
| void CoreChecks::PostCallRecordGetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, |
| size_t dataSize, void *pData, VkDeviceSize stride, VkQueryResultFlags flags, |
| VkResult result) { |
| if (result != VK_SUCCESS) { |
| return; |
| } |
| auto query_pool_state = Get<QUERY_POOL_STATE>(queryPool); |
| if ((flags & VK_QUERY_RESULT_PARTIAL_BIT) == 0) { |
| for (uint32_t i = firstQuery; i < queryCount; ++i) { |
| query_pool_state->SetQueryState(i, 0, QUERYSTATE_AVAILABLE); |
| } |
| } |
| } |