blob: 9c51e48dce50f0f3455aae6c36656847e921af8d [file] [log] [blame]
/* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (C) 2015-2025 Google Inc.
* Modifications Copyright (C) 2020-2022 Advanced Micro Devices, Inc. All rights reserved.
* Modifications Copyright (C) 2022 RasterGrid Kft.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sstream>
#include <string>
#include <vector>
#include <vulkan/vk_enum_string_helper.h>
#include <vulkan/utility/vk_format_utils.h>
#include <vulkan/vulkan_core.h>
#include "containers/custom_containers.h"
#include "core_checks/cc_state_tracker.h"
#include "error_message/logging.h"
#include "state_tracker/last_bound_state.h"
#include "utils/assert_utils.h"
#include "utils/math_utils.h"
#include "utils/vk_struct_compare.h"
#include "core_validation.h"
#include "generated/enum_flag_bits.h"
#include "generated/dispatch_functions.h"
#include "drawdispatch/drawdispatch_vuids.h"
#include "chassis/chassis_modification_state.h"
#include "state_tracker/image_state.h"
#include "state_tracker/buffer_state.h"
#include "state_tracker/descriptor_sets.h"
#include "state_tracker/render_pass_state.h"
#include "state_tracker/cmd_buffer_state.h"
#include "state_tracker/pipeline_state.h"
#include "error_message/error_strings.h"
bool CoreChecks::PreCallValidateCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t count,
const VkGraphicsPipelineCreateInfo *pCreateInfos,
const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines,
const ErrorObject &error_obj, PipelineStates &pipeline_states,
chassis::CreateGraphicsPipelines &chassis_state) const {
bool skip = false;
skip |= ValidateDeviceQueueSupport(error_obj.location);
for (uint32_t i = 0; i < count; i++) {
const Location create_info_loc = error_obj.location.dot(Field::pCreateInfos, i);
skip |= ValidateGraphicsPipeline(*pipeline_states[i].get(), pCreateInfos[i].pNext, create_info_loc);
skip |= ValidateGraphicsPipelineDerivatives(pipeline_states, i, create_info_loc);
// From dumping traces, we found almost all apps only create one pipeline at a time. To greatly simplify the logic, only
// check the stateless validation in the pNext chain for the first pipeline. (The core issue is because we parse the SPIR-V
// at state tracking time, and we state track pipelines first)
if (i == 0) {
uint32_t stage_count = std::min(pCreateInfos[0].stageCount, kCommonMaxGraphicsShaderStages);
for (uint32_t stage = 0; stage < stage_count; stage++) {
if (chassis_state.stateless_data[stage].pipeline_pnext_module) {
skip |= stateless_spirv_validator.Validate(
*chassis_state.stateless_data[stage].pipeline_pnext_module, chassis_state.stateless_data[stage],
create_info_loc.dot(Field::pStages, stage).pNext(Struct::VkShaderModuleCreateInfo, Field::pCode));
}
}
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipeline(const vvl::Pipeline &pipeline, const void *pipeline_ci_pnext,
const Location &create_info_loc) const {
bool skip = false;
// It would be ideal to split all pipeline checks into Dynamic and Non-Dynamic renderpasses, but with GPL it gets a bit tricky.
// Also you might be deep in a function and it is easier to do a if/else check if it is dynamic rendering or not there.
vku::safe_VkSubpassDescription2 *subpass_desc = nullptr;
const auto rp_state = pipeline.RenderPassState();
if (rp_state && rp_state->UsesDynamicRendering()) {
skip |= ValidateGraphicsPipelineExternalFormatResolveDynamicRendering(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineDynamicRendering(pipeline, create_info_loc);
} else if (rp_state && !rp_state->UsesDynamicRendering()) {
const uint32_t subpass = pipeline.Subpass();
if (subpass >= rp_state->create_info.subpassCount) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06046", rp_state->Handle(),
create_info_loc.dot(Field::subpass), "(%" PRIu32 ") is out of range for this renderpass (0..%" PRIu32 ").",
subpass, rp_state->create_info.subpassCount - 1);
subpass_desc = nullptr;
} else {
subpass_desc = &rp_state->create_info.pSubpasses[subpass];
skip |= ValidateGraphicsPipelineExternalFormatResolve(pipeline, *rp_state, *subpass_desc, create_info_loc);
skip |= ValidateGraphicsPipelineMultisampleState(pipeline, *rp_state, *subpass_desc, create_info_loc);
skip |= ValidateGraphicsPipelineRenderPassRasterization(pipeline, *rp_state, *subpass_desc, create_info_loc);
if (subpass_desc->viewMask != 0) {
skip |= ValidateMultiViewShaders(pipeline, create_info_loc.dot(Field::pSubpasses, subpass).dot(Field::viewMask),
subpass_desc->viewMask, false);
}
}
// Check for portability errors
// Issue raised in https://gitlab.khronos.org/vulkan/vulkan/-/issues/3436
// The combination of GPL/DynamicRendering and Portability has spec issues that need to be clarified
if (IsExtEnabled(extensions.vk_khr_portability_subset) && !pipeline.IsGraphicsLibrary()) {
skip |= ValidateGraphicsPipelinePortability(pipeline, create_info_loc);
}
}
if (pipeline.OwnsLibState(pipeline.pre_raster_state) || pipeline.OwnsLibState(pipeline.fragment_shader_state)) {
vvl::unordered_map<VkShaderStageFlags, uint32_t> unique_stage_map;
uint32_t index = 0;
for (const auto &stage_ci : pipeline.shader_stages_ci) {
auto it = unique_stage_map.find(stage_ci.stage);
if (it != unique_stage_map.end()) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-stage-06897", device, create_info_loc.dot(Field::pStages, index),
"and pStages[%" PRIu32 "] both have %s", it->second, string_VkShaderStageFlagBits(stage_ci.stage));
return skip; // If we are here, you could have 2 vertex shaders and interface code will blow up
}
unique_stage_map[stage_ci.stage] = index;
index++;
}
}
// While these are split into the 4 sub-states from GPL, they are validated for normal pipelines too.
// These are VUs that fall strangely between both GPL and non-GPL pipelines
skip |= ValidateGraphicsPipelineVertexInputState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelinePreRasterizationState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineNullRenderPass(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineLibrary(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineInputAssemblyState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineTessellationState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineColorBlendState(pipeline, subpass_desc, create_info_loc);
skip |= ValidateGraphicsPipelineRasterizationState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineNullState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineRasterizationOrderAttachmentAccess(pipeline, subpass_desc, create_info_loc);
skip |= ValidateGraphicsPipelineDynamicState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineShaderState(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineBlendEnable(pipeline, create_info_loc);
skip |= ValidateGraphicsPipelineMeshTask(pipeline, create_info_loc);
// pStages are ignored if not using one of these library states
if (pipeline.OwnsLibState(pipeline.fragment_shader_state) || pipeline.OwnsLibState(pipeline.pre_raster_state)) {
uint32_t stage_index = 0;
for (const auto &stage_ci : pipeline.shader_stages_ci) {
skip |= ValidatePipelineShaderStage(pipeline, stage_ci, pipeline_ci_pnext,
create_info_loc.dot(Field::pStages, stage_index++));
}
}
const Location flags_loc = pipeline.GetCreateFlagsLoc(create_info_loc);
skip |= ValidatePipelineCacheControlFlags(pipeline.create_flags, flags_loc,
"VUID-VkGraphicsPipelineCreateInfo-pipelineCreationCacheControl-02878");
skip |= ValidatePipelineProtectedAccessFlags(pipeline.create_flags, flags_loc);
const void *pipeline_pnext = pipeline.GetCreateInfoPNext();
if (const auto *discard_rectangle_state =
vku::FindStructInPNextChain<VkPipelineDiscardRectangleStateCreateInfoEXT>(pipeline_pnext)) {
skip |= ValidatePipelineDiscardRectangleStateCreateInfo(pipeline, *discard_rectangle_state, create_info_loc);
}
// VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV
if (const auto attachment_sample_count_info = vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(pipeline_pnext)) {
skip |= ValidatePipelineAttachmentSampleCountInfo(pipeline, *attachment_sample_count_info, create_info_loc);
}
if (const auto *pipeline_robustness_info = vku::FindStructInPNextChain<VkPipelineRobustnessCreateInfo>(pipeline_pnext)) {
skip |= ValidatePipelineRobustnessCreateInfo(pipeline, *pipeline_robustness_info, create_info_loc);
}
if (const auto *fragment_shading_rate_state =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pipeline_pnext)) {
skip |= ValidateGraphicsPipelineFragmentShadingRateState(pipeline, *fragment_shading_rate_state, create_info_loc);
}
if (const auto *fragment_density_map_layered =
vku::FindStructInPNextChain<VkPipelineFragmentDensityMapLayeredCreateInfoVALVE>(pipeline_pnext)) {
if (fragment_density_map_layered->maxFragmentDensityMapLayers >
phys_dev_ext_props.fragment_density_map_layered_props.maxFragmentDensityMapLayers) {
skip |= LogError(
"VUID-VkPipelineFragmentDensityMapLayeredCreateInfoVALVE-maxFragmentDensityMapLayers-10825", device,
create_info_loc.pNext(Struct::VkPipelineFragmentDensityMapLayeredCreateInfoVALVE,
Field::maxFragmentDensityMapLayers),
"is %" PRIu32
" but the VkPhysicalDeviceFragmentDensityMapLayeredPropertiesVALVE::maxFragmentDensityMapLayers is %" PRIu32 ".",
fragment_density_map_layered->maxFragmentDensityMapLayers,
phys_dev_ext_props.fragment_density_map_layered_props.maxFragmentDensityMapLayers);
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelinePortability(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
if (!enabled_features.triangleFans && (pipeline.topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN)) {
skip |= LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-triangleFans-04452", device, create_info_loc,
"(portability error): VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN is not supported.");
}
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE)) {
// Validate vertex inputs
for (const auto &state : pipeline.vertex_input_state->bindings) {
const auto &desc = state.second.desc;
const uint32_t min_alignment = phys_dev_ext_props.portability_props.minVertexInputBindingStrideAlignment;
if (min_alignment != 0 && (desc.stride % min_alignment != 0)) {
skip |= LogError(
"VUID-VkVertexInputBindingDescription-stride-04456", device, create_info_loc,
"(portability error): Vertex input stride (%" PRIu32
") must be at least as large as and a "
"multiple of VkPhysicalDevicePortabilitySubsetPropertiesKHR::minVertexInputBindingStrideAlignment (%" PRIu32
").",
desc.stride, min_alignment);
}
}
// Validate vertex attributes
if (!enabled_features.vertexAttributeAccessBeyondStride) {
for (const auto &binding_state : pipeline.vertex_input_state->bindings) {
for (const auto &attrib_state : binding_state.second.locations) {
const auto &attrib = attrib_state.second.desc;
const auto &desc = binding_state.second.desc;
if ((attrib.offset + GetVertexInputFormatSize(attrib.format)) > desc.stride) {
skip |= LogError("VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457", device,
create_info_loc,
"(portability error): attribute.offset (%" PRIu32
") + "
"sizeof(vertex_description.format) (%" PRIu32
") is larger than the vertex stride (%" PRIu32 ").",
attrib.offset, GetVertexInputFormatSize(attrib.format), desc.stride);
}
}
}
}
}
if (const auto raster_state_ci = pipeline.RasterizationState()) {
// Validate polygon mode
if (!enabled_features.pointPolygons && !raster_state_ci->rasterizerDiscardEnable &&
(raster_state_ci->polygonMode == VK_POLYGON_MODE_POINT)) {
skip |= LogError("VUID-VkPipelineRasterizationStateCreateInfo-pointPolygons-04458", device, create_info_loc,
"(portability error): point polygons are not supported.");
}
// Validate depth-stencil state
if (!enabled_features.separateStencilMaskRef && (raster_state_ci->cullMode == VK_CULL_MODE_NONE)) {
const auto ds_state = pipeline.DepthStencilState();
if (ds_state && ds_state->stencilTestEnable && (ds_state->front.reference != ds_state->back.reference)) {
skip |= LogError("VUID-VkPipelineDepthStencilStateCreateInfo-separateStencilMaskRef-04453", device, create_info_loc,
"(portability error): VkStencilOpState::reference must be the "
"same for front and back.");
}
}
// Validate color attachments
const uint32_t subpass = pipeline.Subpass();
auto render_pass = Get<vvl::RenderPass>(pipeline.GraphicsCreateInfo().renderPass);
ASSERT_AND_RETURN_SKIP(render_pass);
const bool ignore_color_blend_state =
raster_state_ci->rasterizerDiscardEnable ||
(pipeline.rendering_create_info ? (pipeline.rendering_create_info->colorAttachmentCount == 0)
: (render_pass->create_info.pSubpasses[subpass].colorAttachmentCount == 0));
const auto *color_blend_state = pipeline.ColorBlendState();
if (!enabled_features.constantAlphaColorBlendFactors && !ignore_color_blend_state && color_blend_state) {
const auto attachments = color_blend_state->pAttachments;
for (uint32_t color_attachment_index = 0; color_attachment_index < color_blend_state->attachmentCount;
++color_attachment_index) {
if ((attachments[color_attachment_index].srcColorBlendFactor == VK_BLEND_FACTOR_CONSTANT_ALPHA) ||
(attachments[color_attachment_index].srcColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA)) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04454", device,
create_info_loc,
"(portability error): srcColorBlendFactor for color attachment %" PRIu32
" must "
"not be VK_BLEND_FACTOR_CONSTANT_ALPHA or VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA",
color_attachment_index);
}
if ((attachments[color_attachment_index].dstColorBlendFactor == VK_BLEND_FACTOR_CONSTANT_ALPHA) ||
(attachments[color_attachment_index].dstColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA)) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04455", device,
create_info_loc,
"(portability error): dstColorBlendFactor for color attachment %" PRIu32
" must "
"not be VK_BLEND_FACTOR_CONSTANT_ALPHA or VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA",
color_attachment_index);
}
}
}
}
return skip;
}
bool CoreChecks::ValidatePipelineLibraryCreateInfo(const vvl::Pipeline &pipeline,
const VkPipelineLibraryCreateInfoKHR &library_create_info,
const Location &create_info_loc) const {
bool skip = false;
const VkPipelineCreateFlags2 pipeline_flags = pipeline.create_flags;
const Location flags_loc = pipeline.GetCreateFlagsLoc(create_info_loc);
const bool has_link_time_opt = (pipeline_flags & VK_PIPELINE_CREATE_2_LINK_TIME_OPTIMIZATION_BIT_EXT) != 0;
const bool has_retain_link_time_opt = (pipeline_flags & VK_PIPELINE_CREATE_2_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT) != 0;
const bool has_capture_internal = (pipeline_flags & VK_PIPELINE_CREATE_2_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR) != 0;
bool uses_descriptor_buffer = false;
bool lib_all_has_capture_internal = false;
const auto gpl_info = vku::FindStructInPNextChain<VkGraphicsPipelineLibraryCreateInfoEXT>(pipeline.GetCreateInfoPNext());
for (uint32_t i = 0; i < library_create_info.libraryCount; ++i) {
const auto lib = Get<vvl::Pipeline>(library_create_info.pLibraries[i]);
if (!lib) continue;
const Location &library_loc = create_info_loc.pNext(Struct::VkPipelineLibraryCreateInfoKHR, Field::pLibraries, i);
const VkPipelineCreateFlags2 lib_pipeline_flags = lib->create_flags;
const bool lib_has_retain_link_time_opt =
(lib_pipeline_flags & VK_PIPELINE_CREATE_2_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT) != 0;
if (has_link_time_opt && !lib_has_retain_link_time_opt) {
const LogObjectList objlist(device, lib->Handle());
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06609", objlist, library_loc,
"(%s) was created with %s, which is missing "
"VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT.\n%s is %s.",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str(), flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
if (has_retain_link_time_opt && !lib_has_retain_link_time_opt) {
const LogObjectList objlist(device, lib->Handle());
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06610", objlist, library_loc,
"(%s) was created with %s, which is missing "
"VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT\n%s is %s.",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str(), flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
const bool lib_has_capture_internal =
(lib_pipeline_flags & VK_PIPELINE_CREATE_2_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR) != 0;
const bool non_zero_gpl = gpl_info && gpl_info->flags != 0;
if (lib_has_capture_internal) {
lib_all_has_capture_internal = true;
if (!has_capture_internal && non_zero_gpl) {
const Location &gpl_flags_loc = create_info_loc.pNext(Struct::VkGraphicsPipelineLibraryCreateInfoEXT, Field::flags);
const LogObjectList objlist(device, lib->Handle());
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pLibraries-06647", objlist, library_loc,
"(%s) was created with %s\n"
"%s is %s\n"
"%s is %s.",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str(), gpl_flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(gpl_info->flags).c_str(), flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
} else {
if (lib_all_has_capture_internal) {
const LogObjectList objlist(device, lib->Handle());
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-pLibraries-06646", objlist, library_loc,
"(%s) was created with %s.", string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str());
} else if (has_capture_internal && non_zero_gpl) {
const Location &gpl_flags_loc = create_info_loc.pNext(Struct::VkGraphicsPipelineLibraryCreateInfoEXT, Field::flags);
const LogObjectList objlist(device, lib->Handle());
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06645", objlist, library_loc,
"(%s) was created with %s\n"
"%s is %s\n"
"%s is %s.",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str(), gpl_flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(gpl_info->flags).c_str(), flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
}
if ((lib->uses_shader_module_id) && !(pipeline_flags & VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT)) {
skip |= LogError("VUID-VkPipelineLibraryCreateInfoKHR-pLibraries-06855", device, library_loc,
"(%s) was created with %s but VkPipelineShaderStageModuleIdentifierCreateInfoEXT::identifierSize was "
"not equal to 0 for the pipeline",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str());
}
struct check_struct {
VkPipelineCreateFlagBits2 bit;
std::string first_vuid;
std::string second_vuid;
};
static const std::array<check_struct, 2> check_infos = {
{{VK_PIPELINE_CREATE_2_NO_PROTECTED_ACCESS_BIT, "VUID-VkPipelineLibraryCreateInfoKHR-pipeline-07404",
"VUID-VkPipelineLibraryCreateInfoKHR-pipeline-07405"},
{VK_PIPELINE_CREATE_2_PROTECTED_ACCESS_ONLY_BIT, "VUID-VkPipelineLibraryCreateInfoKHR-pipeline-07406",
"VUID-VkPipelineLibraryCreateInfoKHR-pipeline-07407"}}};
for (const auto &check_info : check_infos) {
if ((pipeline_flags & check_info.bit)) {
if (!(lib_pipeline_flags & check_info.bit)) {
const LogObjectList objlist(device, lib->Handle());
skip |= LogError(check_info.first_vuid.c_str(), objlist, library_loc,
"(%s) was created with %s, which is missing %s included in %s (%s).",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str(),
string_VkPipelineCreateFlagBits2(check_info.bit), flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
} else {
if ((lib_pipeline_flags & check_info.bit)) {
const LogObjectList objlist(device, lib->Handle());
skip |= LogError(check_info.second_vuid.c_str(), objlist, library_loc,
"(%s) was created with %s, which includes %s not included in %s (%s).",
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(),
string_VkPipelineCreateFlags2(lib_pipeline_flags).c_str(),
string_VkPipelineCreateFlagBits2(check_info.bit), flags_loc.Fields().c_str(),
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
}
}
if (i == 0) {
uses_descriptor_buffer = lib->descriptor_buffer_mode;
} else if (uses_descriptor_buffer != lib->descriptor_buffer_mode) {
skip |= LogError("VUID-VkPipelineLibraryCreateInfoKHR-pLibraries-08096", device, library_loc,
"%s created with VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT which is opposite of pLibraries[0].",
lib->descriptor_buffer_mode ? "was" : "was not");
break; // no point keep checking as might have many of same error
}
}
if (pipeline.GraphicsCreateInfo().renderPass == VK_NULL_HANDLE) {
if (gpl_info) {
skip |= ValidatePipelineLibraryFlags(gpl_info->flags, library_create_info, pipeline.rendering_create_info,
create_info_loc, -1, "VUID-VkGraphicsPipelineCreateInfo-flags-06626");
const uint32_t flags_count =
GetBitSetCount(gpl_info->flags & (VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT));
if (flags_count >= 1 && flags_count <= 2) {
for (uint32_t i = 0; i < library_create_info.libraryCount; ++i) {
const auto lib = Get<vvl::Pipeline>(library_create_info.pLibraries[i]);
if (!lib) continue;
const auto lib_gpl_info =
vku::FindStructInPNextChain<VkGraphicsPipelineLibraryCreateInfoEXT>(lib->GetCreateInfoPNext());
if (!lib_gpl_info) {
continue;
}
const std::array<VkGraphicsPipelineLibraryFlagBitsEXT, 3> flags = {
VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT,
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT};
for (const auto flag : flags) {
if ((lib_gpl_info->flags & flag) > 0 && (gpl_info->flags & flag) == 0) {
break;
}
}
}
}
}
for (uint32_t i = 0; i < library_create_info.libraryCount; ++i) {
const auto lib = Get<vvl::Pipeline>(library_create_info.pLibraries[i]);
if (!lib) continue;
skip |= ValidatePipelineLibraryFlags(lib->graphics_lib_type, library_create_info, lib->rendering_create_info,
create_info_loc, i, "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06627");
}
}
return skip;
}
// vkspec.html#pipelines-graphics-subsets-vertex-input
bool CoreChecks::ValidateGraphicsPipelineVertexInputState(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
// If using a mesh shader, all vertex input is ignored
if (!pipeline.OwnsLibState(pipeline.vertex_input_state) || (pipeline.active_shaders & VK_SHADER_STAGE_MESH_BIT_EXT)) {
return skip;
}
const bool ignore_vertex_input_state = pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT);
const bool ignore_input_assembly_state = IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE) &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) &&
phys_dev_ext_props.extended_dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted;
if (!ignore_vertex_input_state) {
skip |= ValidatePipelineVertexDivisors(pipeline, create_info_loc);
}
const auto *input_state = pipeline.InputState();
const auto *assembly_state = pipeline.InputAssemblyState();
const bool invalid_input_state = !ignore_vertex_input_state && !input_state;
const bool invalid_assembly_state = !ignore_input_assembly_state && !assembly_state;
if (invalid_input_state && invalid_assembly_state && pipeline.IsGraphicsLibrary()) {
// Failed to defined a Vertex Input State
if (!pipeline.pre_raster_state) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-flags-08898", device, create_info_loc,
"pVertexInputState and pInputAssemblyState are both NULL which means this is an invalid Vertex Input "
"State.%s"
"\npInputAssemblyState can be NULL if all the following set:"
"\n Enable VK_EXT_extended_dynamic_state3 (%senabled)"
"\n Use VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE (%s)"
"\n Use VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY (%s)"
"\n dynamicPrimitiveTopologyUnrestricted is VK_TRUE (%s)"
"\nIf there is a mesh stage, these can also be NULL, but the Vertex Input library can also just be skipped if "
"creating a mesh pipeline.",
pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT)
? ""
: "\npVertexInputState can be NULL if using VK_DYNAMIC_STATE_VERTEX_INPUT_EXT",
IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ? "" : "not ",
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) ? "set" : "not set",
phys_dev_ext_props.extended_dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted ? "VK_TRUE" : "VK_FALSE");
} else if ((pipeline.active_shaders & VK_SHADER_STAGE_VERTEX_BIT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-flags-08897", device, create_info_loc,
"pVertexInputState and pInputAssemblyState are both NULL which means this is an invalid Vertex Input "
"State.%s"
"\npInputAssemblyState can be NULL if all the following set:"
"\n Enable VK_EXT_extended_dynamic_state3 (%senabled)"
"\n Use VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE (%s)"
"\n Use VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY (%s)"
"\n dynamicPrimitiveTopologyUnrestricted is VK_TRUE (%s)",
pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT)
? ""
: "\npVertexInputState can be NULL if using VK_DYNAMIC_STATE_VERTEX_INPUT_EXT",
IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ? "" : "not ",
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) ? "set" : "not set",
phys_dev_ext_props.extended_dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted ? "VK_TRUE" : "VK_FALSE");
}
} else if (invalid_input_state) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-02097", device, create_info_loc.dot(Field::pVertexInputState),
"is NULL.");
} else if (invalid_assembly_state) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-dynamicPrimitiveTopologyUnrestricted-09031", device,
create_info_loc.dot(Field::pInputAssemblyState), "is NULL.");
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineNullRenderPass(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
// If the vertex input is by itself renderpass is ignored
if (!pipeline.IsRenderPassStateRequired()) return skip;
// If the VkRenderPass is null, it can be from a legit Dynamic Rendering pass
if (pipeline.IsRenderPassNull()) {
if (!enabled_features.dynamicRendering) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576", device,
create_info_loc.dot(Field::renderPass), "is NULL, but the dynamicRendering feature was not enabled");
}
} else if (!pipeline.RenderPassState()) {
// If the vvl::RenderPass object is not found AND the handle is not null, this we know this is an invalid render pass
const auto gpl_info = vku::FindStructInPNextChain<VkGraphicsPipelineLibraryCreateInfoEXT>(pipeline.GetCreateInfoPNext());
const bool has_flags =
gpl_info && (gpl_info->flags & (VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT)) != 0;
const char *vuid =
has_flags ? "VUID-VkGraphicsPipelineCreateInfo-flags-06643" : "VUID-VkGraphicsPipelineCreateInfo-renderPass-06603";
skip |= LogError(vuid, device, create_info_loc.dot(Field::renderPass), "is not a valid render pass.");
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineLibrary(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const VkPipelineCreateFlags2 pipeline_flags = pipeline.create_flags;
const Location flags_loc = pipeline.GetCreateFlagsLoc(create_info_loc);
const bool is_create_library = (pipeline_flags & VK_PIPELINE_CREATE_2_LIBRARY_BIT_KHR) != 0;
// It is possible to have no FS state in a complete pipeline whether or not GPL is used
if (pipeline.OwnsLibState(pipeline.pre_raster_state) && !pipeline.OwnsLibState(pipeline.fragment_shader_state) &&
((pipeline.create_info_shaders & FragmentShaderState::ValidShaderStages()) != 0)) {
// Hint to help as shown from https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8761
const char *hint = is_create_library ? "" : " (Is rasterizerDiscardEnable mistakenly set to VK_TRUE?)";
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-06894", device, create_info_loc,
"does not have fragment shader state, but stages (%s) contains VK_SHADER_STAGE_FRAGMENT_BIT.%s",
string_VkShaderStageFlags(pipeline.create_info_shaders).c_str(), hint);
}
if (!pipeline.OwnsLibState(pipeline.fragment_shader_state) && !pipeline.OwnsLibState(pipeline.pre_raster_state) &&
pipeline.shader_stages_ci.size() > 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-stageCount-09587", device, create_info_loc.dot(Field::stageCount),
"is %zu, but the pipeline does not have a pre-rasterization or fragment shader state.",
pipeline.shader_stages_ci.size());
}
if (is_create_library) {
if (!enabled_features.graphicsPipelineLibrary) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-graphicsPipelineLibrary-06606", device, flags_loc,
"(%s) includes VK_PIPELINE_CREATE_LIBRARY_BIT_KHR, but "
"graphicsPipelineLibrary feature is not enabled.",
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
} else {
// check to make sure all required library states are here
// Note: You don't require a vertex input state (ex. if using Mesh Shaders)
if (!pipeline.pre_raster_state) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-08901", device, create_info_loc,
"Attempting to link pipeline libraries without a pre-rasterization shader state (did you forget to "
"add VK_PIPELINE_CREATE_LIBRARY_BIT_KHR to your intermediate pipeline?).");
} else if (pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE) || !pipeline.RasterizationDisabled()) {
if (!pipeline.fragment_shader_state) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-08909", device, create_info_loc,
"Attempting to link pipeline libraries without a fragment shader state (did you forget to add "
"VK_PIPELINE_CREATE_LIBRARY_BIT_KHR to your intermediate pipeline?).");
} else if (!pipeline.fragment_output_state) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-08909", device, create_info_loc,
"Attempting to link pipeline libraries without a fragment output state (did you forget to add "
"VK_PIPELINE_CREATE_LIBRARY_BIT_KHR to your intermediate pipeline?).");
}
}
}
if (pipeline.OwnsLibState(pipeline.fragment_shader_state) && !pipeline.OwnsLibState(pipeline.pre_raster_state) &&
((pipeline.create_info_shaders & PreRasterState::ValidShaderStages()) != 0)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-06895", device, create_info_loc,
"does not have pre-raster state, but stages (%s) contains pre-raster shader stages.",
string_VkShaderStageFlags(pipeline.create_info_shaders).c_str());
}
// note this is the incoming layout an not ones from the pipeline library
const auto &pipeline_ci = pipeline.GraphicsCreateInfo();
const auto pipeline_layout_state = Get<vvl::PipelineLayout>(pipeline_ci.layout);
if (pipeline.HasFullState()) {
if (is_create_library) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06608", device, flags_loc,
"(%s) includes VK_PIPELINE_CREATE_LIBRARY_BIT_KHR, but defines a complete set of state.",
string_VkPipelineCreateFlags2(pipeline_flags).c_str());
}
// A valid pipeline layout must _always_ be provided, even if the pipeline is defined completely from libraries.
// This a change from the original GPL spec. See https://gitlab.khronos.org/vulkan/vulkan/-/issues/3334 for some
// context
// If libraries are included then pipeline layout can be NULL. See
// https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/6144
if (!pipeline_layout_state && (!pipeline.library_create_info || pipeline.library_create_info->libraryCount == 0)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-None-07826", device, create_info_loc.dot(Field::layout),
"is not a valid VkPipelineLayout, but defines a complete set of state.");
}
// graphics_lib_type effectively tracks which parts of the pipeline are defined by graphics libraries.
// If the complete state is defined by libraries, we need to check for compatibility with each library's layout
const bool from_libraries_only = pipeline.graphics_lib_type == AllVkGraphicsPipelineLibraryFlagBitsEXT;
if (from_libraries_only) {
const VkPipelineLayout linking_layout_handle =
pipeline_layout_state ? pipeline_layout_state->VkHandle() : VK_NULL_HANDLE;
const VkPipelineLayout pre_raster_layout_handle =
pipeline.PreRasterPipelineLayoutState() ? pipeline.PreRasterPipelineLayoutState()->VkHandle() : VK_NULL_HANDLE;
const VkPipelineLayout fs_layout_handle = pipeline.FragmentShaderPipelineLayoutState()
? pipeline.FragmentShaderPipelineLayoutState()->VkHandle()
: VK_NULL_HANDLE;
const bool pre_raster_independent_set = pipeline.pre_raster_state && pipeline.pre_raster_state->IsIndependentSets();
// NOTE: it is possible for an executable pipeline to not contain FS state
const bool fs_independent_set = pipeline.fragment_shader_state && pipeline.fragment_shader_state->IsIndependentSets();
if (!pre_raster_independent_set && !fs_independent_set) {
// The layout defined at link time must be compatible with each (pre-raster and fragment shader) library state's
// layout (vertex input and fragment output state do not contain a layout)
if (pipeline_layout_state) {
if (std::string err_msg; !VerifyPipelineLayoutCompatibility(
*pipeline_layout_state, *pipeline.PreRasterPipelineLayoutState(), err_msg)) {
LogObjectList objlist(linking_layout_handle, pre_raster_layout_handle);
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-layout-07827", objlist, create_info_loc.dot(Field::layout),
"(%s) is incompatible with the %s specified in the pre-rasterization library\n%s",
FormatHandle(linking_layout_handle).c_str(), FormatHandle(pre_raster_layout_handle).c_str(),
err_msg.c_str());
}
if (std::string err_msg; !VerifyPipelineLayoutCompatibility(
*pipeline_layout_state, *pipeline.FragmentShaderPipelineLayoutState(), err_msg)) {
LogObjectList objlist(linking_layout_handle, fs_layout_handle);
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-layout-07827", objlist, create_info_loc.dot(Field::layout),
"(%s) is incompatible with the %s specified in the fragment shader library: %s",
FormatHandle(linking_layout_handle).c_str(), FormatHandle(fs_layout_handle).c_str(), err_msg.c_str());
}
} else {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-layout-07827", device, create_info_loc.dot(Field::layout),
"is null/invalid and therefore not compatible with the libraries layout");
}
}
if (pre_raster_independent_set && fs_independent_set) {
const bool has_link_time_opt = (pipeline.create_flags & VK_PIPELINE_CREATE_2_LINK_TIME_OPTIMIZATION_BIT_EXT) != 0;
const char *vuid = has_link_time_opt ? "VUID-VkGraphicsPipelineCreateInfo-flags-06729"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06730";
if (pipeline_layout_state) {
std::string err_msg;
if (!VerifyPipelineLayoutCompatibilityUnion(*pipeline_layout_state, *pipeline.PreRasterPipelineLayoutState(),
*pipeline.FragmentShaderPipelineLayoutState(), err_msg)) {
LogObjectList objlist(linking_layout_handle, pre_raster_layout_handle, fs_layout_handle);
skip |=
LogError(vuid, objlist, create_info_loc.dot(Field::layout),
"(%s) is incompatible with the layout specified in the union of (pre-rasterization, fragment "
"shader) libraries.\n%s",
FormatHandle(linking_layout_handle).c_str(), err_msg.c_str());
}
// Special case where we also need to check for linking independent set
if (!has_link_time_opt && !pipeline_layout_state->IsIndependentSets()) {
LogObjectList objlist(linking_layout_handle, pre_raster_layout_handle, fs_layout_handle);
skip |= LogError(
vuid, objlist, create_info_loc.dot(Field::layout),
"(%s) was not created with VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT, but the "
"pre-rasterization (%s) "
"and fragment shader (%s) were created with VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT.\nThis "
"is only allowed if you use VK_PIPELINE_CREATE_2_LINK_TIME_OPTIMIZATION_BIT_EXT as well.",
FormatHandle(linking_layout_handle).c_str(), FormatHandle(pre_raster_layout_handle).c_str(),
FormatHandle(fs_layout_handle).c_str());
}
} else {
skip |= LogError(vuid, device, create_info_loc.dot(Field::layout),
"is null/invalid and therefore not compatible with the union of libraries layout");
}
}
}
}
// The pipeline libraries can be declared in two different ways
enum GPLInitType : uint8_t {
uninitialized = 0,
gpl_flags, // VkGraphicsPipelineLibraryCreateInfoEXT::flags
link_libraries, // VkPipelineLibraryCreateInfoKHR::pLibraries
};
struct GPLValidInfo {
GPLInitType init = GPLInitType::uninitialized;
VkPipelineLayoutCreateFlags flags = VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM;
const vvl::PipelineLayout *layout = nullptr;
// Can't use MultisampleState() to get this value as we are checking here if they are identical
const VkPipelineMultisampleStateCreateInfo *ms_state = nullptr;
const VkPipelineFragmentShadingRateStateCreateInfoKHR *shading_rate_state = nullptr;
};
GPLValidInfo pre_raster_info;
GPLValidInfo frag_shader_info;
GPLValidInfo frag_output_info;
const auto gpl_info = vku::FindStructInPNextChain<VkGraphicsPipelineLibraryCreateInfoEXT>(pipeline_ci.pNext);
if (gpl_info) {
if (gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) {
pre_raster_info.init = GPLInitType::gpl_flags;
pre_raster_info.flags =
(pipeline.PreRasterPipelineLayoutState()) ? pipeline.PreRasterPipelineLayoutState()->CreateFlags() : 0;
pre_raster_info.layout = pipeline.PreRasterPipelineLayoutState().get();
pre_raster_info.shading_rate_state =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pipeline_ci.pNext);
}
if (gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) {
frag_shader_info.init = GPLInitType::gpl_flags;
frag_shader_info.flags =
(pipeline.FragmentShaderPipelineLayoutState()) ? pipeline.FragmentShaderPipelineLayoutState()->CreateFlags() : 0;
frag_shader_info.layout = pipeline.FragmentShaderPipelineLayoutState().get();
if (pipeline.fragment_shader_state->ms_state) {
frag_shader_info.ms_state = pipeline.fragment_shader_state->ms_state.get()->ptr();
}
frag_shader_info.shading_rate_state =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pipeline_ci.pNext);
}
if (gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) {
frag_output_info.init = GPLInitType::gpl_flags;
if (pipeline.fragment_output_state->ms_state) {
frag_output_info.ms_state = pipeline.fragment_output_state->ms_state.get()->ptr();
}
}
}
if (pipeline.library_create_info) {
skip |= ValidatePipelineLibraryCreateInfo(pipeline, *pipeline.library_create_info, create_info_loc);
for (uint32_t i = 0; i < pipeline.library_create_info->libraryCount; ++i) {
const auto lib = Get<vvl::Pipeline>(pipeline.library_create_info->pLibraries[i]);
if (!lib) continue;
const auto &lib_ci = lib->GraphicsCreateInfo();
if (lib->graphics_lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) {
pre_raster_info.init = GPLInitType::link_libraries;
const auto layout_state = lib->PreRasterPipelineLayoutState();
if (layout_state) {
pre_raster_info.flags = layout_state->CreateFlags();
pre_raster_info.layout = layout_state.get();
}
pre_raster_info.shading_rate_state =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(lib_ci.pNext);
}
if (lib->graphics_lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) {
frag_shader_info.init = GPLInitType::link_libraries;
const auto layout_state = lib->FragmentShaderPipelineLayoutState();
if (layout_state) {
frag_shader_info.flags = layout_state->CreateFlags();
frag_shader_info.layout = layout_state.get();
}
if (lib->fragment_shader_state->ms_state) {
frag_shader_info.ms_state = lib->fragment_shader_state->ms_state.get()->ptr();
}
frag_shader_info.shading_rate_state =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(lib_ci.pNext);
}
if (lib->graphics_lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) {
frag_output_info.init = GPLInitType::link_libraries;
if (lib->fragment_output_state->ms_state) {
frag_output_info.ms_state = lib->fragment_output_state->ms_state.get()->ptr();
}
}
}
}
if ((pipeline.OwnsLibState(pipeline.pre_raster_state) || pipeline.OwnsLibState(pipeline.fragment_shader_state)) &&
!pipeline_layout_state) {
const char *vuid = (pre_raster_info.init == GPLInitType::gpl_flags || frag_shader_info.init == GPLInitType::gpl_flags)
? "VUID-VkGraphicsPipelineCreateInfo-flags-06642"
: "VUID-VkGraphicsPipelineCreateInfo-layout-06602";
skip |= LogError(vuid, device, create_info_loc.dot(Field::layout), "is not a valid VkPipelineLayout.");
}
if (pipeline_layout_state && pre_raster_info.init == GPLInitType::gpl_flags) {
for (uint32_t i = 0; i < pipeline_layout_state->set_layouts.size(); i++) {
if (pipeline_layout_state->set_layouts[i] != nullptr) {
continue;
}
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE) && pipeline.RasterizationDisabled()) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06683", device,
create_info_loc.pNext(Struct::VkGraphicsPipelineLibraryCreateInfoEXT, Field::flags),
"is %s, but layout was created with pSetLayouts[%" PRIu32 "] == VK_NULL_HANDLE",
string_VkGraphicsPipelineLibraryFlagsEXT(gpl_info->flags).c_str(), i);
} else if (frag_shader_info.init == GPLInitType::gpl_flags) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06682", device,
create_info_loc.pNext(Struct::VkGraphicsPipelineLibraryCreateInfoEXT, Field::flags),
"is %s, but layout was created with pSetLayouts[%" PRIu32 "] == VK_NULL_HANDLE",
string_VkGraphicsPipelineLibraryFlagsEXT(gpl_info->flags).c_str(), i);
}
}
}
// pre-raster and fragemnt shader state is defined by some combination of this library and pLibraries
if (pre_raster_info.init != GPLInitType::uninitialized && frag_shader_info.init != GPLInitType::uninitialized) {
if (!pre_raster_info.layout || !frag_shader_info.layout) {
// This will occur if 06642 is violated, if here, just exit as things are invalid already
// Put here, and not at 06642 to allow the other checks that don't care about pipeline layouts
return skip;
}
// For VUs saying "includes only one... pLibraries includes the other flag" this would be when |only_libs == false|
// This is because it is not valid to have both init be from |gpl_flags|
const bool only_libs =
(pre_raster_info.init == GPLInitType::link_libraries) && (frag_shader_info.init == GPLInitType::link_libraries);
// Check for consistent independent sets across libraries
const auto pre_raster_independant_set = (pre_raster_info.flags & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT);
const auto fs_independant_set = (frag_shader_info.flags & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT);
const bool not_independent_sets = !pre_raster_independant_set && !fs_independant_set;
if (pre_raster_independant_set ^ fs_independant_set) {
const char *vuid =
only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06615" : "VUID-VkGraphicsPipelineCreateInfo-flags-06614";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(
vuid, objlist, create_info_loc,
"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 defined with VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT, "
"and the fragment shader layout create flags (%s) are %s defined with "
"VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT",
string_VkPipelineLayoutCreateFlags(pre_raster_info.flags).c_str(), (pre_raster_independant_set != 0) ? "" : "not",
string_VkPipelineLayoutCreateFlags(frag_shader_info.flags).c_str(), (fs_independant_set != 0) ? "" : "not");
} else if (not_independent_sets) {
// "layout used by this pipeline and the library must be identically defined"
// Inside VkPipelineLayoutCreateInfo, |pSetLayouts| and |pPushConstantRanges| are checked below, this leaves this VU to
// cover everything else. Currently no valid pNext structs are extending pipeline layout creation
if (pre_raster_info.layout->create_flags != frag_shader_info.layout->create_flags) {
const char *vuid = only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06613"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06612";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc,
"VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT was not set and the graphics pipeline library "
"have different VkPipelineLayouts, "
"pre-raster layout create flags (%s) and fragment shader layout create flags (%s).",
string_VkPipelineLayoutCreateFlags(pre_raster_info.layout->create_flags).c_str(),
string_VkPipelineLayoutCreateFlags(frag_shader_info.layout->create_flags).c_str());
}
}
// Push Constants must match regardless of VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT
if (!pre_raster_info.layout->push_constant_ranges_layout->empty() &&
!frag_shader_info.layout->push_constant_ranges_layout->empty()) {
const uint32_t pre_raster_count = static_cast<uint32_t>(pre_raster_info.layout->push_constant_ranges_layout->size());
const uint32_t frag_shader_count = static_cast<uint32_t>(frag_shader_info.layout->push_constant_ranges_layout->size());
if (pre_raster_count != frag_shader_count) {
const char *vuid = only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06621"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06620";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc,
"the graphics pipeline library have different push constants, pre-raster layout has "
"pushConstantRangeCount of %" PRIu32
", fragment shader layout has pushConstantRangeCount of %" PRIu32 ".",
pre_raster_count, frag_shader_count);
} else {
for (uint32_t i = 0; i < pre_raster_count; i++) {
VkPushConstantRange pre_raster_range = pre_raster_info.layout->push_constant_ranges_layout->at(i);
VkPushConstantRange frag_shader_range = frag_shader_info.layout->push_constant_ranges_layout->at(i);
if (pre_raster_range.stageFlags != frag_shader_range.stageFlags ||
pre_raster_range.offset != frag_shader_range.offset || pre_raster_range.size != frag_shader_range.size) {
const char *vuid = only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06621"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06620";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc,
"the graphics pipeline library have different push constants, pre-raster layout has "
"pPushConstantRanges[%" PRIu32 "] of {%s, %" PRIu32 ", %" PRIu32
"}, fragment shader layout has pPushConstantRanges[%" PRIu32 "] of {%s, %" PRIu32
", %" PRIu32 "}.",
i, string_VkShaderStageFlags(pre_raster_range.stageFlags).c_str(), pre_raster_range.offset,
pre_raster_range.size, i, string_VkShaderStageFlags(frag_shader_range.stageFlags).c_str(),
frag_shader_range.offset, frag_shader_range.size);
}
}
}
}
// Check VkPipelineFragmentShadingRateStateCreateInfoKHR
if (frag_shader_info.shading_rate_state || pre_raster_info.shading_rate_state) {
const char *vuid =
only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06639" : "VUID-VkGraphicsPipelineCreateInfo-flags-06638";
if (!pre_raster_info.shading_rate_state) {
skip |= LogError(
vuid, device, create_info_loc,
"Fragment Shader has a valid VkPipelineFragmentShadingRateStateCreateInfoKHR, but Pre Rasterization has "
"no VkPipelineFragmentShadingRateStateCreateInfoKHR in the pNext chain.");
} else if (!frag_shader_info.shading_rate_state) {
skip |= LogError(
vuid, device, create_info_loc,
"Pre Rasterization has a valid VkPipelineFragmentShadingRateStateCreateInfoKHR, but Fragment Shader has "
"no VkPipelineFragmentShadingRateStateCreateInfoKHR in the pNext chain.");
} else if (!ComparePipelineFragmentShadingRateStateCreateInfo(*frag_shader_info.shading_rate_state,
*pre_raster_info.shading_rate_state)) {
skip |= LogError(vuid, device, create_info_loc,
"Fragment Shader and Pre Rasterization were created with different "
"VkPipelineFragmentShadingRateStateCreateInfoKHR.\n"
"Fragment Shader:\n"
"\tfragmentSize: (W = %" PRIu32 ", H = %" PRIu32
")\n"
"\tcombinerOps[0]: %s\n"
"\tcombinerOps[1]: %s\n"
"Pre Rasterization:\n"
"\tfragmentSize: (W = %" PRIu32 ", H = %" PRIu32
")\n"
"\tcombinerOps[0]: %s\n"
"\tcombinerOps[1]: %s\n",
frag_shader_info.shading_rate_state->fragmentSize.width,
frag_shader_info.shading_rate_state->fragmentSize.height,
string_VkFragmentShadingRateCombinerOpKHR(frag_shader_info.shading_rate_state->combinerOps[0]),
string_VkFragmentShadingRateCombinerOpKHR(frag_shader_info.shading_rate_state->combinerOps[1]),
pre_raster_info.shading_rate_state->fragmentSize.width,
pre_raster_info.shading_rate_state->fragmentSize.height,
string_VkFragmentShadingRateCombinerOpKHR(pre_raster_info.shading_rate_state->combinerOps[0]),
string_VkFragmentShadingRateCombinerOpKHR(pre_raster_info.shading_rate_state->combinerOps[1]));
}
}
// Check for consistent shader bindings + layout across libraries
const auto &pre_raster_set_layouts = pre_raster_info.layout->set_layouts;
const auto &fs_set_layouts = frag_shader_info.layout->set_layouts;
const uint32_t pre_raster_count = static_cast<uint32_t>(pre_raster_set_layouts.size());
const uint32_t frag_shader_count = static_cast<uint32_t>(fs_set_layouts.size());
if (not_independent_sets && pre_raster_count != frag_shader_count) {
const char *vuid =
only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06613" : "VUID-VkGraphicsPipelineCreateInfo-flags-06612";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc,
"VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT was not set and the graphics pipeline library "
"have different pipeline layouts, pre-raster layout has "
"setLayoutCount of %" PRIu32 ", fragment shader layout has setLayoutCount of %" PRIu32 ".",
pre_raster_count, frag_shader_count);
}
const auto num_set_layouts = std::max(pre_raster_count, frag_shader_count);
for (uint32_t i = 0; i < num_set_layouts; ++i) {
// if using VK_NULL_HANDLE, index into set_layouts will be null
const auto pre_raster_dsl = i < pre_raster_count ? pre_raster_set_layouts[i] : nullptr;
const auto fs_dsl = i < frag_shader_count ? fs_set_layouts[i] : nullptr;
if (!pre_raster_dsl && fs_dsl) {
// Null DSL at pSetLayouts[i] in pre-raster state. Make sure that shader bindings in corresponding DSL in
// fragment shader state do not overlap.
for (const auto &fs_binding : fs_dsl->GetBindings()) {
if ((fs_binding.stageFlags & PreRasterState::ValidShaderStages()) == 0) {
continue;
}
const auto pre_raster_layout_handle_str = FormatHandle(pre_raster_info.layout->Handle());
const auto fs_layout_handle_str = FormatHandle(frag_shader_info.layout->Handle());
const char *vuid = nullptr;
std::ostringstream msg;
if (pre_raster_info.init == GPLInitType::gpl_flags) {
vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06756";
msg << "represents a library containing pre-raster state, and descriptor set layout (from "
"layout "
<< pre_raster_layout_handle_str << ") at pSetLayouts[" << i << "] is NULL. "
<< "However, a library with fragment shader state is specified in "
"VkPipelineLibraryCreateInfoKHR::pLibraries with non-null descriptor set layout at the "
"same pSetLayouts index ("
<< i << ") from layout " << fs_layout_handle_str << " and bindings ("
<< string_VkShaderStageFlags(fs_binding.stageFlags) << ") that overlap with pre-raster state.";
} else if (frag_shader_info.init == GPLInitType::gpl_flags) {
vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06757";
msg << "represents a library containing fragment shader state, and descriptor set layout (from "
"layout "
<< fs_layout_handle_str << ") at pSetLayouts[" << i << "] contains bindings ("
<< string_VkShaderStageFlags(fs_binding.stageFlags) << ") that overlap with pre-raster state. "
<< "However, a library with pre-raster state is specified in "
"VkPipelineLibraryCreateInfoKHR::pLibraries with a null descriptor set layout at the "
"same pSetLayouts index ("
<< i << ") from layout " << pre_raster_layout_handle_str << ".";
} else {
vuid = "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06758";
msg << "is linking libraries with pre-raster and fragment shader state. The descriptor set "
"layout at index "
<< i << " in pSetLayouts from " << pre_raster_layout_handle_str << " in the pre-raster state is NULL. "
<< "However, the descriptor set layout at the same index (" << i << ") in " << fs_layout_handle_str
<< " is non-null with bindings (" << string_VkShaderStageFlags(fs_binding.stageFlags)
<< ") that overlap with pre-raster state.";
}
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc, "%s", msg.str().c_str());
break;
}
} else if (pre_raster_dsl && !fs_dsl) {
// Null DSL at pSetLayouts[i] in FS state. Make sure that shader bindings in corresponding DSL in pre-raster
// state do not overlap.
for (const auto &pre_raster_binding : pre_raster_dsl->GetBindings()) {
if ((pre_raster_binding.stageFlags & FragmentShaderState::ValidShaderStages()) == 0) {
continue;
}
const auto pre_raster_layout_handle_str = FormatHandle(pre_raster_info.layout->Handle());
const auto fs_layout_handle_str = FormatHandle(frag_shader_info.layout->Handle());
const char *vuid = nullptr;
std::ostringstream msg;
if (frag_shader_info.init == GPLInitType::gpl_flags) {
vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06756";
msg << "represents a library containing fragment shader state, and descriptor set layout (from "
"layout "
<< fs_layout_handle_str << ") at pSetLayouts[" << i << "] is null. "
<< "However, a library with pre-raster state is specified in "
"VkPipelineLibraryCreateInfoKHR::pLibraries with non-null descriptor set layout at the "
"same pSetLayouts index ("
<< i << ") from layout " << pre_raster_layout_handle_str << " and bindings ("
<< string_VkShaderStageFlags(pre_raster_binding.stageFlags)
<< ") that overlap with fragment shader state.";
} else if (pre_raster_info.init == GPLInitType::gpl_flags) {
vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06757";
msg << "represents a library containing pre-raster state, and descriptor set layout (from "
"layout "
<< pre_raster_layout_handle_str << ") at pSetLayouts[" << i << "] contains bindings ("
<< string_VkShaderStageFlags(pre_raster_binding.stageFlags)
<< ") that overlap with fragment shader state. "
<< "However, a library with fragment shader state is specified in "
"VkPipelineLibraryCreateInfoKHR::pLibraries with a null descriptor set layout at the "
"same pSetLayouts index ("
<< i << ") from layout " << fs_layout_handle_str << ".";
} else {
vuid = "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06758";
msg << "is linking libraries with pre-raster and fragment shader state. The descriptor set "
"layout at index "
<< i << " in pSetLayouts from " << fs_layout_handle_str << " in the fragment shader state is NULL. "
<< "However, the descriptor set layout at the same index (" << i << ") in "
<< pre_raster_layout_handle_str << " in the pre-raster state is non-null with bindings ("
<< string_VkShaderStageFlags(pre_raster_binding.stageFlags)
<< ") that overlap with fragment shader "
"state.";
}
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc, "%s", msg.str().c_str());
break;
}
} else if (!pre_raster_dsl && !fs_dsl) {
const auto pre_raster_layout_handle_str = FormatHandle(pre_raster_info.layout->Handle());
const auto fs_layout_handle_str = FormatHandle(frag_shader_info.layout->Handle());
const char *vuid = nullptr;
std::ostringstream msg;
if (frag_shader_info.init == GPLInitType::gpl_flags) {
vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06679";
msg << "represents a library containing fragment shader state, and descriptor set layout (from "
"layout "
<< fs_layout_handle_str << ") at pSetLayouts[" << i << "] is NULL. "
<< "However, a library with pre-raster state is specified in "
"VkPipelineLibraryCreateInfoKHR::pLibraries ("
<< pre_raster_layout_handle_str << ") with pSetLayouts[" << i << "] NULL too.";
} else if (pre_raster_info.init == GPLInitType::gpl_flags) {
vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06679";
msg << "represents a library containing pre-raster state, and descriptor set layout (from "
"layout "
<< pre_raster_layout_handle_str << ") at pSetLayouts[" << i << "] is NULL. "
<< "However, a library with fragment shader state is specified in "
"VkPipelineLibraryCreateInfoKHR::pLibraries ("
<< fs_layout_handle_str << ") with pSetLayouts[" << i << "] NULL too.";
} else {
vuid = "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06681";
msg << "is linking libraries with pre-raster and fragment shader state. The descriptor set "
"layout at index "
<< i << " in pSetLayouts from " << fs_layout_handle_str << " in the fragment shader state is NULL. "
<< "However, the descriptor set layout at the same index (" << i << ") in " << pre_raster_layout_handle_str
<< " in the pre-raster state is NULL too.";
}
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc, "%s", msg.str().c_str());
break;
} else if (not_independent_sets) {
// both handles are valid, but without VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT, need to check everything
// is identically defined
if (pre_raster_dsl->GetCreateFlags() != fs_dsl->GetCreateFlags()) {
const char *vuid = only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06613"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06612";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc,
"VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT was not set and the graphics pipeline "
"library have different pipeline layouts, pre-raster layout has "
"pSetLayouts[%" PRIu32
"] flags (%s)"
", fragment shader layout has pSetLayouts[%" PRIu32 "] flags (%s).",
i, string_VkDescriptorSetLayoutCreateFlags(pre_raster_dsl->GetCreateFlags()).c_str(), i,
string_VkDescriptorSetLayoutCreateFlags(fs_dsl->GetCreateFlags()).c_str());
} else if (pre_raster_dsl->GetBindingCount() != fs_dsl->GetBindingCount()) {
const char *vuid = only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06613"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06612";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(vuid, objlist, create_info_loc,
"VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT was not set and the graphics pipeline "
"library have different pipeline layouts, pre-raster layout has "
"pSetLayouts[%" PRIu32 "] bindingCount (%" PRIu32
")"
", fragment shader layout has pSetLayouts[%" PRIu32 "] bindingCount (%" PRIu32 ").",
i, pre_raster_dsl->GetBindingCount(), i, fs_dsl->GetBindingCount());
} else {
const uint32_t binding_count = pre_raster_dsl->GetBindingCount();
const auto &pre_raster_bindings = pre_raster_dsl->GetBindings();
const auto &fs_bindings = fs_dsl->GetBindings();
for (uint32_t binding_index = 0; binding_index < binding_count; binding_index++) {
const auto &pre_raster_binding = pre_raster_bindings[binding_index];
const auto &fs_binding = fs_bindings[binding_index];
if (!CompareDescriptorSetLayoutBinding(*pre_raster_binding.ptr(), *fs_binding.ptr())) {
const char *vuid = only_libs ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06613"
: "VUID-VkGraphicsPipelineCreateInfo-flags-06612";
LogObjectList objlist(pre_raster_info.layout->Handle(), frag_shader_info.layout->Handle());
skip |= LogError(
vuid, objlist, create_info_loc,
"VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT was not set and the graphics pipeline "
"library have different pipeline layouts, pre-raster layout has "
"pSetLayouts[%" PRIu32 "] pBindings[%" PRIu32
"] of:\n"
"\tbinding: %" PRIu32
"\n"
"\tdescriptorType: %s\n"
"\tdescriptorCount: %" PRIu32
"\n"
"\tstageFlags: %s\n"
"\tpImmutableSamplers: 0x%p\n"
"fragment shader layout has pSetLayouts[%" PRIu32 "] pBindings[%" PRIu32
"] of:\n"
"\tbinding: %" PRIu32
"\n"
"\tdescriptorType: %s\n"
"\tdescriptorCount: %" PRIu32
"\n"
"\tstageFlags: %s\n"
"\tpImmutableSamplers: 0x%p\n",
i, binding_index, pre_raster_binding.binding,
string_VkDescriptorType(pre_raster_binding.descriptorType), pre_raster_binding.descriptorCount,
string_VkShaderStageFlags(pre_raster_binding.stageFlags).c_str(),
pre_raster_binding.pImmutableSamplers, i, binding_index, fs_binding.binding,
string_VkDescriptorType(fs_binding.descriptorType), fs_binding.descriptorCount,
string_VkShaderStageFlags(fs_binding.stageFlags).c_str(), fs_binding.pImmutableSamplers);
}
}
}
}
}
}
if (frag_shader_info.init != GPLInitType::uninitialized && frag_output_info.init != GPLInitType::uninitialized) {
if (!frag_shader_info.ms_state && frag_output_info.ms_state) {
// if Fragment Output has sampleShadingEnable == false, Fragement Shader may be null
if (frag_output_info.ms_state->sampleShadingEnable) {
const char *vuid =
(frag_shader_info.init == GPLInitType::gpl_flags) ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-09567"
: (frag_output_info.init == GPLInitType::gpl_flags) ? "VUID-VkGraphicsPipelineCreateInfo-flags-06637"
: "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06636";
skip |= LogError(
vuid, device, create_info_loc,
"Fragment Output Interface were created with VkPipelineMultisampleStateCreateInfo::sampleShadingEnable to "
"VK_TRUE, but Fragment Shader has a pMultisampleState of NULL.");
}
} else if (frag_shader_info.ms_state && !frag_output_info.ms_state) {
const char *vuid = (frag_shader_info.init == GPLInitType::gpl_flags) ? "VUID-VkGraphicsPipelineCreateInfo-flags-06633"
: (frag_output_info.init == GPLInitType::gpl_flags)
? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06634"
: "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06635";
skip |= LogError(vuid, device, create_info_loc,
"Fragment Shader has a valid VkPipelineMultisampleStateCreateInfo, but Fragment Output Interface has "
"a pMultisampleState of NULL.");
} else if (frag_shader_info.ms_state && frag_output_info.ms_state) {
if (!ComparePipelineMultisampleStateCreateInfo(*frag_shader_info.ms_state, *frag_output_info.ms_state)) {
const char *vuid =
(frag_shader_info.init == GPLInitType::gpl_flags) ? "VUID-VkGraphicsPipelineCreateInfo-flags-06633"
: (frag_output_info.init == GPLInitType::gpl_flags) ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06634"
: "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06635";
skip |= LogError(vuid, device, create_info_loc,
"Fragment Shader and Fragment Output Interface were created with different "
"VkPipelineMultisampleStateCreateInfo."
"Fragment Shader pMultisampleState:\n"
"\tpNext: %p\n"
"\trasterizationSamples: %s\n"
"\tsampleShadingEnable: %d\n"
"\tminSampleShading: %f\n"
"\tpSampleMask: %p\n"
"\talphaToCoverageEnable: %d\n"
"\talphaToOneEnable: %d\n"
"Fragment Output Interface pMultisampleState:\n"
"\tpNext: %p\n"
"\trasterizationSamples: %s\n"
"\tsampleShadingEnable: %d\n"
"\tminSampleShading: %f\n"
"\tpSampleMask: %p\n"
"\talphaToCoverageEnable: %d\n"
"\talphaToOneEnable: %d\n",
frag_shader_info.ms_state->pNext,
string_VkSampleCountFlagBits(frag_shader_info.ms_state->rasterizationSamples),
frag_shader_info.ms_state->sampleShadingEnable, frag_shader_info.ms_state->minSampleShading,
frag_shader_info.ms_state->pSampleMask, frag_shader_info.ms_state->alphaToCoverageEnable,
frag_shader_info.ms_state->alphaToOneEnable, frag_output_info.ms_state->pNext,
string_VkSampleCountFlagBits(frag_output_info.ms_state->rasterizationSamples),
frag_output_info.ms_state->sampleShadingEnable, frag_output_info.ms_state->minSampleShading,
frag_output_info.ms_state->pSampleMask, frag_output_info.ms_state->alphaToCoverageEnable,
frag_output_info.ms_state->alphaToOneEnable);
}
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineBlendEnable(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const auto rp_state = pipeline.RenderPassState();
if (!rp_state || rp_state->UsesDynamicRendering()) {
return skip;
}
const auto subpass = pipeline.Subpass();
const auto *subpass_desc = &rp_state->create_info.pSubpasses[subpass];
if (!subpass_desc) {
return skip;
}
for (uint32_t i = 0; i < pipeline.AttachmentStates().size() && i < subpass_desc->colorAttachmentCount; ++i) {
const auto attachment = subpass_desc->pColorAttachments[i].attachment;
if (attachment == VK_ATTACHMENT_UNUSED) {
continue;
}
const auto attachment_desc = rp_state->create_info.pAttachments[attachment];
if (!pipeline.RasterizationDisabled() && pipeline.AttachmentStates()[i].blendEnable) {
const VkFormatFeatureFlags2 format_features = GetPotentialFormatFeatures(attachment_desc.format);
if (!(format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06041", device,
create_info_loc.dot(Field::pColorBlendState).dot(Field::pAttachments, i).dot(Field::blendEnable),
"is VK_TRUE but format %s of the corresponding attachment description (subpass %" PRIu32
", attachment %" PRIu32 ") doesn't support VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT.\n%s",
string_VkFormat(attachment_desc.format), subpass, attachment,
string_VkFormatFeatureFlags2(format_features).c_str());
}
}
if (attachment_desc.format == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT)) {
const VkColorComponentFlags &color_write_mask = pipeline.AttachmentStates()[i].colorWriteMask;
VkColorComponentFlags rgb = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;
if ((color_write_mask & rgb) != rgb && (color_write_mask & rgb) != 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-None-09043", device, create_info_loc.dot(Field::renderPass),
"was created with pAttachments[%" PRIu32
"].format of VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, but pColorBlendState->pAttachments[%" PRIu32
"].colorWriteMask is %s.",
attachment, i, string_VkColorComponentFlags(color_write_mask).c_str());
}
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineMeshTask(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const ShaderStageState *task_state = nullptr;
const ShaderStageState *mesh_state = nullptr;
for (const auto &stage_state : pipeline.stage_states) {
const VkShaderStageFlagBits stage = stage_state.GetStage();
if (stage == VK_SHADER_STAGE_MESH_BIT_EXT) {
mesh_state = &stage_state;
} else if (stage == VK_SHADER_STAGE_TASK_BIT_EXT) {
task_state = &stage_state;
}
}
if (!mesh_state || !task_state) {
return skip; // checks require optional task shader
}
if (mesh_state->spirv_state && mesh_state->spirv_state->static_data_.has_builtin_draw_index) {
// There is a dedicated equivalent for shader object
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-09631", device, create_info_loc,
"The pipeline is being created with a Task and Mesh shader bound, but the Mesh Shader "
"uses DrawIndex (gl_DrawID) which will be an undefined value when reading.");
}
if (task_state->spirv_state && mesh_state->entrypoint) {
skip |= ValidateTaskPayload(*task_state->spirv_state, *mesh_state->entrypoint, create_info_loc);
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineExternalFormatResolve(const vvl::Pipeline &pipeline, const vvl::RenderPass &rp_state,
const vku::safe_VkSubpassDescription2 &subpass_desc,
const Location &create_info_loc) const {
bool skip = false;
if (!enabled_features.externalFormatResolve) return skip;
if (subpass_desc.colorAttachmentCount == 0 || !subpass_desc.pResolveAttachments) {
return skip;
}
// can only have 1 color attachment
const uint32_t attachment = subpass_desc.pResolveAttachments[0].attachment;
if (attachment == VK_ATTACHMENT_UNUSED) {
return skip;
}
const uint64_t external_format = GetExternalFormat(rp_state.create_info.pAttachments[attachment].pNext);
if (external_format == 0) {
return skip;
}
const auto *multisample_state = pipeline.MultisampleState();
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) && multisample_state) {
if (multisample_state->rasterizationSamples != VK_SAMPLE_COUNT_1_BIT) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09313", device,
create_info_loc.dot(Field::pMultisampleState).dot(Field::rasterizationSamples),
"is %" PRIu32 ", but externalFormat is %" PRIu64 " for subpass %" PRIu32 ".",
multisample_state->rasterizationSamples, external_format, pipeline.Subpass());
}
}
const auto *color_blend_state = pipeline.ColorBlendState();
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT) && color_blend_state) {
for (uint32_t i = 0; i < color_blend_state->attachmentCount; i++) {
if (color_blend_state->pAttachments[i].blendEnable) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09314", device,
create_info_loc.dot(Field::pColorBlendState).dot(Field::pAttachments, i).dot(Field::blendEnable),
"is VK_TRUE, but externalFormat is %" PRIu64 " for subpass %" PRIu32 ".", external_format,
pipeline.Subpass());
}
}
}
const auto fragment_shading_rate =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pipeline.GetCreateInfoPNext());
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR) && fragment_shading_rate) {
if (fragment_shading_rate->fragmentSize.width != 1) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09315", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::fragmentSize)
.dot(Field::width),
"is %" PRIu32 ", but externalFormat is %" PRIu64 " for subpass %" PRIu32 ".",
fragment_shading_rate->fragmentSize.width, external_format, pipeline.Subpass());
}
if (fragment_shading_rate->fragmentSize.height != 1) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09316", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::fragmentSize)
.dot(Field::height),
"is %" PRIu32 ", but externalFormat is %" PRIu64 " for subpass %" PRIu32 ".",
fragment_shading_rate->fragmentSize.height, external_format, pipeline.Subpass());
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineExternalFormatResolveDynamicRendering(const vvl::Pipeline &pipeline,
const Location &create_info_loc) const {
bool skip = false;
if (!enabled_features.externalFormatResolve) return skip;
const uint64_t external_format = GetExternalFormat(pipeline.GetCreateInfoPNext());
const auto *rendering_struct = pipeline.rendering_create_info;
if (external_format == 0 || !rendering_struct) {
return skip;
}
if (rendering_struct->viewMask != 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09301", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask),
"is 0x%" PRIx32 ", but externalFormat is %" PRIu64 ".", rendering_struct->viewMask, external_format);
}
if (rendering_struct->colorAttachmentCount != 1) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09309", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::colorAttachmentCount),
"is %" PRIu32 ", but externalFormat is %" PRIu64 ".", rendering_struct->colorAttachmentCount, external_format);
}
if (pipeline.OwnsLibState(pipeline.fragment_shader_state) && pipeline.fragment_shader_state->fragment_entry_point) {
auto entrypoint = pipeline.fragment_shader_state->fragment_entry_point;
if (entrypoint->execution_mode.Has(spirv::ExecutionModeSet::depth_replacing_bit)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09310", device,
create_info_loc.pNext(Struct::VkExternalFormatANDROID, Field::externalFormat),
"is %" PRIu64 " but the fragment shader declares DepthReplacing.", external_format);
} else if (entrypoint->execution_mode.Has(spirv::ExecutionModeSet::stencil_ref_replacing_bit)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09310", device,
create_info_loc.pNext(Struct::VkExternalFormatANDROID, Field::externalFormat),
"is %" PRIu64 " but the fragment shader declares StencilRefReplacingEXT.", external_format);
}
}
const auto *multisample_state = pipeline.MultisampleState();
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) && multisample_state) {
if (multisample_state->rasterizationSamples != VK_SAMPLE_COUNT_1_BIT) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09304", device,
create_info_loc.dot(Field::pMultisampleState).dot(Field::rasterizationSamples),
"is %s, but externalFormat is %" PRIu64 ".",
string_VkSampleCountFlagBits(multisample_state->rasterizationSamples), external_format);
}
}
const auto *color_blend_state = pipeline.ColorBlendState();
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT) && color_blend_state) {
for (uint32_t i = 0; i < color_blend_state->attachmentCount; i++) {
if (color_blend_state->pAttachments[i].blendEnable) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09305", device,
create_info_loc.dot(Field::pColorBlendState).dot(Field::pAttachments, i).dot(Field::blendEnable),
"is VK_TRUE, but externalFormat is %" PRIu64 ".", external_format);
}
}
}
const auto fragment_shading_rate =
vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pipeline.GetCreateInfoPNext());
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR) && fragment_shading_rate) {
if (fragment_shading_rate->fragmentSize.width != 1) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09306", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::fragmentSize)
.dot(Field::width),
"is %" PRIu32 ", but externalFormat is %" PRIu64 ".", fragment_shading_rate->fragmentSize.width,
external_format);
}
if (fragment_shading_rate->fragmentSize.height != 1) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-externalFormatResolve-09307", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::fragmentSize)
.dot(Field::height),
"is %" PRIu32 ", but externalFormat is %" PRIu64 ".", fragment_shading_rate->fragmentSize.height,
external_format);
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineInputAssemblyState(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const Location ia_loc = create_info_loc.dot(Field::pInputAssemblyState);
// if vertex_input_state is not set, will be null
const auto *ia_state = pipeline.InputAssemblyState();
if (ia_state) {
const VkPrimitiveTopology topology = ia_state->topology;
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE) && ia_state->primitiveRestartEnable) {
if (!enabled_features.primitiveTopologyPatchListRestart && topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
skip |= LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06253", device, ia_loc.dot(Field::topology),
"is VK_PRIMITIVE_TOPOLOGY_PATCH_LIST and primitiveRestartEnable is VK_TRUE, but the "
"primitiveTopologyPatchListRestart feature was not enabled.");
} else if (!enabled_features.primitiveTopologyListRestart &&
IsValueIn(topology, {VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY})) {
skip |= LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06252", device, ia_loc.dot(Field::topology),
"is %s and primitiveRestartEnable is VK_TRUE, but the primitiveTopologyListRestart feature "
"was not enabled.",
string_VkPrimitiveTopology(topology));
}
}
if (!enabled_features.geometryShader &&
IsValueIn(topology,
{VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY})) {
skip |= LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429", device, ia_loc.dot(Field::topology),
"is %s but the geometryShader feature was not enabled.", string_VkPrimitiveTopology(topology));
}
if (!enabled_features.tessellationShader && topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
skip |= LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00430", device, ia_loc.dot(Field::topology),
"is VK_PRIMITIVE_TOPOLOGY_PATCH_LIST but the tessellationShader feature was not enabled.");
}
}
const bool ignore_topology = pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) &&
phys_dev_ext_props.extended_dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted;
// pre_raster has tessellation stage, vertex input has topology
// Both are needed for these checks
if (!ignore_topology && pipeline.pre_raster_state && pipeline.vertex_input_state) {
// Either both or neither TC/TE shaders should be defined
const bool has_control = (pipeline.active_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0;
const bool has_eval = (pipeline.active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0;
const bool has_tessellation = has_control && has_eval; // need both
// VK_PRIMITIVE_TOPOLOGY_PATCH_LIST primitive topology is only valid for tessellation pipelines.
// Mismatching primitive topology and tessellation fails graphics pipeline creation.
if (has_tessellation && (!ia_state || ia_state->topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-08888", device, ia_loc.dot(Field::topology),
"is %s for tessellation shaders in pipeline (needs to be VK_PRIMITIVE_TOPOLOGY_PATCH_LIST).",
ia_state ? string_VkPrimitiveTopology(ia_state->topology) : "null");
}
if (!has_tessellation && (ia_state && ia_state->topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-topology-08889", device, ia_loc.dot(Field::topology),
"is VK_PRIMITIVE_TOPOLOGY_PATCH_LIST but no tessellation shaders.");
}
};
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineTessellationState(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
if (pipeline.OwnsLibState(pipeline.pre_raster_state) &&
(pipeline.create_info_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)) {
if (!pipeline.TessellationState() && (!pipeline.IsDynamic(CB_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT) ||
!IsExtEnabled(extensions.vk_ext_extended_dynamic_state3))) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-09022", device, create_info_loc.dot(Field::pStages),
"includes a tessellation control shader stage, but pTessellationState is NULL."
"\nIf the following are all set, it can be NULL"
"\n Enable VK_EXT_extended_dynamic_state3 (%senabled)"
"\n Use VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT (%s)\n",
IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ? "" : "not ",
pipeline.IsDynamic(CB_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT) ? "set" : "not set");
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelinePreRasterizationState(const vvl::Pipeline &pipeline,
const Location &create_info_loc) const {
bool skip = false;
// Only validate once during creation
if (!pipeline.OwnsLibState(pipeline.pre_raster_state)) {
return skip;
}
const VkShaderStageFlags stages = pipeline.create_info_shaders;
if ((stages & PreRasterState::ValidShaderStages()) == 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-06896", device, create_info_loc,
"contains pre-raster state, but stages (%s) does not contain any pre-raster shaders.",
string_VkShaderStageFlags(stages).c_str());
}
if (!enabled_features.geometryShader && (stages & VK_SHADER_STAGE_GEOMETRY_BIT)) {
skip |= LogError("VUID-VkPipelineShaderStageCreateInfo-stage-00704", device, create_info_loc,
"pStages include Geometry Shader but geometryShader feature was not enabled.");
}
if (!enabled_features.tessellationShader &&
(stages & (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
skip |= LogError("VUID-VkPipelineShaderStageCreateInfo-stage-00705", device, create_info_loc,
"pStages include Tessellation Shader but tessellationShader feature was not enabled.");
}
// VS or mesh is required
if (!(stages & (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_MESH_BIT_EXT))) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-stage-02096", device, create_info_loc,
"no stage in pStages contains a Vertex Shader or Mesh Shader.");
}
// Can't mix mesh and VTG
if ((stages & (VK_SHADER_STAGE_MESH_BIT_EXT | VK_SHADER_STAGE_TASK_BIT_EXT)) &&
(stages & (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT |
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-02095", device, create_info_loc,
"in pStages, Geometric shader stages must either be all mesh (mesh | task) "
"or all VTG (vertex, tess control, tess eval, geom).");
}
// VK_SHADER_STAGE_MESH_BIT_EXT and VK_SHADER_STAGE_MESH_BIT_NV are equivalent
if (!(enabled_features.meshShader) && (stages & VK_SHADER_STAGE_MESH_BIT_EXT)) {
skip |= LogError("VUID-VkPipelineShaderStageCreateInfo-stage-02091", device, create_info_loc,
"pStages include Mesh Shader but meshShader feature was not enabled.");
}
// VK_SHADER_STAGE_TASK_BIT_EXT and VK_SHADER_STAGE_TASK_BIT_NV are equivalent
if (!(enabled_features.taskShader) && (stages & VK_SHADER_STAGE_TASK_BIT_EXT)) {
skip |= LogError("VUID-VkPipelineShaderStageCreateInfo-stage-02092", device, create_info_loc,
"pStages include Task Shader but taskShader feature was not enabled.");
}
// Either both or neither TC/TE shaders should be defined
const bool has_control = (stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0;
const bool has_eval = (stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0;
if (has_control && !has_eval) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-00729", device, create_info_loc,
"pStages include a VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT but no "
"VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT stage.");
}
if (!has_control && has_eval) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-00730", device, create_info_loc,
"pStages include a VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT but no "
"VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT stage.");
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineColorBlendAttachmentState(const vvl::Pipeline &pipeline,
const vku::safe_VkSubpassDescription2 *subpass_desc,
const Location &color_loc) const {
bool skip = false;
const auto &attachment_states = pipeline.AttachmentStates();
if (attachment_states.empty()) return skip;
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT)) return skip;
if (!enabled_features.independentBlend && attachment_states.size() > 1) {
for (size_t i = 1; i < attachment_states.size(); i++) {
if (!ComparePipelineColorBlendAttachmentState(attachment_states[0], attachment_states[i])) {
skip |= LogError("VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605", device,
color_loc.dot(Field::pAttachments, (uint32_t)i),
"is different than pAttachments[0] and independentBlend feature was not enabled.");
break;
}
}
}
const VkBlendOp first_color_blend_op = attachment_states[0].colorBlendOp;
const VkBlendOp first_alpha_blend_op = attachment_states[0].alphaBlendOp;
for (size_t i = 0; i < attachment_states.size(); i++) {
const VkPipelineColorBlendAttachmentState &attachment_state = attachment_states[i];
const Location &attachment_loc = color_loc.dot(Field::pAttachments, (uint32_t)i);
if (!enabled_features.dualSrcBlend) {
if (IsSecondaryColorInputBlendFactor(attachment_state.srcColorBlendFactor)) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-00608", device,
attachment_loc.dot(Field::srcColorBlendFactor),
"(%s) is a dual-source blend factor, but dualSrcBlend feature was not enabled.",
string_VkBlendFactor(attachment_state.srcColorBlendFactor));
}
if (IsSecondaryColorInputBlendFactor(attachment_state.dstColorBlendFactor)) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-dstColorBlendFactor-00609", device,
attachment_loc.dot(Field::dstColorBlendFactor),
"(%s) is a dual-source blend factor, but dualSrcBlend feature was not enabled.",
string_VkBlendFactor(attachment_state.dstColorBlendFactor));
}
if (IsSecondaryColorInputBlendFactor(attachment_state.srcAlphaBlendFactor)) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-srcAlphaBlendFactor-00610", device,
attachment_loc.dot(Field::srcAlphaBlendFactor),
"(%s) is a dual-source blend factor, but dualSrcBlend feature was not enabled.",
string_VkBlendFactor(attachment_state.srcAlphaBlendFactor));
}
if (IsSecondaryColorInputBlendFactor(attachment_state.dstAlphaBlendFactor)) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-dstAlphaBlendFactor-00611", device,
attachment_loc.dot(Field::dstAlphaBlendFactor),
"(%s) is a dual-source blend factor, but dualSrcBlend feature was not enabled.",
string_VkBlendFactor(attachment_state.dstAlphaBlendFactor));
}
}
// if blendEnabled is false, these values are ignored
if (attachment_state.blendEnable) {
bool advance_blend = false;
if (IsAdvanceBlendOperation(attachment_state.colorBlendOp)) {
advance_blend = true;
if (phys_dev_ext_props.blend_operation_advanced_props.advancedBlendAllOperations == VK_FALSE) {
// This VUID checks if a subset of advance blend ops are allowed
switch (attachment_state.colorBlendOp) {
case VK_BLEND_OP_ZERO_EXT:
case VK_BLEND_OP_SRC_EXT:
case VK_BLEND_OP_DST_EXT:
case VK_BLEND_OP_SRC_OVER_EXT:
case VK_BLEND_OP_DST_OVER_EXT:
case VK_BLEND_OP_SRC_IN_EXT:
case VK_BLEND_OP_DST_IN_EXT:
case VK_BLEND_OP_SRC_OUT_EXT:
case VK_BLEND_OP_DST_OUT_EXT:
case VK_BLEND_OP_SRC_ATOP_EXT:
case VK_BLEND_OP_DST_ATOP_EXT:
case VK_BLEND_OP_XOR_EXT:
case VK_BLEND_OP_INVERT_EXT:
case VK_BLEND_OP_INVERT_RGB_EXT:
case VK_BLEND_OP_LINEARDODGE_EXT:
case VK_BLEND_OP_LINEARBURN_EXT:
case VK_BLEND_OP_VIVIDLIGHT_EXT:
case VK_BLEND_OP_LINEARLIGHT_EXT:
case VK_BLEND_OP_PINLIGHT_EXT:
case VK_BLEND_OP_HARDMIX_EXT:
case VK_BLEND_OP_PLUS_EXT:
case VK_BLEND_OP_PLUS_CLAMPED_EXT:
case VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT:
case VK_BLEND_OP_PLUS_DARKER_EXT:
case VK_BLEND_OP_MINUS_EXT:
case VK_BLEND_OP_MINUS_CLAMPED_EXT:
case VK_BLEND_OP_CONTRAST_EXT:
case VK_BLEND_OP_INVERT_OVG_EXT:
case VK_BLEND_OP_RED_EXT:
case VK_BLEND_OP_GREEN_EXT:
case VK_BLEND_OP_BLUE_EXT: {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-advancedBlendAllOperations-01409", device,
attachment_loc.dot(Field::colorBlendOp),
"(%s) but "
"VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT::"
"advancedBlendAllOperations is "
"VK_FALSE",
string_VkBlendOp(attachment_state.colorBlendOp));
break;
}
default:
break;
}
}
if (phys_dev_ext_props.blend_operation_advanced_props.advancedBlendIndependentBlend == VK_FALSE &&
attachment_state.colorBlendOp != first_color_blend_op) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-advancedBlendIndependentBlend-01407", device,
attachment_loc.dot(Field::colorBlendOp), "(%s) is not same the other attachments (%s).",
string_VkBlendOp(attachment_state.colorBlendOp), string_VkBlendOp(first_color_blend_op));
}
}
if (IsAdvanceBlendOperation(attachment_state.alphaBlendOp)) {
advance_blend = true;
if (phys_dev_ext_props.blend_operation_advanced_props.advancedBlendIndependentBlend == VK_FALSE &&
attachment_state.alphaBlendOp != first_alpha_blend_op) {
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-advancedBlendIndependentBlend-01408", device,
attachment_loc.dot(Field::alphaBlendOp), "(%s) is not same the other attachments (%s).",
string_VkBlendOp(attachment_state.alphaBlendOp), string_VkBlendOp(first_alpha_blend_op));
}
}
if (advance_blend) {
if (attachment_state.colorBlendOp != attachment_state.alphaBlendOp) {
skip |=
LogError("VUID-VkPipelineColorBlendAttachmentState-colorBlendOp-01406", device, attachment_loc,
"has different colorBlendOp (%s) and alphaBlendOp (%s) but one of "
"them is an advance blend operation.",
string_VkBlendOp(attachment_state.colorBlendOp), string_VkBlendOp(attachment_state.alphaBlendOp));
} else {
const uint32_t color_attachment_count = pipeline.rendering_create_info
? pipeline.rendering_create_info->colorAttachmentCount
: subpass_desc ? subpass_desc->colorAttachmentCount
: 0;
if (color_attachment_count != 0 &&
color_attachment_count >
phys_dev_ext_props.blend_operation_advanced_props.advancedBlendMaxColorAttachments) {
// color_attachment_count is found one of multiple spots above
//
// error can guarantee it is the same VkBlendOp
skip |= LogError("VUID-VkPipelineColorBlendAttachmentState-colorBlendOp-01410", device, attachment_loc,
"has an advance blend operation (%s) but the colorAttachmentCount (%" PRIu32
") is larger than advancedBlendMaxColorAttachments (%" PRIu32 ").",
string_VkBlendOp(attachment_state.colorBlendOp), color_attachment_count,
phys_dev_ext_props.blend_operation_advanced_props.advancedBlendMaxColorAttachments);
break; // if this fails once, will fail every iteration
}
}
}
}
}
return skip;
}
bool CoreChecks::IsColorBlendStateAttachmentCountIgnore(const vvl::Pipeline &pipeline) const {
return pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT) &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT) &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT) &&
(pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT) || !enabled_features.advancedBlendCoherentOperations);
}
bool CoreChecks::ValidatePipelineColorBlendAdvancedStateCreateInfo(
const vvl::Pipeline &pipeline, const VkPipelineColorBlendAdvancedStateCreateInfoEXT &color_blend_advanced,
const Location &color_loc) const {
bool skip = false;
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT)) return skip;
const auto &prop = phys_dev_ext_props.blend_operation_advanced_props;
if (!prop.advancedBlendCorrelatedOverlap && color_blend_advanced.blendOverlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT) {
skip |= LogError("VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-blendOverlap-01426", device,
color_loc.pNext(Struct::VkPipelineColorBlendAdvancedStateCreateInfoEXT, Field::blendOverlap),
"is %s, but advancedBlendCorrelatedOverlap was not enabled.",
string_VkBlendOverlapEXT(color_blend_advanced.blendOverlap));
}
if (!prop.advancedBlendNonPremultipliedDstColor && color_blend_advanced.dstPremultiplied != VK_TRUE) {
skip |= LogError("VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-dstPremultiplied-01425", device,
color_loc.pNext(Struct::VkPipelineColorBlendAdvancedStateCreateInfoEXT, Field::dstPremultiplied),
"is VK_FALSE, but advancedBlendNonPremultipliedDstColor was not enabled.");
}
if (!prop.advancedBlendNonPremultipliedSrcColor && color_blend_advanced.srcPremultiplied != VK_TRUE) {
skip |= LogError("VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-srcPremultiplied-01424", device,
color_loc.pNext(Struct::VkPipelineColorBlendAdvancedStateCreateInfoEXT, Field::srcPremultiplied),
"is VK_FALSE, but advancedBlendNonPremultipliedSrcColor was not enabled.");
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineColorBlendState(const vvl::Pipeline &pipeline,
const vku::safe_VkSubpassDescription2 *subpass_desc,
const Location &create_info_loc) const {
bool skip = false;
const Location color_loc = create_info_loc.dot(Field::pColorBlendState);
const auto color_blend_state = pipeline.ColorBlendState();
if (!color_blend_state) {
return skip;
}
const auto rp_state = pipeline.RenderPassState();
const bool null_rp = pipeline.IsRenderPassNull();
if (!null_rp && !rp_state) return skip; // invalid render pass
// VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV
const auto *attachment_sample_count_info =
vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(pipeline.GetCreateInfoPNext());
const auto *rendering_struct = pipeline.rendering_create_info;
if (null_rp && rendering_struct && attachment_sample_count_info &&
(attachment_sample_count_info->colorAttachmentCount != rendering_struct->colorAttachmentCount)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06063", device,
create_info_loc.pNext(Struct::VkAttachmentSampleCountInfoAMD, Field::attachmentCount),
"(%" PRIu32 ") is different then %s (%" PRIu32 ").", attachment_sample_count_info->colorAttachmentCount,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::colorAttachmentCount).Fields().c_str(),
rendering_struct->colorAttachmentCount);
}
if (!null_rp && subpass_desc && color_blend_state->attachmentCount != subpass_desc->colorAttachmentCount) {
if (!IsColorBlendStateAttachmentCountIgnore(pipeline)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-07609", device, color_loc.dot(Field::attachmentCount),
"(%" PRIu32 ") is different than %s pSubpasses[%" PRIu32 "].colorAttachmentCount (%" PRIu32 ").",
color_blend_state->attachmentCount, FormatHandle(rp_state->Handle()).c_str(), pipeline.Subpass(),
subpass_desc->colorAttachmentCount);
}
}
skip |= ValidateGraphicsPipelineColorBlendAttachmentState(pipeline, subpass_desc, color_loc);
if (!enabled_features.logicOp && (color_blend_state->logicOpEnable == VK_TRUE) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT)) {
skip |= LogError("VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00606", device,
color_loc.dot(Field::logicOpEnable), "is VK_TRUE, but the logicOp feature was not enabled.");
}
if (auto color_write = vku::FindStructInPNextChain<VkPipelineColorWriteCreateInfoEXT>(color_blend_state->pNext)) {
if (color_write->attachmentCount > phys_dev_props.limits.maxColorAttachments) {
skip |= LogError("VUID-VkPipelineColorWriteCreateInfoEXT-attachmentCount-06655", device,
color_loc.pNext(Struct::VkPipelineColorWriteCreateInfoEXT, Field::attachmentCount),
"(%" PRIu32 ") is larger than the maxColorAttachments limit (%" PRIu32 ").",
color_write->attachmentCount, phys_dev_props.limits.maxColorAttachments);
}
if (!enabled_features.colorWriteEnable && !pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT)) {
for (uint32_t i = 0; i < color_write->attachmentCount; ++i) {
if (color_write->pColorWriteEnables[i] != VK_TRUE) {
skip |= LogError("VUID-VkPipelineColorWriteCreateInfoEXT-pAttachments-04801", device,
color_loc.pNext(Struct::VkPipelineColorWriteCreateInfoEXT, Field::pColorWriteEnables, i),
"is VK_FALSE, but colorWriteEnable feature was not enabled.");
}
}
}
}
if (const auto *color_blend_advanced =
vku::FindStructInPNextChain<VkPipelineColorBlendAdvancedStateCreateInfoEXT>(color_blend_state->pNext)) {
skip |= ValidatePipelineColorBlendAdvancedStateCreateInfo(pipeline, *color_blend_advanced, color_loc);
}
return skip;
}
bool CoreChecks::ValidatePipelineRasterizationStateStreamCreateInfo(
const vvl::Pipeline &pipeline, const VkPipelineRasterizationStateStreamCreateInfoEXT &rasterization_state_stream_ci,
const Location &raster_loc) const {
bool skip = false;
if (!enabled_features.geometryStreams) {
skip |= LogError("VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-geometryStreams-02324", device,
raster_loc.dot(Field::pNext),
"chain includes VkPipelineRasterizationStateStreamCreateInfoEXT, but "
"geometryStreams feature was not enabled.");
} else if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_STREAM_EXT)) {
const auto &props = phys_dev_ext_props.transform_feedback_props;
if (props.transformFeedbackRasterizationStreamSelect == VK_FALSE &&
rasterization_state_stream_ci.rasterizationStream != 0) {
skip |= LogError("VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-rasterizationStream-02326", device,
raster_loc.pNext(Struct::VkPipelineRasterizationStateStreamCreateInfoEXT, Field::rasterizationStream),
"is (%" PRIu32 ") but transformFeedbackRasterizationStreamSelect is VK_FALSE.",
rasterization_state_stream_ci.rasterizationStream);
} else if (rasterization_state_stream_ci.rasterizationStream >= props.maxTransformFeedbackStreams) {
skip |= LogError("VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-rasterizationStream-02325", device,
raster_loc.pNext(Struct::VkPipelineRasterizationStateStreamCreateInfoEXT, Field::rasterizationStream),
"(%" PRIu32 ") is not less than maxTransformFeedbackStreams (%" PRIu32 ").",
rasterization_state_stream_ci.rasterizationStream, props.maxTransformFeedbackStreams);
}
}
return skip;
}
bool CoreChecks::ValidatePipelineRasterizationConservativeStateCreateInfo(
const vvl::Pipeline &pipeline, const VkPipelineRasterizationConservativeStateCreateInfoEXT &rasterization_conservative_state_ci,
const Location &raster_loc) const {
bool skip = false;
if (rasterization_conservative_state_ci.extraPrimitiveOverestimationSize < 0.0f ||
rasterization_conservative_state_ci.extraPrimitiveOverestimationSize >
phys_dev_ext_props.conservative_rasterization_props.maxExtraPrimitiveOverestimationSize) {
skip |=
LogError("VUID-VkPipelineRasterizationConservativeStateCreateInfoEXT-extraPrimitiveOverestimationSize-01769", device,
raster_loc.pNext(Struct::VkPipelineRasterizationConservativeStateCreateInfoEXT,
Field::extraPrimitiveOverestimationSize),
"is (%f), which is not between 0.0 and "
"maxExtraPrimitiveOverestimationSize (%f).",
rasterization_conservative_state_ci.extraPrimitiveOverestimationSize,
phys_dev_ext_props.conservative_rasterization_props.maxExtraPrimitiveOverestimationSize);
}
if (!phys_dev_ext_props.conservative_rasterization_props.conservativePointAndLineRasterization &&
rasterization_conservative_state_ci.conservativeRasterizationMode != VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT) {
if (IsLineTopology(pipeline.topology_at_rasterizer) || IsPointTopology(pipeline.topology_at_rasterizer)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-conservativePointAndLineRasterization-08892", device,
raster_loc.pNext(Struct::VkPipelineRasterizationConservativeStateCreateInfoEXT,
Field::conservativeRasterizationMode),
"is %s but the Rasterization Input Topology is %s.",
string_VkConservativeRasterizationModeEXT(rasterization_conservative_state_ci.conservativeRasterizationMode),
string_VkPrimitiveTopology(pipeline.topology_at_rasterizer));
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineRasterizationState(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const auto raster_state = pipeline.RasterizationState();
if (!raster_state) return skip;
const Location raster_loc = create_info_loc.dot(Field::pRasterizationState);
if ((raster_state->depthClampEnable == VK_TRUE) && !enabled_features.depthClamp &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT)) {
skip |= LogError("VUID-VkPipelineRasterizationStateCreateInfo-depthClampEnable-00782", device,
raster_loc.dot(Field::depthClampEnable), "is VK_TRUE, but the depthClamp feature was not enabled.");
}
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BIAS) && (raster_state->depthBiasClamp != 0.0) &&
!enabled_features.depthBiasClamp) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00754", device, raster_loc.dot(Field::depthBiasClamp),
"is %f, but the depthBiasClamp feature was not enabled", raster_state->depthBiasClamp);
}
// If rasterization is enabled...
if (raster_state->rasterizerDiscardEnable == VK_FALSE) {
// pMultisampleState can be null for graphics library
const bool has_ms_state =
!pipeline.IsGraphicsLibrary() ||
((pipeline.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 = pipeline.MultisampleState();
if (ms_state && (ms_state->alphaToOneEnable == VK_TRUE) && !enabled_features.alphaToOne &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT)) {
skip |= LogError("VUID-VkPipelineMultisampleStateCreateInfo-alphaToOneEnable-00785", device,
create_info_loc.dot(Field::pMultisampleState).dot(Field::alphaToOneEnable),
"is VK_TRUE, but the alphaToOne feature was not enabled.");
}
}
}
if (auto provoking_vertex_state_ci =
vku::FindStructInPNextChain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>(raster_state->pNext)) {
if (provoking_vertex_state_ci->provokingVertexMode == VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT &&
!enabled_features.provokingVertexLast && !pipeline.IsDynamic(CB_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT)) {
skip |= LogError(
"VUID-VkPipelineRasterizationProvokingVertexStateCreateInfoEXT-provokingVertexMode-04883", device,
raster_loc.pNext(Struct::VkPipelineRasterizationProvokingVertexStateCreateInfoEXT, Field::provokingVertexMode),
"is VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT but the provokingVertexLast feature was not enabled.");
}
}
if (const auto rasterization_state_stream_ci =
vku::FindStructInPNextChain<VkPipelineRasterizationStateStreamCreateInfoEXT>(raster_state->pNext)) {
skip |= ValidatePipelineRasterizationStateStreamCreateInfo(pipeline, *rasterization_state_stream_ci, raster_loc);
}
if (const auto rasterization_conservative_state_ci =
vku::FindStructInPNextChain<VkPipelineRasterizationConservativeStateCreateInfoEXT>(raster_state->pNext)) {
skip |=
ValidatePipelineRasterizationConservativeStateCreateInfo(pipeline, *rasterization_conservative_state_ci, raster_loc);
} else {
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-09639", device, raster_loc.dot(Field::pNext),
"is missing VkPipelineRasterizationConservativeStateCreateInfoEXT which it needs because this "
"pipeline has VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT but not "
"VK_DYNAMIC_STATE_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE_EXT.\n%s",
PrintPNextChain(Struct::VkPipelineRasterizationStateCreateInfo, raster_state->pNext).c_str());
}
}
if (const auto *depth_bias_representation = vku::FindStructInPNextChain<VkDepthBiasRepresentationInfoEXT>(raster_state->pNext);
depth_bias_representation != nullptr) {
skip |= ValidateDepthBiasRepresentationInfo(raster_loc, LogObjectList(device), *depth_bias_representation);
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineRenderPassRasterization(const vvl::Pipeline &pipeline, const vvl::RenderPass &rp_state,
const vku::safe_VkSubpassDescription2 &subpass_desc,
const Location &create_info_loc) const {
bool skip = false;
const auto raster_state = pipeline.RasterizationState();
if (!raster_state || raster_state->rasterizerDiscardEnable) return skip;
// If subpass uses a depth/stencil attachment, pDepthStencilState must be a pointer to a valid structure
if (pipeline.fragment_shader_state) {
if (subpass_desc.pDepthStencilAttachment && subpass_desc.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) {
const Location ds_loc = create_info_loc.dot(Field::pDepthStencilState);
const auto ds_state = pipeline.DepthStencilState();
if (!ds_state) {
if (!pipeline.IsDepthStencilStateDynamic() || !IsExtEnabled(extensions.vk_ext_extended_dynamic_state3)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09028", rp_state.Handle(), ds_loc,
"is NULL when rasterization is enabled "
"and subpass %" PRIu32 " uses a depth/stencil attachment.",
pipeline.Subpass());
}
} else {
const float minDepthBounds = ds_state->minDepthBounds;
const float maxDepthBounds = ds_state->maxDepthBounds;
if (ds_state->depthBoundsTestEnable == VK_TRUE && !pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE)) {
if (!enabled_features.depthBounds) {
skip |= LogError("VUID-VkPipelineDepthStencilStateCreateInfo-depthBoundsTestEnable-00598", device,
ds_loc.dot(Field::depthBoundsTestEnable),
"is VK_TRUE, but depthBounds feature was not enabled.");
}
if (!IsExtEnabled(extensions.vk_ext_depth_range_unrestricted) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BOUNDS)) {
if (!(minDepthBounds >= 0.0) || !(minDepthBounds <= 1.0)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-02510", device, ds_loc.dot(Field::minDepthBounds),
"is %f, depthBoundsTestEnable is VK_TRUE, but VK_EXT_depth_range_unrestricted extension "
"is not enabled (and not using VK_DYNAMIC_STATE_DEPTH_BOUNDS).",
minDepthBounds);
}
if (!(maxDepthBounds >= 0.0) || !(maxDepthBounds <= 1.0)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-02510", device, ds_loc.dot(Field::minDepthBounds),
"is %f, depthBoundsTestEnable is VK_TRUE, but VK_EXT_depth_range_unrestricted extension "
"is not enabled (and not using VK_DYNAMIC_STATE_DEPTH_BOUNDS).",
maxDepthBounds);
}
}
}
if (ds_state->depthBoundsTestEnable == VK_TRUE || pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE)) {
if (minDepthBounds > maxDepthBounds && !pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BOUNDS)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-10913", device, ds_loc.dot(Field::minDepthBounds),
"(%f) is greater than maxDepthBounds (%f).%s", minDepthBounds, maxDepthBounds,
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE)
? " (Even if depthBoundsTestEnable is dynamic, it can never be enabled and therefore this "
"is still invalid)"
: "");
}
}
}
}
}
// If subpass uses color attachments, pColorBlendState must be valid pointer
if (pipeline.fragment_output_state && !pipeline.fragment_output_state->color_blend_state &&
!pipeline.IsColorBlendStateDynamic()) {
for (uint32_t i = 0; i < subpass_desc.colorAttachmentCount; ++i) {
if (subpass_desc.pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09030", rp_state.Handle(),
create_info_loc.dot(Field::pColorBlendState),
"is NULL when rasterization is enabled and "
"pSubpasses[%" PRIu32 "].pColorAttachments[%" PRIu32 "].attachment (%" PRIu32
") is a color attachments.",
pipeline.Subpass(), i, subpass_desc.pColorAttachments[i].attachment);
break; // only wnat one error, else becomes spam
}
}
}
return skip;
}
bool CoreChecks::ValidateSampleLocationsInfo(const VkSampleLocationsInfoEXT &sample_location_info, const Location &loc) const {
bool skip = false;
const VkSampleCountFlagBits sample_count = sample_location_info.sampleLocationsPerPixel;
const uint32_t sample_total_size = sample_location_info.sampleLocationGridSize.width *
sample_location_info.sampleLocationGridSize.height * SampleCountSize(sample_count);
if (sample_location_info.sampleLocationsCount != sample_total_size) {
skip |= LogError("VUID-VkSampleLocationsInfoEXT-sampleLocationsCount-01527", device, loc.dot(Field::sampleLocationsCount),
"(%" PRIu32
") must equal grid width * grid height * pixel "
"sample rate which currently is (%" PRIu32 " * %" PRIu32 " * %" PRIu32 ").",
sample_location_info.sampleLocationsCount, sample_location_info.sampleLocationGridSize.width,
sample_location_info.sampleLocationGridSize.height, SampleCountSize(sample_count));
}
if ((phys_dev_ext_props.sample_locations_props.sampleLocationSampleCounts & sample_count) == 0) {
skip |=
LogError("VUID-VkSampleLocationsInfoEXT-sampleLocationsPerPixel-01526", device, loc.dot(Field::sampleLocationsPerPixel),
"is %s, but VkPhysicalDeviceSampleLocationsPropertiesEXT::sampleLocationSampleCounts is %s.",
string_VkSampleCountFlagBits(sample_count),
string_VkSampleCountFlags(phys_dev_ext_props.sample_locations_props.sampleLocationSampleCounts).c_str());
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineMultisampleState(const vvl::Pipeline &pipeline, const vvl::RenderPass &rp_state,
const vku::safe_VkSubpassDescription2 &subpass_desc,
const Location &create_info_loc) const {
bool skip = false;
const auto *multisample_state = pipeline.MultisampleState();
if (!multisample_state) return skip;
const Location ms_loc = create_info_loc.dot(Field::pMultisampleState);
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.create_info.pAttachments[attachment].samples);
}
}
};
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)) {
const uint32_t raster_samples = SampleCountSize(multisample_state->rasterizationSamples);
if (!IsMixSamplingSupported()) {
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.create_info.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("VUID-VkGraphicsPipelineCreateInfo-multisampledRenderToSingleSampled-06853", device,
ms_loc.dot(Field::rasterizationSamples),
"(%" PRIu32
") does not match the number of samples of the RenderPass color and/or depth attachment (%" PRIu32
").",
raster_samples, subpass_num_samples);
}
}
if (IsExtEnabled(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.create_info.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.create_info.pAttachments[subpass_desc.pDepthStencilAttachment->attachment].samples);
}
if (!pipeline.RasterizationDisabled() && (max_sample_count != static_cast<VkSampleCountFlagBits>(0)) &&
(multisample_state->rasterizationSamples != max_sample_count)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-subpass-01505", device, ms_loc.dot(Field::rasterizationSamples),
"(%s) is different from the max attachment samples (%s) used in pSubpasses[%" PRIu32 "].",
string_VkSampleCountFlagBits(multisample_state->rasterizationSamples),
string_VkSampleCountFlagBits(max_sample_count), pipeline.Subpass());
}
}
if (IsExtEnabled(extensions.vk_nv_framebuffer_mixed_samples)) {
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.create_info.pAttachments[attachment].samples);
const auto ds_state = pipeline.DepthStencilState();
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(
"VUID-VkGraphicsPipelineCreateInfo-subpass-01411", device, ms_loc.dot(Field::rasterizationSamples),
"(%" PRIu32 ") does not match the number of samples of the RenderPass depth attachment (%" PRIu32 ").",
raster_samples, subpass_depth_samples);
}
}
}
if (IsPowerOfTwo(subpass_color_samples)) {
if (raster_samples < subpass_color_samples) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-subpass-01412", device, ms_loc.dot(Field::rasterizationSamples),
"(%" PRIu32
") "
"is not greater or equal to the number of samples of the RenderPass color attachment (%" PRIu32 ").",
raster_samples, subpass_color_samples);
}
if (multisample_state) {
if ((raster_samples > subpass_color_samples) && (multisample_state->sampleShadingEnable == VK_TRUE)) {
skip |= LogError("VUID-VkPipelineMultisampleStateCreateInfo-rasterizationSamples-01415", device,
ms_loc.dot(Field::rasterizationSamples),
"(%" PRIu32
") is greater than the number of "
"samples of the "
"subpass color attachment (%" PRIu32 ") and sampleShadingEnable is VK_TRUE.",
raster_samples, subpass_color_samples);
}
const auto *coverage_modulation_state =
vku::FindStructInPNextChain<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(
"VUID-VkPipelineCoverageModulationStateCreateInfoNV-coverageModulationTableEnable-01405", device,
ms_loc.pNext(Struct::VkPipelineCoverageModulationStateCreateInfoNV,
Field::coverageModulationTableCount),
"is %" PRIu32 ".", coverage_modulation_state->coverageModulationTableCount);
}
}
}
}
}
if (IsExtEnabled(extensions.vk_nv_coverage_reduction_mode)) {
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.create_info.pAttachments[attachment].samples);
}
if (multisample_state && IsPowerOfTwo(subpass_color_samples) &&
(subpass_depth_samples == 0 || IsPowerOfTwo(subpass_depth_samples))) {
const auto *coverage_reduction_state =
vku::FindStructInPNextChain<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("VUID-VkGraphicsPipelineCreateInfo-coverageReductionMode-02722", device, create_info_loc,
"specifies a combination of coverage "
"reduction mode (%s), pMultisampleState->rasterizationSamples (%" PRIu32
"), sample counts for "
"the subpass color and depth/stencil attachments is not a valid combination returned by "
"vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV.",
string_VkCoverageReductionModeNV(coverage_reduction_mode), raster_samples);
}
}
}
}
const auto msrtss_info = vku::FindStructInPNextChain<VkMultisampledRenderToSingleSampledInfoEXT>(subpass_desc.pNext);
if (msrtss_info && msrtss_info->multisampledRenderToSingleSampledEnable &&
(msrtss_info->rasterizationSamples != multisample_state->rasterizationSamples)) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06854", rp_state.Handle(),
create_info_loc.dot(Field::pSubpasses, pipeline.Subpass())
.pNext(Struct::VkMultisampledRenderToSingleSampledInfoEXT, Field::rasterizationSamples),
"(%" PRIu32 ") is not equal to %s (%" PRIu32 ") and multisampledRenderToSingleSampledEnable is VK_TRUE.",
msrtss_info->rasterizationSamples, ms_loc.dot(Field::rasterizationSamples).Fields().c_str(),
multisample_state->rasterizationSamples);
}
if (rp_state.UsesNoAttachment(pipeline.Subpass())) {
if ((multisample_state->rasterizationSamples & phys_dev_props.limits.framebufferNoAttachmentsSampleCounts) == 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-subpass-00758", rp_state.Handle(),
ms_loc.dot(Field::rasterizationSamples),
"(%s) is not in "
"framebufferNoAttachmentsSampleCounts (%s) but attempting to use a zero-attachment subpass.",
string_VkSampleCountFlagBits(multisample_state->rasterizationSamples),
string_VkSampleCountFlags(phys_dev_props.limits.framebufferNoAttachmentsSampleCounts).c_str());
}
}
}
// VK_NV_fragment_coverage_to_color
const auto coverage_to_color_state = vku::FindStructInPNextChain<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.create_info.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("VUID-VkPipelineCoverageToColorStateCreateInfoNV-coverageToColorEnable-01404", device,
ms_loc.pNext(Struct::VkPipelineCoverageToColorStateCreateInfoNV, Field::coverageToColorLocation),
"is %" PRIu32 ", but %s", coverage_to_color_state->coverageToColorLocation, error_detail.c_str());
}
}
// VK_EXT_sample_locations
const auto *sample_location_state =
vku::FindStructInPNextChain<VkPipelineSampleLocationsStateCreateInfoEXT>(multisample_state->pNext);
if (sample_location_state != nullptr) {
if ((sample_location_state->sampleLocationsEnable == VK_TRUE) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)) {
const VkSampleLocationsInfoEXT sample_location_info = sample_location_state->sampleLocationsInfo;
const Location sample_info_loc =
ms_loc.pNext(Struct::VkPipelineSampleLocationsStateCreateInfoEXT, Field::sampleLocationsInfo);
skip |= ValidateSampleLocationsInfo(sample_location_info, sample_info_loc.dot(Field::sampleLocationsInfo));
const VkExtent2D grid_size = sample_location_info.sampleLocationGridSize;
VkMultisamplePropertiesEXT multisample_prop = vku::InitStructHelper();
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("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07610", device,
sample_info_loc.dot(Field::sampleLocationGridSize).dot(Field::width),
"(%" PRIu32
") is not evenly divided by VkMultisamplePropertiesEXT::sampleLocationGridSize.width (%" PRIu32 ").",
grid_size.width, max_grid_size.width);
}
if (SafeModulo(max_grid_size.height, grid_size.height) != 0) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07611", device,
sample_info_loc.dot(Field::sampleLocationGridSize).dot(Field::height),
"(%" PRIu32
") is not evenly divided by VkMultisamplePropertiesEXT::sampleLocationGridSize.height (%" PRIu32 ").",
grid_size.height, max_grid_size.height);
}
if (sample_location_info.sampleLocationsPerPixel != multisample_state->rasterizationSamples) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07612", device,
sample_info_loc.dot(Field::sampleLocationsPerPixel), "(%s) is different from %s (%s).",
string_VkSampleCountFlagBits(sample_location_info.sampleLocationsPerPixel),
ms_loc.dot(Field::rasterizationSamples).Fields().c_str(),
string_VkSampleCountFlagBits(multisample_state->rasterizationSamples));
}
}
}
if (IsExtEnabled(extensions.vk_qcom_render_pass_shader_resolve)) {
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.create_info.pAttachments[attachment].samples);
}
}
if ((subpass_desc.flags & VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM) != 0) {
const uint32_t raster_samples = SampleCountSize(multisample_state->rasterizationSamples);
if ((raster_samples != subpass_input_attachment_samples) &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-rasterizationSamples-04899", device,
ms_loc.dot(Field::rasterizationSamples),
"%s is different then pSubpasses[%" PRIu32 "] input attachment samples (%" PRIu32
") but flags include VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM.",
string_VkSampleCountFlagBits(multisample_state->rasterizationSamples), pipeline.Subpass(),
subpass_input_attachment_samples);
}
if (multisample_state->sampleShadingEnable == VK_TRUE) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-sampleShadingEnable-04900", device,
ms_loc.dot(Field::sampleShadingEnable),
"is VK_TRUE, but pSubpasses[%" PRIu32
"] includes "
"VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM. ",
pipeline.Subpass());
}
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineNullState(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const bool null_rp = pipeline.IsRenderPassNull();
if (null_rp) {
if (!pipeline.DepthStencilState()) {
if (pipeline.fragment_shader_state && !pipeline.fragment_output_state) {
if (!pipeline.IsDepthStencilStateDynamic() || !IsExtEnabled(extensions.vk_ext_extended_dynamic_state3)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09035", device,
create_info_loc.dot(Field::pDepthStencilState), "is NULL.");
}
}
}
}
if (IsExtEnabled(extensions.vk_ext_graphics_pipeline_library)) {
if (pipeline.OwnsLibState(pipeline.fragment_output_state) && !pipeline.MultisampleState()) {
// if VK_KHR_dynamic_rendering is not enabled, can be null renderpass if using GPL
if (!null_rp) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderpass-06631", device,
create_info_loc.dot(Field::pMultisampleState),
"is NULL, but pipeline is being created with fragment shader that uses samples.");
}
}
}
const auto &pipeline_ci = pipeline.GraphicsCreateInfo();
if (!pipeline_ci.pMultisampleState && pipeline.OwnsLibState(pipeline.fragment_output_state)) {
const bool dynamic_alpha_to_one =
pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT) || !enabled_features.alphaToOne;
if (!IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_SAMPLE_MASK_EXT) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT) || !dynamic_alpha_to_one) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pMultisampleState-09026", device,
create_info_loc.dot(Field::pMultisampleState),
"is NULL."
"\nIf the following are all set, it can be NULL"
"\n Enable VK_EXT_extended_dynamic_state3 (%senabled)"
"\n Use VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT (%s)"
"\n Use VK_DYNAMIC_STATE_SAMPLE_MASK_EXT (%s)"
"\n Use VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT (%s)"
"\n Use CB_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT (%s) or enable alphaToOne feature (%s)\n",
IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ? "" : "not ",
pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_SAMPLE_MASK_EXT) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT) ? "set" : "not set",
enabled_features.alphaToOne ? "VK_TRUE" : "VK_FALSE");
}
}
if (!pipeline.RasterizationState()) {
if (!pipeline_ci.pRasterizationState && pipeline.OwnsLibState(pipeline.pre_raster_state)) {
if (!IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_POLYGON_MODE_EXT) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_CULL_MODE) || !pipeline.IsDynamic(CB_DYNAMIC_STATE_FRONT_FACE) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BIAS_ENABLE) || !pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BIAS) ||
!pipeline.IsDynamic(CB_DYNAMIC_STATE_LINE_WIDTH)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pRasterizationState-06601", device,
create_info_loc.dot(Field::pRasterizationState),
"is NULL."
"\nIf the following are all set, it can be NULL"
"\n Enable VK_EXT_extended_dynamic_state3 (%senabled)"
"\n Use VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT (%s)"
"\n Use VK_DYNAMIC_STATE_POLYGON_MODE_EXT (%s)"
"\n Use VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE (%s)"
"\n Use VK_DYNAMIC_STATE_CULL_MODE (%s)"
"\n Use VK_DYNAMIC_STATE_FRONT_FACE (%s)"
"\n Use VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE (%s)"
"\n Use VK_DYNAMIC_STATE_DEPTH_BIAS (%s)"
"\n Use VK_DYNAMIC_STATE_LINE_WIDTH (%s)\n",
IsExtEnabled(extensions.vk_ext_extended_dynamic_state3) ? "" : "not ",
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_POLYGON_MODE_EXT) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_CULL_MODE) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_FRONT_FACE) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BIAS_ENABLE) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BIAS) ? "set" : "not set",
pipeline.IsDynamic(CB_DYNAMIC_STATE_LINE_WIDTH) ? "set" : "not set");
}
}
}
return skip;
}
// VK_EXT_rasterization_order_attachment_access
bool CoreChecks::ValidateGraphicsPipelineRasterizationOrderAttachmentAccess(const vvl::Pipeline &pipeline,
const vku::safe_VkSubpassDescription2 *subpass_desc,
const Location &create_info_loc) const {
bool skip = false;
const auto rp_state = pipeline.RenderPassState();
const bool null_rp = pipeline.IsRenderPassNull();
if (!null_rp && !rp_state) return skip; // invalid render pass
if (const auto ds_state = pipeline.DepthStencilState()) {
const Location ds_loc = create_info_loc.dot(Field::pDepthStencilState);
const bool raster_order_attach_depth =
ds_state->flags & VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_EXT;
const bool raster_order_attach_stencil =
ds_state->flags & VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_EXT;
if (null_rp) {
if (!enabled_features.dynamicRenderingLocalRead && (raster_order_attach_depth || raster_order_attach_stencil)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-None-09526", device, ds_loc.dot(Field::flags),
"is %s but renderPass is VK_NULL_HANDLE.",
string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str());
}
} else if (!rp_state->UsesDynamicRendering()) {
if (raster_order_attach_depth) {
// Can't validate these in stateless as possible to have a pDepthStencilState pointer with garbage
if (!enabled_features.rasterizationOrderDepthAttachmentAccess) {
skip |= LogError("VUID-VkPipelineDepthStencilStateCreateInfo-rasterizationOrderDepthAttachmentAccess-06463",
device, ds_loc.dot(Field::flags),
"is (%s) but rasterizationOrderDepthAttachmentAccess feature was not enabled",
string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str());
}
if (subpass_desc &&
(subpass_desc->flags & VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_EXT) == 0) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09528", rp_state->Handle(), ds_loc.dot(Field::flags),
"is (%s) but VkRenderPassCreateInfo::VkSubpassDescription::flags == %s",
string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str(),
string_VkSubpassDescriptionFlags(subpass_desc->flags).c_str());
}
}
if (raster_order_attach_stencil) {
if (!enabled_features.rasterizationOrderStencilAttachmentAccess) {
skip |= LogError("VUID-VkPipelineDepthStencilStateCreateInfo-rasterizationOrderStencilAttachmentAccess-06464",
device, ds_loc.dot(Field::flags),
"is (%s) but rasterizationOrderStencilAttachmentAccess feature was not enabled",
string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str());
}
if (subpass_desc &&
(subpass_desc->flags & VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_EXT) == 0) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09529", rp_state->Handle(), ds_loc.dot(Field::flags),
"is (%s) but VkRenderPassCreateInfo::VkSubpassDescription::flags == %s",
string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str(),
string_VkSubpassDescriptionFlags(subpass_desc->flags).c_str());
}
}
}
}
if (const auto color_blend_state = pipeline.ColorBlendState()) {
const Location color_loc = create_info_loc.dot(Field::pColorBlendState);
if (color_blend_state->flags & VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_EXT) {
if (!enabled_features.rasterizationOrderColorAttachmentAccess) {
skip |=
LogError("VUID-VkPipelineColorBlendStateCreateInfo-rasterizationOrderColorAttachmentAccess-06465", device,
color_loc.dot(Field::flags), "(%s) but rasterizationOrderColorAttachmentAccess feature is not enabled",
string_VkPipelineColorBlendStateCreateFlags(color_blend_state->flags).c_str());
}
if (!enabled_features.dynamicRenderingLocalRead && null_rp) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06482", device, color_loc.dot(Field::flags),
"includes "
"VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_EXT, but "
"renderpass is VK_NULL_HANDLE.");
}
if (!null_rp && subpass_desc &&
(subpass_desc->flags & VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_EXT) == 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09527", rp_state->Handle(),
color_loc.dot(Field::flags), "(%s) but VkRenderPassCreateInfo::VkSubpassDescription::flags == %s",
string_VkPipelineColorBlendStateCreateFlags(color_blend_state->flags).c_str(),
string_VkSubpassDescriptionFlags(subpass_desc->flags).c_str());
}
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineDynamicState(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
auto get_state_index = [&pipeline](const VkDynamicState state) {
const auto dynamic_info = pipeline.GraphicsCreateInfo().pDynamicState;
for (uint32_t i = 0; i < dynamic_info->dynamicStateCount; i++) {
if (dynamic_info->pDynamicStates[i] == state) {
return i;
}
}
assert(false);
return dynamic_info->dynamicStateCount;
};
bool skip = false;
if (pipeline.create_info_shaders & VK_SHADER_STAGE_MESH_BIT_EXT) {
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07065", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY)),
"is VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY, but the pipeline contains a mesh shader.");
} else if (pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07065", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE)),
"is VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE, but the pipeline contains a mesh shader.");
}
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE)),
"is VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE, but the pipeline contains a mesh shader.");
} else if (pipeline.IsDynamic(CB_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07066", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT)),
"is VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT, but the pipeline contains a mesh shader.");
}
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07067", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)),
"is VK_DYNAMIC_STATE_VERTEX_INPUT_EXT, but the pipeline contains a mesh shader.");
}
}
if (api_version < VK_API_VERSION_1_3 && !enabled_features.extendedDynamicState &&
(pipeline.IsDynamic(CB_DYNAMIC_STATE_CULL_MODE) || pipeline.IsDynamic(CB_DYNAMIC_STATE_FRONT_FACE) ||
pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) || pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT_WITH_COUNT) ||
pipeline.IsDynamic(CB_DYNAMIC_STATE_SCISSOR_WITH_COUNT) ||
pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE) ||
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_TEST_ENABLE) || pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_WRITE_ENABLE) ||
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_COMPARE_OP) || pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE) ||
pipeline.IsDynamic(CB_DYNAMIC_STATE_STENCIL_TEST_ENABLE) || pipeline.IsDynamic(CB_DYNAMIC_STATE_STENCIL_OP))) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03378", device, create_info_loc.dot(Field::pDynamicState),
"contains dynamic states from VK_EXT_extended_dynamic_state, but the extendedDynamicState feature was not enabled.");
}
if (api_version < VK_API_VERSION_1_3 && !enabled_features.extendedDynamicState2) {
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE)) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04868", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE)),
"is VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE, but the extendedDynamicState2 feature was not enabled.");
} else if (pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_BIAS_ENABLE)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04868", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE)),
"is VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE, but the extendedDynamicState2 feature was not enabled.");
} else if (pipeline.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE)) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04868", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE)),
"is VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE, but the extendedDynamicState2 feature was not enabled.");
}
}
if (!enabled_features.extendedDynamicState2LogicOp && pipeline.IsDynamic(CB_DYNAMIC_STATE_LOGIC_OP_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04869", device,
create_info_loc.dot(Field::pDynamicState).dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_LOGIC_OP_EXT)),
"is VK_DYNAMIC_STATE_LOGIC_OP_EXT, but the extendedDynamicState2LogicOp feature was not enabled.");
}
if (!enabled_features.extendedDynamicState2PatchControlPoints &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04870", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT)),
"is VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT, but the extendedDynamicState2PatchControlPoints feature "
"was not enabled.");
}
if (!enabled_features.extendedDynamicState3TessellationDomainOrigin &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_TESSELLATION_DOMAIN_ORIGIN_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3TessellationDomainOrigin-07370", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_TESSELLATION_DOMAIN_ORIGIN_EXT)),
"is VK_DYNAMIC_STATE_TESSELLATION_DOMAIN_ORIGIN_EXT, but the "
"extendedDynamicState3TessellationDomainOrigin feature was not enabled.");
}
if (!enabled_features.extendedDynamicState3DepthClampEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3DepthClampEnable-07371", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, but the extendedDynamicState3DepthClampEnable feature was not enabled.");
}
if (!enabled_features.extendedDynamicState3PolygonMode && pipeline.IsDynamic(CB_DYNAMIC_STATE_POLYGON_MODE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3PolygonMode-07372", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_POLYGON_MODE_EXT)),
"is VK_DYNAMIC_STATE_POLYGON_MODE_EXT, but the extendedDynamicState3PolygonMode feature was not enabled.");
}
if (!enabled_features.extendedDynamicState3RasterizationSamples &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3RasterizationSamples-07373", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)),
"is VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT but the extendedDynamicState3RasterizationSamples feature "
"is not enabled.");
}
if (!enabled_features.extendedDynamicState3SampleMask && pipeline.IsDynamic(CB_DYNAMIC_STATE_SAMPLE_MASK_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3SampleMask-07374", device,
create_info_loc.dot(Field::pDynamicState).dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_SAMPLE_MASK_EXT)),
"is VK_DYNAMIC_STATE_SAMPLE_MASK_EXT but the extendedDynamicState3SampleMask feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3AlphaToCoverageEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3AlphaToCoverageEnable-07375", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT but the extendedDynamicState3AlphaToCoverageEnable "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3AlphaToOneEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3AlphaToOneEnable-07376", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT but the extendedDynamicState3AlphaToOneEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3LogicOpEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT)) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3LogicOpEnable-07377", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT but the extendedDynamicState3LogicOpEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ColorBlendEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ColorBlendEnable-07378", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT but the extendedDynamicState3ColorBlendEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ColorBlendEquation &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ColorBlendEquation-07379", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT)),
"is VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT but the extendedDynamicState3ColorBlendEquation feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ColorWriteMask && pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ColorWriteMask-07380", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT)),
"is VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT but the extendedDynamicState3ColorWriteMask feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3RasterizationStream &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_STREAM_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3RasterizationStream-07381", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_RASTERIZATION_STREAM_EXT)),
"is VK_DYNAMIC_STATE_RASTERIZATION_STREAM_EXT but the extendedDynamicState3RasterizationStream feature is "
"not enabled.");
}
if (!enabled_features.extendedDynamicState3ConservativeRasterizationMode &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ConservativeRasterizationMode-07382", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT)),
"is VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT but the "
"extendedDynamicState3ConservativeRasterizationMode feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ExtraPrimitiveOverestimationSize &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ExtraPrimitiveOverestimationSize-07383", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE_EXT)),
"is VK_DYNAMIC_STATE_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE_EXT but the "
"extendedDynamicState3ExtraPrimitiveOverestimationSize feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3DepthClipEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3DepthClipEnable-07384", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT but the extendedDynamicState3DepthClipEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3SampleLocationsEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3SampleLocationsEnable-07385", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT but the extendedDynamicState3SampleLocationsEnable "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ColorBlendAdvanced &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ColorBlendAdvanced-07386", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT)),
"is VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT but the extendedDynamicState3ColorBlendAdvanced feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ProvokingVertexMode &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ProvokingVertexMode-07387", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT)),
"is VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT but the extendedDynamicState3ProvokingVertexMode feature "
"is not enabled.");
}
if (!enabled_features.extendedDynamicState3LineRasterizationMode &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3LineRasterizationMode-07388", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT)),
"is VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT but the extendedDynamicState3LineRasterizationMode "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3LineStippleEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3LineStippleEnable-07389", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT but the extendedDynamicState3LineStippleEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3DepthClipNegativeOneToOne &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3DepthClipNegativeOneToOne-07390", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT)),
"is VK_DYNAMIC_STATE_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT but the "
"extendedDynamicState3DepthClipNegativeOneToOne feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ViewportWScalingEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT_W_SCALING_ENABLE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ViewportWScalingEnable-07391", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_ENABLE_NV)),
"is VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_ENABLE_NV but the extendedDynamicState3ViewportWScalingEnable "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ViewportSwizzle && pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT_SWIZZLE_NV)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ViewportSwizzle-07392", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_VIEWPORT_SWIZZLE_NV)),
"is VK_DYNAMIC_STATE_VIEWPORT_SWIZZLE_NV but the extendedDynamicState3ViewportSwizzle feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3CoverageToColorEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COVERAGE_TO_COLOR_ENABLE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3CoverageToColorEnable-07393", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COVERAGE_TO_COLOR_ENABLE_NV)),
"is VK_DYNAMIC_STATE_COVERAGE_TO_COLOR_ENABLE_NV but the extendedDynamicState3CoverageToColorEnable "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3CoverageToColorLocation &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COVERAGE_TO_COLOR_LOCATION_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3CoverageToColorLocation-07394", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COVERAGE_TO_COLOR_LOCATION_NV)),
"is VK_DYNAMIC_STATE_COVERAGE_TO_COLOR_LOCATION_NV but the extendedDynamicState3CoverageToColorLocation "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3CoverageModulationMode &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COVERAGE_MODULATION_MODE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3CoverageModulationMode-07395", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COVERAGE_MODULATION_MODE_NV)),
"is VK_DYNAMIC_STATE_COVERAGE_MODULATION_MODE_NV but the extendedDynamicState3CoverageModulationMode "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3CoverageModulationTableEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_ENABLE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3CoverageModulationTableEnable-07396", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_ENABLE_NV)),
"is VK_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_ENABLE_NV but the "
"extendedDynamicState3CoverageModulationTableEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3CoverageModulationTable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3CoverageModulationTable-07397", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_NV)),
"is VK_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_NV but the extendedDynamicState3CoverageModulationTable "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3CoverageReductionMode &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_COVERAGE_REDUCTION_MODE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3CoverageReductionMode-07398", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COVERAGE_REDUCTION_MODE_NV)),
"is VK_DYNAMIC_STATE_COVERAGE_REDUCTION_MODE_NV but the extendedDynamicState3CoverageReductionMode "
"feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3RepresentativeFragmentTestEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_REPRESENTATIVE_FRAGMENT_TEST_ENABLE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3RepresentativeFragmentTestEnable-07399", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_REPRESENTATIVE_FRAGMENT_TEST_ENABLE_NV)),
"is VK_DYNAMIC_STATE_REPRESENTATIVE_FRAGMENT_TEST_ENABLE_NV but the "
"extendedDynamicState3RepresentativeFragmentTestEnable feature is not enabled.");
}
if (!enabled_features.extendedDynamicState3ShadingRateImageEnable &&
pipeline.IsDynamic(CB_DYNAMIC_STATE_SHADING_RATE_IMAGE_ENABLE_NV)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-extendedDynamicState3ShadingRateImageEnable-07400", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_SHADING_RATE_IMAGE_ENABLE_NV)),
"is VK_DYNAMIC_STATE_SHADING_RATE_IMAGE_ENABLE_NV but the extendedDynamicState3ShadingRateImageEnable "
"feature is not enabled.");
}
if (!enabled_features.vertexInputDynamicState && pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04807", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)),
"is VK_DYNAMIC_STATE_VERTEX_INPUT_EXT but the vertexInputDynamicState feature is not enabled.");
}
if (!enabled_features.colorWriteEnable && pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04800", device,
create_info_loc.dot(Field::pDynamicState)
.dot(Field::pDynamicStates, get_state_index(VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT)),
"is VK_DYNAMIC_STATE_VERTEX_INPUT_EXT but the colorWriteEnable feature is not enabled.");
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineFragmentShadingRateState(
const vvl::Pipeline &pipeline, const VkPipelineFragmentShadingRateStateCreateInfoKHR &fragment_shading_rate_state,
const Location &create_info_loc) const {
bool skip = false;
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR)) {
return skip;
}
const Location fragment_loc =
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::fragmentSize);
if (fragment_shading_rate_state.fragmentSize.width == 0) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04494", device, fragment_loc.dot(Field::width), "is zero.");
}
if (fragment_shading_rate_state.fragmentSize.height == 0) {
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04495", device, fragment_loc.dot(Field::height), "is zero.");
}
if (fragment_shading_rate_state.fragmentSize.width != 0 && !IsPowerOfTwo(fragment_shading_rate_state.fragmentSize.width)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04496", device, fragment_loc.dot(Field::width),
"is %" PRIu32 ".", fragment_shading_rate_state.fragmentSize.width);
}
if (fragment_shading_rate_state.fragmentSize.height != 0 && !IsPowerOfTwo(fragment_shading_rate_state.fragmentSize.height)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04497", device, fragment_loc.dot(Field::height),
"is %" PRIu32 ".", fragment_shading_rate_state.fragmentSize.height);
}
if (fragment_shading_rate_state.fragmentSize.width > 4) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04498", device, fragment_loc.dot(Field::width),
"is %" PRIu32 ".", fragment_shading_rate_state.fragmentSize.width);
}
if (fragment_shading_rate_state.fragmentSize.height > 4) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04499", device, fragment_loc.dot(Field::height),
"is %" PRIu32 ".", fragment_shading_rate_state.fragmentSize.height);
}
if (!enabled_features.pipelineFragmentShadingRate && fragment_shading_rate_state.fragmentSize.width != 1) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500", device, fragment_loc.dot(Field::width),
"is %" PRIu32 ", but the pipelineFragmentShadingRate feature was not enabled.",
fragment_shading_rate_state.fragmentSize.width);
}
if (!enabled_features.pipelineFragmentShadingRate && fragment_shading_rate_state.fragmentSize.height != 1) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04500", device, fragment_loc.dot(Field::height),
"is %" PRIu32 ", but the pipelineFragmentShadingRate feature was not enabled.",
fragment_shading_rate_state.fragmentSize.height);
}
if (!enabled_features.primitiveFragmentShadingRate &&
fragment_shading_rate_state.combinerOps[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04501", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 0),
"is %s, but the primitiveFragmentShadingRate feature was not enabled.",
string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state.combinerOps[0]));
}
if (!enabled_features.attachmentFragmentShadingRate &&
fragment_shading_rate_state.combinerOps[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-04502", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 1),
"is %s, but the attachmentFragmentShadingRate feature was not enabled.",
string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state.combinerOps[1]));
}
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("VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 0),
"is %s, but the fragmentShadingRateNonTrivialCombinerOps feature is not enabled.",
string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state.combinerOps[0]));
}
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("VUID-VkGraphicsPipelineCreateInfo-fragmentShadingRateNonTrivialCombinerOps-04506", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 1),
"is %s, but the fragmentShadingRateNonTrivialCombinerOps feature is not enabled.",
string_VkFragmentShadingRateCombinerOpKHR(fragment_shading_rate_state.combinerOps[1]));
}
auto is_valid_enum_value = [](VkFragmentShadingRateCombinerOpKHR value) {
switch (value) {
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR:
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR:
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_KHR:
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR:
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR:
return true;
default:
return false;
};
};
const auto combiner_ops = fragment_shading_rate_state.combinerOps;
if (pipeline.OwnsLibState(pipeline.pre_raster_state) || pipeline.OwnsLibState(pipeline.fragment_shader_state)) {
if (!is_valid_enum_value(combiner_ops[0])) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 0),
"(0x%" PRIx32 ") is invalid.", combiner_ops[0]);
}
if (!is_valid_enum_value(combiner_ops[1])) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06568", device,
create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 1),
"(0x%" PRIx32 ") is invalid.", combiner_ops[1]);
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineDynamicRendering(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const auto &pipeline_ci = pipeline.GraphicsCreateInfo();
if (pipeline_ci.renderPass != VK_NULL_HANDLE) {
return skip;
}
const auto color_blend_state = pipeline.ColorBlendState();
const auto *rendering_struct = pipeline.rendering_create_info;
if (!rendering_struct) {
// The spec says when thie struct is not included it is same as
// viewMask = 0, colorAttachmentCount = 0, formats = VK_FORMAT_UNDEFINED.
// Most VUs are worded around this, but some need to be validated here
if (pipeline.OwnsLibState(pipeline.fragment_output_state) && color_blend_state && color_blend_state->attachmentCount > 0) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06055", device,
create_info_loc.dot(Field::pColorBlendState).dot(Field::attachmentCount),
"is %" PRIu32
", but VkPipelineRenderingCreateInfo::colorAttachmentCount is zero because the pNext chain was not included.",
color_blend_state->attachmentCount);
}
return skip;
}
if (!pipeline.RasterizationDisabled()) {
if (pipeline.fragment_shader_state && pipeline.fragment_output_state &&
((rendering_struct->depthAttachmentFormat != VK_FORMAT_UNDEFINED) ||
(rendering_struct->stencilAttachmentFormat != VK_FORMAT_UNDEFINED)) &&
!pipeline.DepthStencilState() && !pipeline.IsDepthStencilStateDynamic() &&
!IsExtEnabled(extensions.vk_ext_extended_dynamic_state3)) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-renderPass-09033", device, create_info_loc.dot(Field::pDepthStencilState),
"is NULL, but %s is %s and stencilAttachmentFormat is %s.",
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::depthAttachmentFormat).Fields().c_str(),
string_VkFormat(rendering_struct->depthAttachmentFormat),
string_VkFormat(rendering_struct->stencilAttachmentFormat));
}
if (pipeline.fragment_output_state && (rendering_struct->colorAttachmentCount != 0) && !color_blend_state &&
!pipeline.IsColorBlendStateDynamic()) {
for (const auto [i, format] :
vvl::enumerate(rendering_struct->pColorAttachmentFormats, rendering_struct->colorAttachmentCount)) {
if (format != VK_FORMAT_UNDEFINED) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-renderPass-09037", device, create_info_loc.dot(Field::pColorBlendState),
"is NULL, but %s is %" PRIu32 " and pColorAttachmentFormats[%" PRIu32 "] is %s.",
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::colorAttachmentCount).Fields().c_str(),
rendering_struct->colorAttachmentCount, i, string_VkFormat(format));
}
}
}
if (pipeline.fragment_shader_state && pipeline.fragment_output_state) {
const auto input_attachment_index = vku::FindStructInPNextChain<VkRenderingInputAttachmentIndexInfo>(pipeline_ci.pNext);
if (input_attachment_index) {
skip |= ValidateRenderingInputAttachmentIndices(
*input_attachment_index, device, create_info_loc.pNext(Struct::VkRenderingInputAttachmentIndexInfo));
if (input_attachment_index->colorAttachmentCount != rendering_struct->colorAttachmentCount) {
const Location loc =
create_info_loc.pNext(Struct::VkRenderingInputAttachmentIndexInfo, Field::colorAttachmentCount);
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09531", device, loc,
"(%" PRIu32 ") does not match VkPipelineRenderingCreateInfo.colorAttachmentCount (%" PRIu32 ").",
input_attachment_index->colorAttachmentCount, rendering_struct->colorAttachmentCount);
}
}
const auto attachment_location = vku::FindStructInPNextChain<VkRenderingAttachmentLocationInfo>(pipeline_ci.pNext);
if (attachment_location) {
skip |= ValidateRenderingAttachmentLocations(*attachment_location, device,
create_info_loc.pNext(Struct::VkRenderingAttachmentLocationInfo));
if (attachment_location->colorAttachmentCount != rendering_struct->colorAttachmentCount) {
const Location loc =
create_info_loc.pNext(Struct::VkRenderingAttachmentLocationInfo, Field::colorAttachmentCount);
skip |=
LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09532", device, loc,
"(%" PRIu32 ") does not match VkPipelineRenderingCreateInfo.colorAttachmentCount (%" PRIu32 ").",
attachment_location->colorAttachmentCount, rendering_struct->colorAttachmentCount);
}
}
}
}
if (rendering_struct->viewMask != 0) {
skip |= ValidateMultiViewShaders(pipeline, create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask),
rendering_struct->viewMask, true);
}
if (pipeline.OwnsLibState(pipeline.fragment_output_state)) {
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) {
continue;
}
VkFormatFeatureFlags2 format_features = GetPotentialFormatFeatures(color_format);
if ((format_features &
(VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV)) == 0) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06582", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::pColorAttachmentFormats, color_index),
"(%s) doesn't support VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT\n(supported features: %s).",
string_VkFormat(color_format), string_VkFormatFeatureFlags2(format_features).c_str());
}
if (color_blend_state && color_index < color_blend_state->attachmentCount) {
const auto &color_blend_attachment = color_blend_state->pAttachments[color_index];
if (((format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT) == 0) &&
color_blend_attachment.blendEnable) {
skip |= LogError(
"VUID-VkGraphicsPipelineCreateInfo-renderPass-06062", device,
create_info_loc.dot(Field::pColorBlendState).dot(Field::pAttachments, color_index).dot(Field::blendEnable),
"is VK_TRUE.");
}
if (color_format == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 &&
!pipeline.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT)) {
VkColorComponentFlags rgb = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;
if ((color_blend_attachment.colorWriteMask & rgb) != rgb &&
(color_blend_attachment.colorWriteMask & rgb) != 0) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-None-09043", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo,
Field::pColorAttachmentFormats, color_index),
"is VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, but pColorBlendState->pAttachments[%" PRIu32
"].colorWriteMask is %s.",
color_index, string_VkColorComponentFlags(color_blend_attachment.colorWriteMask).c_str());
}
}
}
}
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("VUID-VkGraphicsPipelineCreateInfo-renderPass-06585", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::depthAttachmentFormat),
"(%s) doesn't support VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT\n(supported features: %s).",
string_VkFormat(rendering_struct->depthAttachmentFormat),
string_VkFormatFeatureFlags2(format_features).c_str());
}
}
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("VUID-VkGraphicsPipelineCreateInfo-renderPass-06586", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::stencilAttachmentFormat),
"(%s) doesn't support VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT\n(supported features: %s).",
string_VkFormat(rendering_struct->stencilAttachmentFormat),
string_VkFormatFeatureFlags2(format_features).c_str());
}
}
if ((rendering_struct->depthAttachmentFormat != VK_FORMAT_UNDEFINED) &&
(rendering_struct->stencilAttachmentFormat != VK_FORMAT_UNDEFINED) &&
(rendering_struct->depthAttachmentFormat != rendering_struct->stencilAttachmentFormat)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06589", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::depthAttachmentFormat),
"(%s) is not equal to stencilAttachmentFormat (%s).",
string_VkFormat(rendering_struct->depthAttachmentFormat),
string_VkFormat(rendering_struct->stencilAttachmentFormat));
}
if (color_blend_state && rendering_struct->colorAttachmentCount != color_blend_state->attachmentCount &&
!IsColorBlendStateAttachmentCountIgnore(pipeline)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06055", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::colorAttachmentCount),
"(%" PRIu32 ") is different from %s (%" PRIu32 ").", rendering_struct->colorAttachmentCount,
create_info_loc.dot(Field::pColorBlendState).dot(Field::attachmentCount).Fields().c_str(),
color_blend_state->attachmentCount);
}
}
if (pipeline.IsRenderPassStateRequired()) {
if ((enabled_features.multiview == VK_FALSE) && (rendering_struct->viewMask != 0)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-multiview-06577", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask),
"is 0x%" PRIx32 ", but the multiview feature was not enabled.", rendering_struct->viewMask);
}
if (MostSignificantBit(rendering_struct->viewMask) >= static_cast<int32_t>(phys_dev_props_core11.maxMultiviewViewCount)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06578", device,
create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask),
"is 0x%" PRIx32 ", but maxMultiviewViewCount is %" PRIu32 "", rendering_struct->viewMask,
phys_dev_props_core11.maxMultiviewViewCount);
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineBindPoint(const vvl::CommandBuffer &cb_state, const vvl::Pipeline &pipeline,
const Location &loc) const {
bool skip = false;
auto &cb_sub_state = core::SubState(cb_state);
if (!cb_sub_state.viewport.inherited_depths.empty()) {
bool dyn_viewport =
pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT_WITH_COUNT) || pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT);
bool dyn_scissor = pipeline.IsDynamic(CB_DYNAMIC_STATE_SCISSOR_WITH_COUNT) || pipeline.IsDynamic(CB_DYNAMIC_STATE_SCISSOR);
if (!dyn_viewport || !dyn_scissor) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError("VUID-vkCmdBindPipeline-commandBuffer-04808", objlist, loc,
"Graphics pipeline incompatible with viewport/scissor inheritance.");
}
const auto *discard_rectangle_state =
vku::FindStructInPNextChain<VkPipelineDiscardRectangleStateCreateInfoEXT>(pipeline.GetCreateInfoPNext());
if ((discard_rectangle_state && discard_rectangle_state->discardRectangleCount != 0) ||
(pipeline.IsDynamic(CB_DYNAMIC_STATE_DISCARD_RECTANGLE_ENABLE_EXT))) {
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT)) {
std::stringstream msg;
if (discard_rectangle_state) {
msg << "VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleCount = "
<< discard_rectangle_state->discardRectangleCount;
} else {
msg << "VK_DYNAMIC_STATE_DISCARD_RECTANGLE_ENABLE_EXT";
}
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(
"VUID-vkCmdBindPipeline-commandBuffer-04809", objlist, loc.dot(Field::commandBuffer),
"is a secondary command buffer with VkCommandBufferInheritanceViewportScissorInfoNV::viewportScissor2D "
"enabled, pipelineBindPoint is VK_PIPELINE_BIND_POINT_GRAPHICS and pipeline was created with %s, but without "
"VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT.",
msg.str().c_str());
}
}
}
if (phys_dev_ext_props.provoking_vertex_props.provokingVertexModePerPipeline == VK_FALSE) {
// Render passes only occur in graphics pipelines
const auto &last_bound = cb_state.GetLastBoundGraphics();
const vvl::Pipeline *old_pipeline_state = last_bound.pipeline_state;
if (old_pipeline_state) {
auto old_provoking_vertex_state_ci =
vku::FindStructInPNextChain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>(
old_pipeline_state->RasterizationStatePNext());
auto current_provoking_vertex_state_ci =
vku::FindStructInPNextChain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>(
pipeline.RasterizationStatePNext());
if (old_provoking_vertex_state_ci && !current_provoking_vertex_state_ci) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), old_pipeline_state->Handle());
skip |= LogError("VUID-vkCmdBindPipeline-pipelineBindPoint-04881", objlist, loc,
"Previous %s's provokingVertexMode is %s, but %s doesn't chain "
"VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.",
FormatHandle(old_pipeline_state->Handle()).c_str(),
string_VkProvokingVertexModeEXT(old_provoking_vertex_state_ci->provokingVertexMode),
FormatHandle(pipeline.Handle()).c_str());
} else if (!old_provoking_vertex_state_ci && current_provoking_vertex_state_ci) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), old_pipeline_state->Handle());
skip |= LogError("VUID-vkCmdBindPipeline-pipelineBindPoint-04881", objlist, loc,
"%s's provokingVertexMode is %s, but previous %s doesn't chain "
"VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.",
FormatHandle(pipeline.Handle()).c_str(),
string_VkProvokingVertexModeEXT(current_provoking_vertex_state_ci->provokingVertexMode),
FormatHandle(old_pipeline_state->Handle()).c_str());
} else if (old_provoking_vertex_state_ci && current_provoking_vertex_state_ci &&
old_provoking_vertex_state_ci->provokingVertexMode !=
current_provoking_vertex_state_ci->provokingVertexMode) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), old_pipeline_state->Handle());
skip |= LogError("VUID-vkCmdBindPipeline-pipelineBindPoint-04881", objlist, loc,
"%s's provokingVertexMode is %s, but previous %s's provokingVertexMode is %s.",
FormatHandle(pipeline.Handle()).c_str(),
string_VkProvokingVertexModeEXT(current_provoking_vertex_state_ci->provokingVertexMode),
FormatHandle(old_pipeline_state->Handle()).c_str(),
string_VkProvokingVertexModeEXT(old_provoking_vertex_state_ci->provokingVertexMode));
}
}
}
return skip;
}
// Validate draw-time state related to the PSO
bool CoreChecks::ValidateDrawPipeline(const LastBound &last_bound_state, const vvl::Pipeline &pipeline,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
const vvl::RenderPass *rp_state = cb_state.active_render_pass.get();
if (!rp_state) return skip;
if (rp_state->UsesDynamicRendering()) {
skip |= ValidateDrawPipelineDynamicRenderpass(last_bound_state, pipeline, *rp_state, vuid);
} else {
skip |= ValidateDrawPipelineRenderpass(last_bound_state, pipeline, *rp_state, vuid);
}
skip |= ValidateDrawPipelineFramebuffer(cb_state, pipeline, vuid);
skip |= ValidateDrawPipelineVertexBinding(cb_state, last_bound_state, pipeline, vuid);
skip |= ValidateDrawPipelineFragmentDensityMapLayered(cb_state, pipeline, *rp_state, vuid);
skip |= ValidateDrawPipelineRasterizationState(last_bound_state, pipeline, vuid);
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) && rp_state->UsesDynamicRendering()) {
const auto msrtss_info = vku::FindStructInPNextChain<VkMultisampledRenderToSingleSampledInfoEXT>(
rp_state->dynamic_rendering_begin_rendering_info.pNext);
if (msrtss_info && msrtss_info->multisampledRenderToSingleSampledEnable &&
msrtss_info->rasterizationSamples != pipeline.MultisampleState()->rasterizationSamples) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.rasterization_samples_07935, objlist, vuid.loc(),
"VkMultisampledRenderToSingleSampledInfoEXT::multisampledRenderToSingleSampledEnable is VK_TRUE, but "
"the rasterizationSamples (%" PRIu32 ") is not equal to rasterizationSamples (%" PRIu32
") of the the currently bound pipeline.",
msrtss_info->rasterizationSamples, pipeline.MultisampleState()->rasterizationSamples);
}
}
if ((pipeline.create_info_shaders & (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT |
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_GEOMETRY_BIT)) != 0) {
for (const auto &query : cb_state.active_queries) {
const auto query_pool_state = Get<vvl::QueryPool>(query.pool);
if (query_pool_state && query_pool_state->create_info.queryType == VK_QUERY_TYPE_MESH_PRIMITIVES_GENERATED_EXT) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), query.pool);
skip |= LogError(vuid.mesh_shader_queries_07073, objlist, vuid.loc(),
"Query (slot %" PRIu32 ") with type VK_QUERY_TYPE_MESH_PRIMITIVES_GENERATED_EXT is active.",
query.slot);
}
}
}
return skip;
}
// Verify that PSO creation renderPass is compatible with active (non-dynamic) renderPass
bool CoreChecks::ValidateDrawPipelineRenderpass(const LastBound &last_bound_state, const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state, const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
const auto &pipeline_rp_state = pipeline.RenderPassState();
// TODO: AMD extension codes are included here, but actual function entrypoints are not yet intercepted
if (rp_state.VkHandle() != pipeline_rp_state->VkHandle()) {
// renderPass that PSO was created with must be compatible with active renderPass that PSO is being used with
skip |= ValidateRenderPassCompatibility(cb_state.Handle(), rp_state, pipeline.Handle(), *pipeline_rp_state.get(),
vuid.loc(), vuid.render_pass_compatible_02684);
}
const uint32_t subpass = pipeline.Subpass();
if (subpass != cb_state.GetActiveSubpass()) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), rp_state.Handle());
skip |= LogError(vuid.subpass_index_02685, objlist, vuid.loc(),
"Pipeline was built for subpass %" PRIu32 " but used in subpass %" PRIu32 ".", subpass,
cb_state.GetActiveSubpass());
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpass(const LastBound &last_bound_state, const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state, const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
const auto pipeline_rp_state = pipeline.RenderPassState();
ASSERT_AND_RETURN_SKIP(pipeline_rp_state);
if (pipeline_rp_state->VkHandle() != VK_NULL_HANDLE) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), pipeline_rp_state->Handle());
skip |= LogError(vuid.dynamic_rendering_06198, objlist, vuid.loc(),
"Currently bound pipeline %s must have been created with a "
"VkGraphicsPipelineCreateInfo::renderPass equal to VK_NULL_HANDLE",
FormatHandle(pipeline).c_str());
return skip;
}
const VkRenderingFlags render_flags = rp_state.GetRenderingFlags();
if ((render_flags & VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT) != 0 &&
(render_flags & VK_RENDERING_CONTENTS_INLINE_BIT_KHR) == 0) {
// Being discussed if this is correct or not
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10761
if (!rp_state.use_dynamic_rendering_inherited) {
skip |= LogError(vuid.rendering_contents_10582, cb_state.Handle(), vuid.loc(),
"the render pass is %s::flags %s (missing VK_RENDERING_CONTENTS_INLINE_BIT_KHR)",
rp_state.use_dynamic_rendering_inherited ? "inherited with VkCommandBufferInheritanceRenderingInfo"
: "begun with VkRenderingInfo",
string_VkRenderingFlags(render_flags).c_str());
}
}
const VkPipelineRenderingCreateInfo &pipeline_rendering_ci = *(pipeline_rp_state->dynamic_pipeline_rendering_create_info.ptr());
const auto rendering_view_mask = rp_state.GetDynamicRenderingViewMask();
// There is currently a 06031 VU that catches the viewMask at vkCmdExecuteCommands time, so seems like this is not suppose to be
// validated with inherited render passes
if (rp_state.use_dynamic_rendering && pipeline_rendering_ci.viewMask != rendering_view_mask) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_view_mask_06178, objlist, vuid.loc(),
"Currently bound pipeline %s viewMask (0x%" PRIx32
") must be equal to VkRenderingInfo::viewMask (0x%" PRIx32 ")",
FormatHandle(pipeline).c_str(), pipeline_rendering_ci.viewMask, rendering_view_mask);
}
skip |= ValidateDrawPipelineDynamicRenderpassFragmentFormat(last_bound_state, pipeline, rp_state, pipeline_rendering_ci, vuid);
skip |= ValidateDrawPipelineDynamicRenderpassDepthStencil(last_bound_state, pipeline, rp_state, pipeline_rendering_ci, vuid);
skip |=
ValidateDrawPipelineDynamicRenderpassUnusedAttachments(last_bound_state, pipeline, rp_state, pipeline_rendering_ci, vuid);
skip |= ValidateDrawPipelineDynamicRenderpassLegacyDithering(last_bound_state, pipeline, rp_state, vuid);
skip |= ValidateDrawPipelineDynamicRenderpassSampleCount(last_bound_state, pipeline, rp_state, vuid);
skip |= ValidateDrawPipelineDynamicRenderpassFragmentShadingRate(last_bound_state, pipeline, rp_state, vuid);
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpassDepthStencil(const LastBound &last_bound_state, const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state,
const VkPipelineRenderingCreateInfo &pipeline_rendering_ci,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
if (last_bound_state.IsDepthWriteEnable() && pipeline_rendering_ci.depthAttachmentFormat == VK_FORMAT_UNDEFINED) {
if (rp_state.use_dynamic_rendering && rp_state.dynamic_rendering_begin_rendering_info.pDepthAttachment) {
const VkImageView depth_attachment = rp_state.dynamic_rendering_begin_rendering_info.pDepthAttachment->imageView;
if (depth_attachment != VK_NULL_HANDLE) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), depth_attachment);
skip |= LogError(vuid.depth_attachment_08964, objlist, vuid.loc(),
"VkRenderingInfo::pDepthAttachment is %s, but currently bound graphics pipeline %s was created "
"with VkPipelineRenderingCreateInfo::depthAttachmentFormat equal to VK_FORMAT_UNDEFINED",
FormatHandle(depth_attachment).c_str(), FormatHandle(pipeline).c_str());
}
} else if (rp_state.use_dynamic_rendering_inherited) {
const VkFormat depth_format = rp_state.inheritance_rendering_info.depthAttachmentFormat;
if (depth_format != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.depth_attachment_08964, objlist, vuid.loc(),
"VkCommandBufferInheritanceRenderingInfo::depthAttachmentFormat is %s, but currently bound "
"graphics pipeline %s was created "
"with VkPipelineRenderingCreateInfo::depthAttachmentFormat equal to VK_FORMAT_UNDEFINED",
string_VkFormat(depth_format), FormatHandle(pipeline).c_str());
}
}
}
if (last_bound_state.IsStencilTestEnable() && pipeline_rendering_ci.stencilAttachmentFormat == VK_FORMAT_UNDEFINED) {
if (rp_state.use_dynamic_rendering && rp_state.dynamic_rendering_begin_rendering_info.pStencilAttachment) {
const VkImageView stencil_attachment = rp_state.dynamic_rendering_begin_rendering_info.pStencilAttachment->imageView;
if (stencil_attachment != VK_NULL_HANDLE) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), stencil_attachment);
skip |=
LogError(vuid.stencil_attachment_08965, objlist, vuid.loc(),
"VkRenderingInfo::pStencilAttachment is %s, but currently bound graphics pipeline %s was created with "
"VkPipelineRenderingCreateInfo::stencilAttachmentFormat equal to VK_FORMAT_UNDEFINED",
FormatHandle(stencil_attachment).c_str(), FormatHandle(pipeline).c_str());
}
} else if (rp_state.use_dynamic_rendering_inherited) {
const VkFormat stencil_format = rp_state.inheritance_rendering_info.stencilAttachmentFormat;
if (stencil_format != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.stencil_attachment_08965, objlist, vuid.loc(),
"VkCommandBufferInheritanceRenderingInfo::stencilAttachmentFormat is %s, but currently bound "
"graphics pipeline %s was created "
"with VkPipelineRenderingCreateInfo::stencilAttachmentFormat equal to VK_FORMAT_UNDEFINED",
string_VkFormat(stencil_format), FormatHandle(pipeline).c_str());
}
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpassFragmentFormat(const LastBound &last_bound_state,
const vvl::Pipeline &pipeline, const vvl::RenderPass &rp_state,
const VkPipelineRenderingCreateInfo &pipeline_rendering_ci,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
if ((pipeline.active_shaders & VK_SHADER_STAGE_FRAGMENT_BIT) == 0) {
return skip;
}
const uint32_t color_count = rp_state.use_dynamic_rendering
? rp_state.dynamic_rendering_begin_rendering_info.colorAttachmentCount
: rp_state.inheritance_rendering_info.colorAttachmentCount;
for (uint32_t i = 0; i < color_count; ++i) {
const bool statically_writes_to_color_attachment = pipeline.fragmentShader_writable_output_location_list.find(i) !=
pipeline.fragmentShader_writable_output_location_list.end();
const bool mask_and_write_enabled = last_bound_state.GetColorWriteMask(i) != 0 && last_bound_state.IsColorWriteEnabled(i);
if (!statically_writes_to_color_attachment || !mask_and_write_enabled) {
continue;
}
if (i < pipeline_rendering_ci.colorAttachmentCount &&
pipeline_rendering_ci.pColorAttachmentFormats[i] != VK_FORMAT_UNDEFINED) {
continue;
}
if (rp_state.use_dynamic_rendering) {
const VkImageView color_attachment = rp_state.dynamic_rendering_begin_rendering_info.pColorAttachments[i].imageView;
if (color_attachment != VK_NULL_HANDLE) {
const LogObjectList objlist(last_bound_state.cb_state.Handle(), pipeline.Handle(), color_attachment);
skip |= LogError(vuid.color_attachment_08963, objlist, vuid.loc(),
"VkRenderingInfo::pColorAttachments[%" PRIu32
"] is %s, but currently bound graphics pipeline %s was created with "
"VkPipelineRenderingCreateInfo::pColorAttachmentFormats[%" PRIu32 "] equal to VK_FORMAT_UNDEFINED",
i, FormatHandle(color_attachment).c_str(), FormatHandle(pipeline).c_str(), i);
}
} else {
assert(rp_state.use_dynamic_rendering_inherited);
const VkFormat color_format = rp_state.inheritance_rendering_info.pColorAttachmentFormats[i];
if (color_format != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(last_bound_state.cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.color_attachment_08963, objlist, vuid.loc(),
"VkCommandBufferInheritanceRenderingInfo::pColorAttachmentFormats[%" PRIu32
"] is %s, but currently bound graphics pipeline %s was created with "
"VkPipelineRenderingCreateInfo::pColorAttachmentFormats[%" PRIu32 "] equal to VK_FORMAT_UNDEFINED",
i, string_VkFormat(color_format), FormatHandle(pipeline).c_str(), i);
}
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpassUnusedAttachments(const LastBound &last_bound_state,
const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state,
const VkPipelineRenderingCreateInfo &pipeline_rendering_ci,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
if (!enabled_features.dynamicRenderingUnusedAttachments) {
const auto color_attachment_count = pipeline_rendering_ci.colorAttachmentCount;
const auto rendering_color_attachment_count = cb_state.GetDynamicRenderingColorAttachmentCount();
if (color_attachment_count && (color_attachment_count != rendering_color_attachment_count)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_color_count_06179, objlist, vuid.loc(),
"Currently bound %s was created with VkPipelineRenderingCreateInfo::colorAttachmentCount (%" PRIu32
") which must be equal to %s::colorAttachmentCount (%" PRIu32
").\nThe dynamicRenderingUnusedAttachments feature allows a way to remove this restriction.",
FormatHandle(pipeline).c_str(), pipeline_rendering_ci.colorAttachmentCount,
rp_state.use_dynamic_rendering ? "VkRenderingInfo" : "VkCommandBufferInheritanceRenderingInfo",
rendering_color_attachment_count);
}
}
const VkRenderingInfo &rendering_info = *(rp_state.dynamic_rendering_begin_rendering_info.ptr());
for (uint32_t i = 0; i < rendering_info.colorAttachmentCount; ++i) {
if (enabled_features.dynamicRenderingUnusedAttachments) {
if (rendering_info.pColorAttachments[i].imageView != VK_NULL_HANDLE) {
auto view_state = Get<vvl::ImageView>(rendering_info.pColorAttachments[i].imageView);
ASSERT_AND_CONTINUE(view_state);
if ((pipeline_rendering_ci.colorAttachmentCount > i) && (view_state->create_info.format != VK_FORMAT_UNDEFINED) &&
(pipeline_rendering_ci.pColorAttachmentFormats[i] != VK_FORMAT_UNDEFINED) &&
(view_state->create_info.format != pipeline_rendering_ci.pColorAttachmentFormats[i])) {
// This VU is hard to word, some extra context for future help
// https://gitlab.khronos.org/vulkan/vulkan/-/issues/4379
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_unused_attachments_08911, objlist, vuid.loc(),
"VkRenderingInfo::pColorAttachments[%" PRIu32
"].imageView format (%s) must match the corresponding format in "
"VkPipelineRenderingCreateInfo::pColorAttachmentFormats[%" PRIu32
"] (%s) "
"when both are not VK_FORMAT_UNDEFINED",
i, string_VkFormat(view_state->create_info.format), i,
string_VkFormat(pipeline_rendering_ci.pColorAttachmentFormats[i]));
}
}
} else {
if (rendering_info.pColorAttachments[i].imageView == VK_NULL_HANDLE) {
if ((pipeline_rendering_ci.colorAttachmentCount > i) &&
(pipeline_rendering_ci.pColorAttachmentFormats[i] != VK_FORMAT_UNDEFINED)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_undefined_color_formats_08912, objlist, vuid.loc(),
"VkRenderingInfo::pColorAttachments[%" PRIu32
"].imageView is VK_NULL_HANDLE, but the corresponding format in "
"VkPipelineRenderingCreateInfo::pColorAttachmentFormats[%" PRIu32
"] is %s (but must be VK_FORMAT_UNDEFINED).",
i, i, string_VkFormat(pipeline_rendering_ci.pColorAttachmentFormats[i]));
}
} else {
auto view_state = Get<vvl::ImageView>(rendering_info.pColorAttachments[i].imageView);
ASSERT_AND_CONTINUE(view_state);
if ((pipeline_rendering_ci.colorAttachmentCount > i) &&
view_state->create_info.format != pipeline_rendering_ci.pColorAttachmentFormats[i]) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_color_formats_08910, objlist, vuid.loc(),
"VkRenderingInfo::pColorAttachments[%" PRIu32
"].imageView format (%s) must match the corresponding format in "
"VkPipelineRenderingCreateInfo::pColorAttachmentFormats[%" PRIu32 "] (%s)",
i, string_VkFormat(view_state->create_info.format), i,
string_VkFormat(pipeline_rendering_ci.pColorAttachmentFormats[i]));
}
}
}
}
if (rendering_info.pDepthAttachment) {
if (enabled_features.dynamicRenderingUnusedAttachments) {
if (rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) {
auto view_state = Get<vvl::ImageView>(rendering_info.pDepthAttachment->imageView);
if (view_state && (view_state->create_info.format != VK_FORMAT_UNDEFINED) &&
(pipeline_rendering_ci.depthAttachmentFormat != VK_FORMAT_UNDEFINED) &&
(view_state->create_info.format != pipeline_rendering_ci.depthAttachmentFormat)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_unused_attachments_08915, objlist, vuid.loc(),
"VkRenderingInfo::pDepthAttachment->imageView format (%s) must match the corresponding format "
"in VkPipelineRenderingCreateInfo::depthAttachmentFormat (%s) "
"if both are not VK_FORMAT_UNDEFINED",
string_VkFormat(view_state->create_info.format),
string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat));
}
}
} else {
if (rendering_info.pDepthAttachment->imageView == VK_NULL_HANDLE) {
if (pipeline_rendering_ci.depthAttachmentFormat != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |=
LogError(vuid.dynamic_rendering_undefined_depth_format_08913, objlist, vuid.loc(),
"VkRenderingInfo::pDepthAttachment.imageView is VK_NULL_HANDLE, but the corresponding format in "
"VkPipelineRenderingCreateInfo::depthAttachmentFormat is %s.",
string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat));
}
} else {
auto view_state = Get<vvl::ImageView>(rendering_info.pDepthAttachment->imageView);
if (view_state && view_state->create_info.format != pipeline_rendering_ci.depthAttachmentFormat) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_depth_format_08914, objlist, vuid.loc(),
"VkRenderingInfo::pDepthAttachment->imageView format (%s) must match the corresponding format "
"in VkPipelineRenderingCreateInfo::depthAttachmentFormat (%s)",
string_VkFormat(view_state->create_info.format),
string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat));
}
}
}
} else if (rp_state.use_dynamic_rendering_inherited) {
if (rp_state.inheritance_rendering_info.depthAttachmentFormat != pipeline_rendering_ci.depthAttachmentFormat &&
!enabled_features.dynamicRenderingUnusedAttachments) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_depth_format_08914, objlist, vuid.loc(),
"VkCommandBufferInheritanceRenderingInfo::depthAttachmentFormat (%s) must match the corresponding "
"format in VkPipelineRenderingCreateInfo::depthAttachmentFormat (%s)",
string_VkFormat(rp_state.inheritance_rendering_info.depthAttachmentFormat),
string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat));
}
}
if (rendering_info.pStencilAttachment) {
if (enabled_features.dynamicRenderingUnusedAttachments) {
if (rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) {
auto view_state = Get<vvl::ImageView>(rendering_info.pStencilAttachment->imageView);
if (view_state && (view_state->create_info.format != VK_FORMAT_UNDEFINED) &&
(pipeline_rendering_ci.stencilAttachmentFormat != VK_FORMAT_UNDEFINED) &&
(view_state->create_info.format != pipeline_rendering_ci.stencilAttachmentFormat)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_unused_attachments_08918, objlist, vuid.loc(),
"VkRenderingInfo::pStencilAttachment->imageView format (%s) must match the corresponding "
"format in VkPipelineRenderingCreateInfo::stencilAttachmentFormat (%s) "
"if both are not VK_FORMAT_UNDEFINED",
string_VkFormat(view_state->create_info.format),
string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat));
}
}
} else {
if (rendering_info.pStencilAttachment->imageView == VK_NULL_HANDLE) {
if (pipeline_rendering_ci.stencilAttachmentFormat != VK_FORMAT_UNDEFINED) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_undefined_stencil_format_08916, objlist, vuid.loc(),
"VkRenderingInfo::pStencilAttachment.imageView is VK_NULL_HANDLE, but the "
"corresponding format in "
"VkPipelineRenderingCreateInfo::stencilAttachmentFormat is %s.",
string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat));
}
} else {
auto view_state = Get<vvl::ImageView>(rendering_info.pStencilAttachment->imageView);
if (view_state && view_state->create_info.format != pipeline_rendering_ci.stencilAttachmentFormat) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_stencil_format_08917, objlist, vuid.loc(),
"VkRenderingInfo::pStencilAttachment->imageView format (%s) must match the corresponding "
"format in VkPipelineRenderingCreateInfo::stencilAttachmentFormat (%s)",
string_VkFormat(view_state->create_info.format),
string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat));
}
}
}
} else if (rp_state.use_dynamic_rendering_inherited) {
if (rp_state.inheritance_rendering_info.stencilAttachmentFormat != pipeline_rendering_ci.stencilAttachmentFormat &&
!enabled_features.dynamicRenderingUnusedAttachments) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_stencil_format_08917, objlist, vuid.loc(),
"VkCommandBufferInheritanceRenderingInfo::stencilAttachmentFormat (%s) must match the corresponding "
"format in VkPipelineRenderingCreateInfo::stencilAttachmentFormat (%s)",
string_VkFormat(rp_state.inheritance_rendering_info.stencilAttachmentFormat),
string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat));
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpassFragmentShadingRate(const LastBound &last_bound_state,
const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
auto rendering_fragment_shading_rate_attachment_info =
vku::FindStructInPNextChain<VkRenderingFragmentShadingRateAttachmentInfoKHR>(
rp_state.dynamic_rendering_begin_rendering_info.pNext);
if (rendering_fragment_shading_rate_attachment_info &&
(rendering_fragment_shading_rate_attachment_info->imageView != VK_NULL_HANDLE)) {
if (!(pipeline.create_flags & VK_PIPELINE_CREATE_2_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_fsr_06183, objlist, vuid.loc(),
"Currently bound graphics pipeline %s must have been created with "
"VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR",
FormatHandle(pipeline).c_str());
}
}
auto rendering_fragment_shading_rate_density_map = vku::FindStructInPNextChain<VkRenderingFragmentDensityMapAttachmentInfoEXT>(
rp_state.dynamic_rendering_begin_rendering_info.pNext);
if (rendering_fragment_shading_rate_density_map && (rendering_fragment_shading_rate_density_map->imageView != VK_NULL_HANDLE)) {
if (!(pipeline.create_flags & VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_fdm_06184, objlist, vuid.loc(),
"Currently bound graphics pipeline %s must have been created with "
"VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT",
FormatHandle(pipeline).c_str());
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpassLegacyDithering(const LastBound &last_bound_state,
const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
if (!enabled_features.legacyDithering) {
return skip;
}
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
const VkRenderingFlags render_flags = rp_state.GetRenderingFlags();
const bool has_legacy_dithering_pipeline =
((pipeline.create_flags & VK_PIPELINE_CREATE_2_ENABLE_LEGACY_DITHERING_BIT_EXT) != 0) ||
(pipeline.fragment_output_state && pipeline.fragment_output_state->legacy_dithering_enabled);
const bool has_legacy_dithering_rendering = (render_flags & VK_RENDERING_ENABLE_LEGACY_DITHERING_BIT_EXT) != 0;
if (has_legacy_dithering_pipeline && !has_legacy_dithering_rendering) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_dithering_09643, objlist, vuid.loc(),
"The bound graphics pipeline was created with VK_PIPELINE_CREATE_2_ENABLE_LEGACY_DITHERING_BIT_EXT "
"but the %s::flags was missing "
"VK_RENDERING_ENABLE_LEGACY_DITHERING_BIT_EXT (value used %s).",
rp_state.use_dynamic_rendering_inherited ? "VkCommandBufferInheritanceRenderingInfo"
: "vkCmdBeginRendering::pRenderingInfo",
string_VkRenderingFlags(render_flags).c_str());
} else if (!has_legacy_dithering_pipeline && has_legacy_dithering_rendering) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.dynamic_rendering_dithering_09642, objlist, vuid.loc(),
"%s::flags was set with VK_RENDERING_ENABLE_LEGACY_DITHERING_BIT_EXT, but the bound graphics "
"pipeline was not created with VK_PIPELINE_CREATE_2_ENABLE_LEGACY_DITHERING_BIT_EXT flag (value used %s).",
rp_state.use_dynamic_rendering_inherited ? "VkCommandBufferInheritanceRenderingInfo"
: "vkCmdBeginRendering::pRenderingInfo",
string_VkPipelineCreateFlags2(pipeline.create_flags).c_str());
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineDynamicRenderpassSampleCount(const LastBound &last_bound_state, const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
// VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV
if (auto sample_count_info = vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(pipeline.GetCreateInfoPNext())) {
for (uint32_t i = 0; i < cb_state.active_attachments.size(); ++i) {
const auto &attachment_info = cb_state.active_attachments[i];
const auto *attachment = attachment_info.image_view;
if (!attachment) {
continue;
} else if (attachment_info.IsColor() &&
sample_count_info->pColorAttachmentSamples[attachment_info.type_index] != attachment->samples) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), attachment->Handle(),
attachment->create_info.image);
skip |=
LogError(vuid.dynamic_rendering_color_sample_06185, objlist, vuid.loc(),
"%s was created with an image sample count (%s) which does not match the corresponding "
"VkAttachmentSampleCountInfoAMD::pColorAttachmentSamples[%" PRIu32 "] (%s)",
attachment_info.Describe(cb_state, i).c_str(), string_VkSampleCountFlagBits(attachment->samples),
attachment_info.type_index,
string_VkSampleCountFlagBits(sample_count_info->pColorAttachmentSamples[attachment_info.type_index]));
} else if (attachment_info.IsDepthOrStencil() &&
sample_count_info->depthStencilAttachmentSamples != attachment->samples) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), attachment->Handle(),
attachment->create_info.image);
skip |= LogError(attachment_info.IsDepth() ? vuid.dynamic_rendering_depth_sample_06186
: vuid.dynamic_rendering_stencil_sample_06187,
objlist, vuid.loc(),
"%s was created with an image sample count (%s) which does not match the corresponding "
"VkAttachmentSampleCountInfoAMD::depthStencilAttachmentSamples (%s)",
attachment_info.Describe(cb_state, i).c_str(), string_VkSampleCountFlagBits(attachment->samples),
string_VkSampleCountFlagBits(sample_count_info->depthStencilAttachmentSamples));
}
}
} else if (!enabled_features.multisampledRenderToSingleSampled && !enabled_features.externalFormatResolve &&
!last_bound_state.IsDynamic(CB_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)) {
// this will only be for static pipeline values, dynamic is caught elsewhere for shader object as well
const VkSampleCountFlagBits rasterization_samples = last_bound_state.GetRasterizationSamples();
for (uint32_t i = 0; i < cb_state.active_attachments.size(); ++i) {
const auto &attachment_info = cb_state.active_attachments[i];
const auto *attachment = attachment_info.image_view;
if (attachment && rasterization_samples != attachment->samples &&
(attachment_info.IsColor() || attachment_info.IsDepthOrStencil())) {
const char *err_vuid = attachment_info.IsColor() ? vuid.dynamic_rendering_07285
: attachment_info.IsDepth() ? vuid.dynamic_rendering_07286
: vuid.dynamic_rendering_07287;
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), attachment->Handle(),
attachment->create_info.image);
skip |= LogError(err_vuid, objlist, vuid.loc(),
"%s was created with an image sample count (%s) which does not match the corresponding "
"VkPipelineMultisampleStateCreateInfo::rasterizationSamples (%s)",
attachment_info.Describe(cb_state, i).c_str(), string_VkSampleCountFlagBits(attachment->samples),
string_VkSampleCountFlagBits(rasterization_samples));
}
}
}
return skip;
}
bool CoreChecks::ValidatePipelineVertexDivisors(const vvl::Pipeline &pipeline, const Location &create_info_loc) const {
bool skip = false;
const auto *input_state = pipeline.InputState();
if (!input_state) {
return skip;
}
const auto divisor_state_info = vku::FindStructInPNextChain<VkPipelineVertexInputDivisorStateCreateInfo>(input_state->pNext);
if (!divisor_state_info) {
return skip;
}
const Location vertex_input_loc = create_info_loc.dot(Field::pVertexInputState);
// Can use raw Pipeline state values because not using the stride (which can be dynamic with
// VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE)
const auto &binding_descriptions = pipeline.GraphicsCreateInfo().pVertexInputState->pVertexBindingDescriptions;
const auto &binding_desc_count = pipeline.GraphicsCreateInfo().pVertexInputState->vertexBindingDescriptionCount;
const VkPhysicalDeviceLimits *device_limits = &phys_dev_props.limits;
for (uint32_t j = 0; j < divisor_state_info->vertexBindingDivisorCount; j++) {
const Location divisor_loc =
vertex_input_loc.pNext(Struct::VkVertexInputBindingDivisorDescription, Field::pVertexBindingDivisors, j);
const auto *vibdd = &(divisor_state_info->pVertexBindingDivisors[j]);
if (vibdd->binding >= device_limits->maxVertexInputBindings) {
skip |= LogError("VUID-VkVertexInputBindingDivisorDescription-binding-01869", device, divisor_loc.dot(Field::binding),
"(%" PRIu32 ") exceeds device maxVertexInputBindings (%" PRIu32 ").", vibdd->binding,
device_limits->maxVertexInputBindings);
}
if (vibdd->divisor > phys_dev_props_core14.maxVertexAttribDivisor) {
skip |= LogError("VUID-VkVertexInputBindingDivisorDescription-divisor-01870", device, divisor_loc.dot(Field::divisor),
"(%" PRIu32 ") exceeds device maxVertexAttribDivisor (%" PRIu32 ").", vibdd->divisor,
phys_dev_props_core14.maxVertexAttribDivisor);
}
if ((0 == vibdd->divisor) && !enabled_features.vertexAttributeInstanceRateZeroDivisor) {
skip |=
LogError("VUID-VkVertexInputBindingDivisorDescription-vertexAttributeInstanceRateZeroDivisor-02228", device,
divisor_loc.dot(Field::divisor),
"is (%" PRIu32 ") but vertexAttributeInstanceRateZeroDivisor feature was not enabled.", vibdd->divisor);
}
if ((1 != vibdd->divisor) && !enabled_features.vertexAttributeInstanceRateDivisor) {
skip |= LogError("VUID-VkVertexInputBindingDivisorDescription-vertexAttributeInstanceRateDivisor-02229", device,
divisor_loc.dot(Field::divisor),
"is (%" PRIu32 ") but vertexAttributeInstanceRateDivisor feature was not enabled.", vibdd->divisor);
}
// Find the corresponding binding description and validate input rate setting
bool found_input_rate = false;
for (size_t k = 0; k < binding_desc_count; k++) {
if ((vibdd->binding == binding_descriptions[k].binding) &&
(VK_VERTEX_INPUT_RATE_INSTANCE == binding_descriptions[k].inputRate)) {
found_input_rate = true;
break;
}
}
if (!found_input_rate) { // Description not found, or has incorrect inputRate value
skip |= LogError("VUID-VkVertexInputBindingDivisorDescription-inputRate-01871", device, divisor_loc.dot(Field::binding),
"is %" PRIu32 ", but inputRate is not VK_VERTEX_INPUT_RATE_INSTANCE.", vibdd->binding);
}
}
return skip;
}
bool CoreChecks::ValidatePipelineLibraryFlags(const VkGraphicsPipelineLibraryFlagsEXT lib_flags,
const VkPipelineLibraryCreateInfoKHR &link_info,
const VkPipelineRenderingCreateInfo *rendering_struct, const Location &loc,
int lib_index, const char *vuid) const {
const bool current_pipeline = lib_index == -1;
bool skip = false;
VkGraphicsPipelineLibraryFlagsEXT flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT;
const uint32_t flags_count = GetBitSetCount(lib_flags & (VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT));
if (flags_count == 0 || flags_count > 3) return skip;
// We start iterating at the index after lib_index to avoid duplicating checks, because the caller will iterate the same loop
for (int i = lib_index + 1; i < static_cast<int>(link_info.libraryCount); ++i) {
const auto lib = Get<vvl::Pipeline>(link_info.pLibraries[i]);
if (!lib) continue;
const bool other_flag = (lib->graphics_lib_type & flags) && (lib->graphics_lib_type & ~lib_flags);
if (!other_flag) {
continue;
}
if (current_pipeline) {
if (lib->GraphicsCreateInfo().renderPass != VK_NULL_HANDLE) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderpass-06625", device, loc.dot(Field::renderPass),
"is VK_NULL_HANDLE and includes "
"VkGraphicsPipelineLibraryCreateInfoEXT::flags (%s), but pLibraries[%" PRIu32
"] includes VkGraphicsPipelineLibraryCreateInfoEXT::flags (%s) and "
"render pass is not VK_NULL_HANDLE.",
string_VkGraphicsPipelineLibraryFlagsEXT(lib_flags).c_str(), i,
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str());
}
}
uint32_t view_mask = rendering_struct ? rendering_struct->viewMask : 0;
uint32_t lib_view_mask = lib->rendering_create_info ? lib->rendering_create_info->viewMask : 0;
if (view_mask != lib_view_mask) {
skip |= LogError(vuid, device, loc,
"pLibraries[%" PRIu32 "] is (flags = %s and viewMask = 0x%" PRIx32 "), but pLibraries[%" PRIu32
"] is (flags = %s and viewMask 0x%" PRIx32 ").",
lib_index, string_VkGraphicsPipelineLibraryFlagsEXT(lib_flags).c_str(), view_mask, i,
string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(), lib_view_mask);
}
}
return skip;
}
bool CoreChecks::ValidateGraphicsPipelineDerivatives(PipelineStates &pipeline_states, uint32_t pipe_index,
const Location &loc) const {
bool skip = false;
const auto &pipeline = *pipeline_states[pipe_index].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.create_flags & VK_PIPELINE_CREATE_2_DERIVATIVE_BIT) {
std::shared_ptr<const vvl::Pipeline> base_pipeline;
const auto &pipeline_ci = pipeline.GraphicsCreateInfo();
const VkPipeline base_handle = pipeline_ci.basePipelineHandle;
const int32_t base_index = pipeline_ci.basePipelineIndex;
if (base_index != -1 && base_index < static_cast<int32_t>(pipeline_states.size())) {
if (static_cast<uint32_t>(base_index) >= pipe_index) {
skip |= LogError("VUID-vkCreateGraphicsPipelines-flags-00720", base_handle, loc,
"base pipeline (index %" PRId32
") must occur earlier in array than derivative pipeline (index %" PRIu32 ").",
base_index, pipe_index);
} else {
base_pipeline = pipeline_states[base_index];
}
} else if (base_handle != VK_NULL_HANDLE) {
base_pipeline = Get<vvl::Pipeline>(base_handle);
}
if (base_pipeline && !(base_pipeline->create_flags & VK_PIPELINE_CREATE_2_ALLOW_DERIVATIVES_BIT)) {
skip |= LogError("VUID-vkCreateGraphicsPipelines-flags-00721", base_pipeline->Handle(), loc,
"base pipeline does not allow derivatives.");
}
}
return skip;
}
bool CoreChecks::ValidateMultiViewShaders(const vvl::Pipeline &pipeline, const Location &multiview_loc, uint32_t view_mask,
bool dynamic_rendering) const {
bool skip = false;
const VkShaderStageFlags stages = pipeline.create_info_shaders;
if (!enabled_features.multiviewTessellationShader &&
(stages & (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
const char *vuid = dynamic_rendering ? "VUID-VkGraphicsPipelineCreateInfo-renderPass-06057"
: "VUID-VkGraphicsPipelineCreateInfo-renderPass-06047";
skip |= LogError(vuid, device, multiview_loc,
"is 0x%" PRIx32
" and pStages contains tessellation shaders, but the multiviewTessellationShader feature was not enabled.",
view_mask);
}
if (!enabled_features.multiviewGeometryShader && (stages & VK_SHADER_STAGE_GEOMETRY_BIT)) {
const char *vuid = dynamic_rendering ? "VUID-VkGraphicsPipelineCreateInfo-renderPass-06058"
: "VUID-VkGraphicsPipelineCreateInfo-renderPass-06048";
skip |= LogError(vuid, device, multiview_loc,
"is 0x%" PRIx32
" and pStages contains geometry shader, but the multiviewGeometryShader feature was not enabled.",
view_mask);
}
if (!enabled_features.multiviewMeshShader && (stages & VK_SHADER_STAGE_MESH_BIT_EXT)) {
const char *vuid = dynamic_rendering ? "VUID-VkGraphicsPipelineCreateInfo-renderPass-07720"
: "VUID-VkGraphicsPipelineCreateInfo-renderPass-07064";
skip |= LogError(vuid, device, multiview_loc,
"is 0x%" PRIx32 " and pStages contains mesh shader, but the multiviewMeshShader feature was not enabled.",
view_mask);
}
for (const auto &stage : pipeline.stage_states) {
// Stage may not have SPIR-V data (e.g. due to the use of shader module identifier or in Vulkan SC)
if (!stage.spirv_state) continue;
if (stage.spirv_state->static_data_.has_builtin_layer) {
// Special case for GLSL and Mesh Shading discussed in https://gitlab.khronos.org/vulkan/vulkan/-/issues/4194
const char *vuid = dynamic_rendering ? "VUID-VkGraphicsPipelineCreateInfo-renderPass-06059"
: "VUID-VkGraphicsPipelineCreateInfo-renderPass-06050";
skip |= LogError(vuid, device, multiview_loc, "is 0x%" PRIx32 " but %s stage contains a Layer decorated OpVariable.%s",
view_mask, string_VkShaderStageFlagBits(stage.GetStage()),
stage.GetStage() == VK_SHADER_STAGE_MESH_BIT_EXT
? "(If hitting this with Mesh Shading using GLSL you can explicitly leave out Layer, see "
"https://godbolt.org/z/av9zsxT8G as an example)"
: "");
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineFramebuffer(const vvl::CommandBuffer &cb_state, const vvl::Pipeline &pipeline,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
if (!cb_state.active_framebuffer) return skip;
// Verify attachments for unprotected/protected command buffer.
if (enabled_features.protectedMemory == VK_TRUE) {
for (uint32_t i = 0; i < cb_state.active_attachments.size(); i++) {
const auto *view_state = cb_state.active_attachments[i].image_view;
const auto &subpass = cb_state.active_subpasses[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 Functions could not be protected. See VUID 02711.
if (subpass.usage != VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT && vuid.protected_command_buffer_02712 != kVUIDUndefined) {
skip |= ValidateUnprotectedImage(cb_state, *view_state->image_state, vuid.loc(),
vuid.protected_command_buffer_02712, image_desc.c_str());
}
skip |= ValidateProtectedImage(cb_state, *view_state->image_state, vuid.loc(),
vuid.unprotected_command_buffer_02707, image_desc.c_str());
}
}
}
for (auto &stage_state : pipeline.stage_states) {
const VkShaderStageFlagBits stage = stage_state.GetStage();
if (stage_state.entrypoint && stage_state.entrypoint->written_builtin_layer &&
cb_state.active_framebuffer->create_info.layers == 1) {
if (cb_state.active_render_pass && cb_state.active_render_pass->has_multiview_enabled) {
// If using MultiView, you should already have hit an error that Framebuffer Layer must be 1, but due to things like
// https://gitlab.khronos.org/vulkan/vulkan/-/issues/4194 we should check here and ignore if things are invalid
// already
break;
}
LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogUndefinedValue("Undefined-Value-Layer-Written", objlist, vuid.loc(),
"Shader stage %s writes to Layer (gl_Layer) but the framebuffer was created with "
"VkFramebufferCreateInfo::layer of 1, this write will have an undefined value set to it.",
string_VkShaderStageFlags(stage).c_str());
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineFragmentDensityMapLayered(const vvl::CommandBuffer &cb_state, const vvl::Pipeline &pipeline,
const vvl::RenderPass &rp_state,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
if (!(pipeline.create_flags & VK_PIPELINE_CREATE_2_PER_LAYER_FRAGMENT_DENSITY_BIT_VALVE)) {
return skip;
}
if (const auto *fragment_density_map_layered =
vku::FindStructInPNextChain<VkPipelineFragmentDensityMapLayeredCreateInfoVALVE>(pipeline.GetCreateInfoPNext())) {
if (rp_state.UsesDynamicRendering()) {
if (rp_state.GetRenderingFlags() & VK_RENDERING_PER_LAYER_FRAGMENT_DENSITY_BIT_VALVE) {
if (rp_state.dynamic_rendering_begin_rendering_info.layerCount >
fragment_density_map_layered->maxFragmentDensityMapLayers) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.fdm_layered_10831, objlist, vuid.loc(),
"the vkCmdBeginRendering set layerCount to %" PRIu32
" which is greater than the bound pipeline maxFragmentDensityMapLayers %" PRIu32 ".",
rp_state.dynamic_rendering_begin_rendering_info.layerCount,
fragment_density_map_layered->maxFragmentDensityMapLayers);
}
}
} else if (cb_state.active_framebuffer) {
if (rp_state.create_info.flags & VK_RENDER_PASS_CREATE_PER_LAYER_FRAGMENT_DENSITY_BIT_VALVE) {
if (cb_state.active_framebuffer->create_info.layers > fragment_density_map_layered->maxFragmentDensityMapLayers) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), rp_state.Handle(),
cb_state.active_framebuffer->Handle());
skip |= LogError(vuid.fdm_layered_10831, objlist, vuid.loc(),
"the bound VkFramebuffer was created with %" PRIu32
" layers which is greater than the bound pipeline maxFragmentDensityMapLayers %" PRIu32 ".",
cb_state.active_framebuffer->create_info.layers,
fragment_density_map_layered->maxFragmentDensityMapLayers);
}
}
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineVertexBinding(const vvl::CommandBuffer &cb_state, const LastBound &last_bound,
const vvl::Pipeline &pipeline, const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
// TODO - Seems even if using mesh shaders, the vertex_input_state is non-null (but emmpty)
if (!pipeline.vertex_input_state || ((pipeline.active_shaders & VK_SHADER_STAGE_VERTEX_BIT) == 0)) {
return skip;
}
// Since we need to know if the Vertex shader actually declares/uses the Input Location, if the shader validation was disabled,
// there will no SPIR-V to reflect the information from.
if (disabled[shader_validation]) {
return skip;
}
const bool has_dynamic_descriptions = pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_EXT);
const auto &vertex_bindings =
has_dynamic_descriptions ? cb_state.dynamic_state_value.vertex_bindings : pipeline.vertex_input_state->bindings;
auto print_binding = [has_dynamic_descriptions](const VertexBindingState binding_description) {
std::stringstream ss;
if (has_dynamic_descriptions) {
ss << "the last call to vkCmdSetVertexInputEXT";
} else {
ss << "the last bound pipeline";
}
ss << " has pVertexBindingDescriptions[" << binding_description.index << "].binding (" << binding_description.desc.binding
<< ") (pointing to Locations [";
const char *separator = "";
for (const auto &location : binding_description.locations) {
ss << separator << location.first;
separator = ", ";
}
ss << "])";
return ss.str();
};
const spirv::EntryPoint *vertex_entry_point = last_bound.GetVertexEntryPoint();
// Can be NULL if pipeline binaries are used
if (!vertex_entry_point) {
return skip;
}
vvl::unordered_set<uint32_t> spirv_input_locations;
for (const auto &pair : vertex_entry_point->input_interface_slots) {
spirv_input_locations.emplace(pair.first.Location());
}
// It is ok to have binding descriptions not used, them and find if there is matching buffer tied to it or not
for (const auto &[binding_index, binding_description] : vertex_bindings) {
// If no attribute points to a binding, it is unused
if (binding_description.locations.empty()) {
continue;
}
bool shader_has_location = false;
for (const auto &location : binding_description.locations) {
if (spirv_input_locations.find(location.first) != spirv_input_locations.end()) {
shader_has_location = true;
break;
}
}
if (!shader_has_location) {
// If the Vertex shader doesn't declare the vertex input, there can be invalid/unbound bindings
continue;
}
const auto *vertex_buffer_binding = vvl::Find(cb_state.current_vertex_buffer_binding_info, binding_index);
if (!vertex_buffer_binding || !vertex_buffer_binding->bound) {
// Likely to not get
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.vertex_binding_04007, objlist, vuid.loc(),
"%s which didn't have a buffer bound from any vkCmdBindVertexBuffers call in this command buffer.",
print_binding(binding_description).c_str());
continue;
}
// This means the app actively set the buffer to null
// Going to hit VUID-vkCmdBindVertexBuffers-pBuffers-04001 first anyway
if (vertex_buffer_binding->buffer == VK_NULL_HANDLE) {
if (!enabled_features.nullDescriptor) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(vuid.vertex_binding_null_04008, objlist, vuid.loc(),
"%s which was bound with a buffer of VK_NULL_HANDLE, but nullDescriptor is not enabled.",
print_binding(binding_description).c_str());
}
continue;
}
const auto vertex_buffer_state = Get<vvl::Buffer>(vertex_buffer_binding->buffer);
if (!vertex_buffer_state) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError(
vuid.vertex_binding_04007, objlist, vuid.loc(),
"%s which has an invalid/destroyed buffer bound from a vkCmdBindVertexBuffers call in this command buffer.",
print_binding(binding_description).c_str());
continue;
}
for (const auto &location : binding_description.locations) {
const auto attr_index = location.second.index;
const auto &attr_desc = location.second.desc;
if (pipeline.IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE)) {
const VkDeviceSize attribute_binding_extent = attr_desc.offset + GetVertexInputFormatSize(attr_desc.format);
if (vertex_buffer_binding->stride != 0 && vertex_buffer_binding->stride < attribute_binding_extent) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle());
skip |= LogError("VUID-vkCmdBindVertexBuffers2-pStrides-06209", objlist, vuid.loc(),
"(attribute binding %" PRIu32 ", attribute location %" PRIu32 ") The pStrides value (%" PRIu64
") parameter in the last call to %s is not 0 "
"and less than the extent of the binding for the attribute (%" PRIu64 ").",
attr_desc.binding, attr_desc.location, vertex_buffer_binding->stride, String(vuid.function),
attribute_binding_extent);
}
}
if (!enabled_features.robustBufferAccess && !pipeline.uses_pipeline_vertex_robustness) {
const VkDeviceSize vertex_buffer_offset = vertex_buffer_binding->offset;
// Use 1 as vertex/instance index to use buffer stride as well
const VkDeviceSize attrib_address = vertex_buffer_offset + vertex_buffer_binding->stride + attr_desc.offset;
VkDeviceSize vtx_attrib_req_alignment = GetVertexInputFormatSize(attr_desc.format);
// TODO - There is no real spec language describing these, but also almost no one supports these formats for vertex
// input and this check should probably just removed and do the safe division always. Will need to run against CTS
// before-and-after to make sure.
if (!vkuFormatIsPacked(attr_desc.format) && !vkuFormatIsCompressed(attr_desc.format) &&
!vkuFormatIsSinglePlane_422(attr_desc.format) && !vkuFormatIsMultiplane(attr_desc.format)) {
vtx_attrib_req_alignment = SafeDivision(vtx_attrib_req_alignment, vkuFormatComponentCount(attr_desc.format));
}
if (SafeModulo(attrib_address, vtx_attrib_req_alignment) != 0) {
const LogObjectList objlist(cb_state.Handle(), vertex_buffer_state->Handle(), pipeline.Handle());
skip |= LogError(vuid.vertex_binding_attribute_02721, objlist, vuid.loc(),
"Format %s has an alignment of %" PRIu64 " but the alignment of attribAddress (%" PRIu64
") is not aligned in pVertexAttributeDescriptions[%" PRIu32 "] (binding=%" PRIu32
" location=%" PRIu32 ") where attribAddress = vertex buffer offset (%" PRIu64
") + binding stride (%" PRIu64 ") + attribute offset (%" PRIu32 ").",
string_VkFormat(attr_desc.format), vtx_attrib_req_alignment, attrib_address, attr_index,
attr_desc.binding, attr_desc.location, vertex_buffer_offset, vertex_buffer_binding->stride,
attr_desc.offset);
}
}
}
}
return skip;
}
bool CoreChecks::ValidateDrawPipelineRasterizationState(const LastBound &last_bound_state, const vvl::Pipeline &pipeline,
const vvl::DrawDispatchVuid &vuid) const {
bool skip = false;
const vvl::CommandBuffer &cb_state = last_bound_state.cb_state;
const vvl::RenderPass *rp_state = cb_state.active_render_pass.get();
ASSERT_AND_RETURN_SKIP(rp_state);
// 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 (pipeline.RasterizationDisabled()) return skip;
if (rp_state->UsesDynamicRendering()) {
// TODO: Mirror the below VUs but using dynamic rendering
const auto dynamic_rendering_info = rp_state->dynamic_rendering_begin_rendering_info;
} else {
const bool dynamic_line_raster_mode = pipeline.IsDynamic(CB_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT);
const bool dynamic_line_stipple_enable = pipeline.IsDynamic(CB_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT);
if (dynamic_line_stipple_enable || dynamic_line_raster_mode) {
const auto raster_line_state =
vku::FindStructInPNextChain<VkPipelineRasterizationLineStateCreateInfo>(pipeline.RasterizationStatePNext());
const VkLineRasterizationMode line_rasterization_mode = (dynamic_line_raster_mode)
? cb_state.dynamic_state_value.line_rasterization_mode
: raster_line_state->lineRasterizationMode;
const bool stippled_line_enable = (dynamic_line_stipple_enable) ? cb_state.dynamic_state_value.stippled_line_enable
: raster_line_state->stippledLineEnable;
if (stippled_line_enable) {
if (line_rasterization_mode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR &&
(!enabled_features.stippledRectangularLines)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), rp_state->Handle());
skip |= LogError(vuid.stippled_rectangular_lines_07495, objlist, vuid.loc(),
"lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR (set %s) with "
"stippledLineEnable (set %s) but the stippledRectangularLines feature is not enabled.",
dynamic_line_raster_mode ? "dynamically" : "in pipeline",
dynamic_line_stipple_enable ? "dynamically" : "in pipeline");
}
if (line_rasterization_mode == VK_LINE_RASTERIZATION_MODE_BRESENHAM && (!enabled_features.stippledBresenhamLines)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), rp_state->Handle());
skip |= LogError(vuid.stippled_bresenham_lines_07496, objlist, vuid.loc(),
"lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_BRESENHAM (set %s) with "
"stippledLineEnable (set %s) but the stippledBresenhamLines feature is not enabled.",
dynamic_line_raster_mode ? "dynamically" : "in pipeline",
dynamic_line_stipple_enable ? "dynamically" : "in pipeline");
}
if (line_rasterization_mode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH &&
(!enabled_features.stippledSmoothLines)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), rp_state->Handle());
skip |= LogError(vuid.stippled_smooth_lines_07497, objlist, vuid.loc(),
"lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH (set %s) with "
"stippledLineEnable (set %s) but the stippledSmoothLines feature is not enabled.",
dynamic_line_raster_mode ? "dynamically" : "in pipeline",
dynamic_line_stipple_enable ? "dynamically" : "in pipeline");
}
if (line_rasterization_mode == VK_LINE_RASTERIZATION_MODE_DEFAULT &&
(!enabled_features.stippledRectangularLines || !phys_dev_props.limits.strictLines)) {
const LogObjectList objlist(cb_state.Handle(), pipeline.Handle(), rp_state->Handle());
skip |=
LogError(vuid.stippled_default_strict_07498, objlist, vuid.loc(),
"lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_DEFAULT (set %s) with "
"stippledLineEnable (set %s), the stippledRectangularLines features is %s and strictLines is %s.",
dynamic_line_raster_mode ? "dynamically" : "in pipeline",
dynamic_line_stipple_enable ? "dynamically" : "in pipeline",
enabled_features.stippledRectangularLines ? "enabled" : "not enabled",
string_VkBool32(phys_dev_props.limits.strictLines).c_str());
}
}
}
}
return skip;
}
bool CoreChecks::ValidatePipelineDiscardRectangleStateCreateInfo(
const vvl::Pipeline &pipeline, const VkPipelineDiscardRectangleStateCreateInfoEXT &discard_rectangle_state,
const Location &create_info_loc) const {
bool skip = false;
if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_DISCARD_RECTANGLE_ENABLE_EXT) &&
discard_rectangle_state.discardRectangleCount > phys_dev_ext_props.discard_rectangle_props.maxDiscardRectangles) {
skip |= LogError("VUID-VkPipelineDiscardRectangleStateCreateInfoEXT-discardRectangleCount-00582", device,
create_info_loc.pNext(Struct::VkPipelineDiscardRectangleStateCreateInfoEXT, Field::discardRectangleCount),
"(%" PRIu32 ") is not less than maxDiscardRectangles (%" PRIu32 ").",
discard_rectangle_state.discardRectangleCount,
phys_dev_ext_props.discard_rectangle_props.maxDiscardRectangles);
}
return skip;
}
bool CoreChecks::ValidatePipelineAttachmentSampleCountInfo(const vvl::Pipeline &pipeline,
const VkAttachmentSampleCountInfoAMD &attachment_sample_count_info,
const Location &create_info_loc) const {
bool skip = false;
const uint32_t bits = GetBitSetCount(static_cast<uint32_t>(attachment_sample_count_info.depthStencilAttachmentSamples));
if (pipeline.fragment_output_state && attachment_sample_count_info.depthStencilAttachmentSamples != 0 &&
((attachment_sample_count_info.depthStencilAttachmentSamples & AllVkSampleCountFlagBits) == 0 || bits > 1)) {
skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-depthStencilAttachmentSamples-06593", device,
create_info_loc.pNext(Struct::VkAttachmentSampleCountInfoAMD, Field::depthStencilAttachmentSamples),
"(0x%" PRIx32 ") is invalid.", attachment_sample_count_info.depthStencilAttachmentSamples);
}
return skip;
}