| /* Copyright (c) 2015-2023 The Khronos Group Inc. |
| * Copyright (c) 2015-2023 Valve Corporation |
| * Copyright (c) 2015-2023 LunarG, Inc. |
| * Copyright (C) 2015-2023 Google Inc. |
| * Modifications Copyright (C) 2020-2022 Advanced Micro Devices, Inc. All rights reserved. |
| * 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 <string> |
| #include <vector> |
| |
| #include <vulkan/vk_enum_string_helper.h> |
| #include "generated/chassis.h" |
| #include "core_validation.h" |
| #include "generated/enum_flag_bits.h" |
| |
| bool CoreChecks::PreCallValidateCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t count, |
| const VkGraphicsPipelineCreateInfo *pCreateInfos, |
| const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines, |
| const ErrorObject &error_obj, void *cgpl_state_data) const { |
| bool skip = StateTracker::PreCallValidateCreateGraphicsPipelines(device, pipelineCache, count, pCreateInfos, pAllocator, |
| pPipelines, error_obj, cgpl_state_data); |
| create_graphics_pipeline_api_state *cgpl_state = reinterpret_cast<create_graphics_pipeline_api_state *>(cgpl_state_data); |
| |
| for (uint32_t i = 0; i < count; i++) { |
| const Location create_info_loc = error_obj.location.dot(Field::pCreateInfos, i); |
| skip |= ValidateGraphicsPipeline(*cgpl_state->pipe_state[i].get(), create_info_loc); |
| skip |= ValidatePipelineDerivatives(cgpl_state->pipe_state, i, create_info_loc); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipeline(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| safe_VkSubpassDescription2 *subpass_desc = nullptr; |
| |
| const auto &rp_state = pipeline.RenderPassState(); |
| if (pipeline.IsRenderPassStateRequired()) { |
| if (!rp_state) { |
| const char *vuid = nullptr; |
| if (!IsExtEnabled(device_extensions.vk_khr_dynamic_rendering)) { |
| vuid = "VUID-VkGraphicsPipelineCreateInfo-renderPass-06575"; |
| } else if (!enabled_features.core13.dynamicRendering) { |
| vuid = "VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576"; |
| } |
| if (vuid) { |
| skip |= LogError(vuid, device, create_info_loc, "requires a valid renderPass, but one was not provided"); |
| } |
| } |
| } |
| |
| const auto subpass = pipeline.Subpass(); |
| if (rp_state && !rp_state->UsesDynamicRendering()) { |
| // Ensure the subpass index is valid. If not, then ValidateGraphicsPipelineShaderState |
| // produces nonsense errors that confuse users. Other layers should already |
| // emit errors for renderpass being invalid. |
| subpass_desc = &rp_state->createInfo.pSubpasses[subpass]; |
| if (subpass >= rp_state->createInfo.subpassCount) { |
| skip |= |
| LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06046", rp_state->renderPass(), |
| create_info_loc.dot(Field::subpass), "(%" PRIu32 ") is out of range for this renderpass (0..%" PRIu32 ").", |
| subpass, rp_state->createInfo.subpassCount - 1); |
| subpass_desc = nullptr; |
| } |
| |
| // 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(device_extensions.vk_khr_portability_subset) && !pipeline.IsGraphicsLibrary()) { |
| skip |= ValidateGraphicsPipelinePortability(pipeline, create_info_loc); |
| } |
| } |
| |
| skip |= ValidateGraphicsPipelineLibrary(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelinePreRasterState(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineInputAssemblyState(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineColorBlendState(pipeline, subpass_desc, create_info_loc); |
| skip |= ValidateGraphicsPipelineRasterizationState(pipeline, subpass_desc, create_info_loc); |
| skip |= ValidateGraphicsPipelineMultisampleState(pipeline, subpass_desc, create_info_loc); |
| skip |= ValidateGraphicsPipelineDepthStencilState(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineDynamicState(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineFragmentShadingRateState(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineDynamicRendering(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineShaderState(pipeline, create_info_loc); |
| skip |= ValidateGraphicsPipelineBlendEnable(pipeline, create_info_loc); |
| |
| if (pipeline.pre_raster_state || 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)); |
| } |
| unique_stage_map[stage_ci.stage] = index; |
| index++; |
| } |
| } |
| |
| // Check if a Vertex Input State is used |
| // Need to make sure it has a vertex shader and if a GPL, that it contains a GPL vertex input state |
| // vkspec.html#pipelines-graphics-subsets-vertex-input |
| if ((pipeline.create_info_shaders & VK_SHADER_STAGE_VERTEX_BIT) && |
| (!pipeline.IsGraphicsLibrary() || (pipeline.IsGraphicsLibrary() && pipeline.vertex_input_state))) { |
| if (!pipeline.IsDynamic(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)) { |
| const auto *input_state = pipeline.InputState(); |
| if (!input_state) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-02097", device, |
| create_info_loc.dot(Field::pVertexInputState), "is NULL."); |
| } else { |
| const auto *binding_descriptions = pipeline.BindingDescriptions(); |
| if (binding_descriptions) { |
| skip |= ValidatePipelineVertexDivisors(*input_state, *binding_descriptions, create_info_loc); |
| } |
| } |
| } |
| |
| if (!pipeline.InputAssemblyState()) { |
| if (!IsExtEnabled(device_extensions.vk_ext_extended_dynamic_state3) || |
| !pipeline.IsDynamic(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE) || |
| !pipeline.IsDynamic(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) || |
| !phys_dev_ext_props.extended_dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted) |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-dynamicPrimitiveTopologyUnrestricted-09031", device, |
| create_info_loc.dot(Field::pInputAssemblyState), "is NULL."); |
| } |
| } |
| |
| skip |= ValidateShaderModuleId(pipeline, create_info_loc); |
| skip |= ValidatePipelineCacheControlFlags(pipeline.create_flags, create_info_loc.dot(Field::flags), |
| "VUID-VkGraphicsPipelineCreateInfo-pipelineCreationCacheControl-02878"); |
| skip |= ValidatePipelineProtectedAccessFlags(pipeline.create_flags, create_info_loc.dot(Field::flags)); |
| |
| const auto *discard_rectangle_state = vku::FindStructInPNextChain<VkPipelineDiscardRectangleStateCreateInfoEXT>(pipeline.PNext()); |
| if (discard_rectangle_state) { |
| if (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); |
| } |
| } |
| |
| // VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV |
| const auto attachment_sample_count_info = vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(pipeline.PNext()); |
| if (attachment_sample_count_info) { |
| const uint32_t bits = GetBitSetCount(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); |
| } |
| } |
| |
| if (const auto *pipeline_robustness_info = vku::FindStructInPNextChain<VkPipelineRobustnessCreateInfoEXT>(pipeline.PNext()); |
| pipeline_robustness_info) { |
| skip |= ValidatePipelineRobustnessCreateInfo(pipeline, *pipeline_robustness_info, create_info_loc); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelinePortability(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| if (!enabled_features.portability_subset_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."); |
| } |
| |
| // Validate vertex inputs |
| for (const auto &desc : pipeline.vertex_input_state->binding_descriptions) { |
| const uint32_t min_alignment = phys_dev_ext_props.portability_props.minVertexInputBindingStrideAlignment; |
| if ((desc.stride < min_alignment) || (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.portability_subset_features.vertexAttributeAccessBeyondStride) { |
| for (const auto &attrib : pipeline.vertex_input_state->vertex_attribute_descriptions) { |
| const auto vertex_binding_map_it = pipeline.vertex_input_state->binding_to_index_map.find(attrib.binding); |
| if (vertex_binding_map_it != pipeline.vertex_input_state->binding_to_index_map.cend()) { |
| const auto &desc = pipeline.vertex_input_state->binding_descriptions[vertex_binding_map_it->second]; |
| if ((attrib.offset + vkuFormatElementSize(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, vkuFormatElementSize(attrib.format), desc.stride); |
| } |
| } |
| } |
| } |
| |
| auto raster_state_ci = pipeline.RasterizationState(); |
| if (raster_state_ci) { |
| // Validate polygon mode |
| if (!enabled_features.portability_subset_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.portability_subset_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<RENDER_PASS_STATE>(pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().renderPass); |
| const bool ignore_color_blend_state = |
| raster_state_ci->rasterizerDiscardEnable || |
| (pipeline.rendering_create_info ? (pipeline.rendering_create_info->colorAttachmentCount == 0) |
| : (render_pass->createInfo.pSubpasses[subpass].colorAttachmentCount == 0)); |
| const auto *color_blend_state = pipeline.ColorBlendState(); |
| if (!enabled_features.portability_subset_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::ValidateGraphicsPipelineLibrary(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| |
| // It is possible to have no FS state in a complete pipeline whether or not GPL is used |
| if (pipeline.pre_raster_state && !pipeline.fragment_shader_state && |
| ((pipeline.create_info_shaders & FragmentShaderState::ValidShaderStages()) != 0)) { |
| 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.", |
| string_VkShaderStageFlags(pipeline.create_info_shaders).c_str()); |
| } |
| |
| if (!IsExtEnabled(device_extensions.vk_ext_graphics_pipeline_library)) { |
| if (!pipeline.PipelineLayoutState()) { |
| skip |= |
| LogError("VUID-VkGraphicsPipelineCreateInfo-None-07826", device, create_info_loc.dot(Field::layout), |
| "is not a valid pipeline layout, but %s is not enabled.", VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| } else if (!pipeline.RenderPassState()) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06603", device, create_info_loc.dot(Field::renderPass), |
| "is not a valid render pass, but %s is not enabled.", VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| } |
| } else { |
| const VkPipelineCreateFlags pipeline_flags = pipeline.create_flags; |
| const bool is_library = (pipeline_flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) != 0; |
| |
| if (is_library && !enabled_features.graphics_pipeline_library_features.graphicsPipelineLibrary) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-graphicsPipelineLibrary-06606", device, |
| create_info_loc.dot(Field::flags), |
| "(%s) includes VK_PIPELINE_CREATE_LIBRARY_BIT_KHR, but " |
| "graphicsPipelineLibrary feature is not enabled.", |
| string_VkPipelineCreateFlags(pipeline_flags).c_str()); |
| } |
| |
| if (pipeline.fragment_shader_state && !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()); |
| } |
| |
| if (pipeline.HasFullState()) { |
| if (is_library) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06608", device, create_info_loc.dot(Field::flags), |
| "(%s) includes VK_PIPELINE_CREATE_LIBRARY_BIT_KHR, but defines a complete set of state.", |
| string_VkPipelineCreateFlags(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 |
| auto &pipe_ci = pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>(); |
| if (!pipe_ci.layout) { |
| 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; |
| // NOTE: it is possible for an executable pipeline to not contain FS state |
| const auto fs_layout_flags = (pipeline.fragment_shader_state) |
| ? pipeline.fragment_shader_state->PipelineLayoutCreateFlags() |
| : static_cast<VkPipelineLayoutCreateFlags>(0); |
| const bool no_independent_sets = ((pipeline.pre_raster_state->PipelineLayoutCreateFlags() & fs_layout_flags) & |
| VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT) == 0; |
| if (from_libraries_only && no_independent_sets) { |
| // The layout defined at link time must be compatible with each (pre-raster and fragment shader) sub state's layout |
| // (vertex input and fragment output state do not contain a layout) |
| const auto layout_state = Get<PIPELINE_LAYOUT_STATE>(pipe_ci.layout); |
| if (layout_state) { |
| if (std::string err_msg; |
| !VerifySetLayoutCompatibility(*layout_state, *pipeline.PreRasterPipelineLayoutState(), err_msg)) { |
| LogObjectList objs(device); |
| objs.add(layout_state->Handle()); |
| objs.add(pipeline.PreRasterPipelineLayoutState()->Handle()); |
| skip |= |
| LogError("VUID-VkGraphicsPipelineCreateInfo-layout-07827", objs, create_info_loc.dot(Field::layout), |
| "is incompatible with the layout specified in the pre-raster sub-state: %s", err_msg.c_str()); |
| } |
| if (std::string err_msg; |
| !VerifySetLayoutCompatibility(*layout_state, *pipeline.FragmentShaderPipelineLayoutState(), err_msg)) { |
| LogObjectList objs(device); |
| objs.add(layout_state->Handle()); |
| objs.add(pipeline.FragmentShaderPipelineLayoutState()->Handle()); |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-layout-07827", objs, create_info_loc.dot(Field::layout), |
| "is incompatible with the layout specified in the fragment shader sub-state: %s", |
| err_msg.c_str()); |
| } |
| } |
| } |
| } |
| |
| enum GPLInitInfo : uint8_t { |
| uninitialized = 0, |
| from_gpl_info, |
| from_link_info, |
| }; |
| struct GPLValidInfo { |
| GPLValidInfo() = default; |
| GPLValidInfo(GPLInitInfo ii, const PIPELINE_LAYOUT_STATE *pls) : init_type(ii), layout_state(pls) {} |
| GPLInitInfo init_type = GPLInitInfo::uninitialized; |
| const PIPELINE_LAYOUT_STATE *layout_state = nullptr; |
| }; |
| std::pair<VkPipelineLayoutCreateFlags, GPLValidInfo> pre_raster_flags = std::make_pair( |
| VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM, GPLValidInfo{}), |
| fs_flags = std::make_pair(VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM, |
| GPLValidInfo{}); |
| const auto gpl_info = vku::FindStructInPNextChain<VkGraphicsPipelineLibraryCreateInfoEXT>(pipeline.PNext()); |
| const Location &gpl_flags_loc = create_info_loc.pNext(Struct::VkGraphicsPipelineLibraryCreateInfoEXT, Field::flags); |
| if (gpl_info) { |
| if ((gpl_info->flags & (VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT | |
| VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)) != 0) { |
| // NOTE: 06642 only refers to the create flags, not the sub-state, so look at the "raw layout" rather than the |
| // layout |
| // associated with the sub-state |
| const auto layout_state = Get<PIPELINE_LAYOUT_STATE>(pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().layout); |
| if (!layout_state) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06642", device, create_info_loc, |
| "is a graphics library created with %s state, but does not have a valid layout specified.", |
| string_VkGraphicsPipelineLibraryFlagsEXT(gpl_info->flags).c_str()); |
| } |
| } |
| |
| if ((gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) && |
| !(gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)) { |
| pre_raster_flags.first = |
| (pipeline.PreRasterPipelineLayoutState()) ? pipeline.PreRasterPipelineLayoutState()->CreateFlags() : 0; |
| pre_raster_flags.second = {GPLInitInfo::from_gpl_info, pipeline.PreRasterPipelineLayoutState().get()}; |
| } else if ((gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) && |
| !(gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT)) { |
| fs_flags.first = (pipeline.FragmentShaderPipelineLayoutState()) |
| ? pipeline.FragmentShaderPipelineLayoutState()->CreateFlags() |
| : 0; |
| fs_flags.second = {GPLInitInfo::from_gpl_info, pipeline.FragmentShaderPipelineLayoutState().get()}; |
| } |
| } |
| |
| if (pipeline.library_create_info) { |
| const bool has_link_time_opt = (pipeline_flags & VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT) != 0; |
| const bool has_retain_link_time_opt = |
| (pipeline_flags & VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT) != 0; |
| const bool has_capture_internal = (pipeline_flags & VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR) != 0; |
| bool uses_descriptor_buffer = false; |
| bool lib_all_has_capture_internal = false; |
| |
| for (uint32_t i = 0; i < pipeline.library_create_info->libraryCount; ++i) { |
| const auto lib = Get<PIPELINE_STATE>(pipeline.library_create_info->pLibraries[i]); |
| if (!lib) { |
| continue; |
| } |
| const Location &library_loc = create_info_loc.pNext(Struct::VkPipelineLibraryCreateInfoKHR, Field::pLibraries, i); |
| const VkPipelineCreateFlags lib_pipeline_flags = lib->create_flags; |
| if (lib->PipelineLayoutState()) { |
| if (lib->graphics_lib_type == VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) { |
| pre_raster_flags.first = lib->PreRasterPipelineLayoutState()->CreateFlags(); |
| pre_raster_flags.second = {GPLInitInfo::from_link_info, lib->PreRasterPipelineLayoutState().get()}; |
| } else if (lib->graphics_lib_type == VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) { |
| fs_flags.first = lib->FragmentShaderPipelineLayoutState()->CreateFlags(); |
| fs_flags.second = {GPLInitInfo::from_link_info, lib->FragmentShaderPipelineLayoutState().get()}; |
| } |
| } |
| |
| const bool lib_has_retain_link_time_opt = |
| (lib_pipeline_flags & VK_PIPELINE_CREATE_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, %s is %s.", |
| string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(), |
| string_VkPipelineCreateFlags(lib_pipeline_flags).c_str(), |
| create_info_loc.dot(Field::flags).Fields().c_str(), |
| string_VkPipelineCreateFlags(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, %s is %s.", |
| string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(), |
| string_VkPipelineCreateFlags(lib_pipeline_flags).c_str(), |
| create_info_loc.dot(Field::flags).Fields().c_str(), |
| string_VkPipelineCreateFlags(pipeline_flags).c_str()); |
| } |
| |
| const bool lib_has_capture_internal = |
| (lib_pipeline_flags & VK_PIPELINE_CREATE_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 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_VkPipelineCreateFlags(lib_pipeline_flags).c_str(), gpl_flags_loc.Fields().c_str(), |
| string_VkPipelineCreateFlags(gpl_info->flags).c_str(), |
| create_info_loc.dot(Field::flags).Fields().c_str(), |
| string_VkPipelineCreateFlags(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_VkPipelineCreateFlags(lib_pipeline_flags).c_str()); |
| } else if (has_capture_internal && non_zero_gpl) { |
| 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_VkPipelineCreateFlags(lib_pipeline_flags).c_str(), gpl_flags_loc.Fields().c_str(), |
| string_VkPipelineCreateFlags(gpl_info->flags).c_str(), |
| create_info_loc.dot(Field::flags).Fields().c_str(), |
| string_VkPipelineCreateFlags(pipeline_flags).c_str()); |
| } |
| } |
| |
| if ((lib->uses_shader_module_id) && !(pipeline_flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT)) { |
| const LogObjectList objlist(device); |
| skip |= LogError( |
| "VUID-VkPipelineLibraryCreateInfoKHR-pLibraries-06855", objlist, 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_VkPipelineCreateFlags(lib_pipeline_flags).c_str()); |
| } |
| struct check_struct { |
| VkPipelineCreateFlagBits bit; |
| std::string first_vuid; |
| std::string second_vuid; |
| }; |
| static const std::array<check_struct, 2> check_infos = { |
| {{VK_PIPELINE_CREATE_NO_PROTECTED_ACCESS_BIT_EXT, "VUID-VkPipelineLibraryCreateInfoKHR-pipeline-07404", |
| "VUID-VkPipelineLibraryCreateInfoKHR-pipeline-07405"}, |
| {VK_PIPELINE_CREATE_PROTECTED_ACCESS_ONLY_BIT_EXT, "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_VkPipelineCreateFlags(lib_pipeline_flags).c_str(), |
| string_VkPipelineCreateFlagBits(check_info.bit), |
| create_info_loc.dot(Field::flags).Fields().c_str(), |
| string_VkPipelineCreateFlags(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_VkPipelineCreateFlags(lib_pipeline_flags).c_str(), |
| string_VkPipelineCreateFlagBits(check_info.bit), |
| create_info_loc.dot(Field::flags).Fields().c_str(), |
| string_VkPipelineCreateFlags(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 opopposite of pLibraries[0].", |
| lib->descriptor_buffer_mode ? "was" : "was not"); |
| break; // no point keep checking as might have many of same error |
| } |
| } |
| } |
| |
| if (pipeline.library_create_info && pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().renderPass == VK_NULL_HANDLE) { |
| const auto rendering_struct = vku::FindStructInPNextChain<VkPipelineRenderingCreateInfo>(pipeline.PNext()); |
| if (gpl_info) { |
| skip |= ValidatePipelineLibraryFlags(gpl_info->flags, *pipeline.library_create_info, rendering_struct, |
| 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 < pipeline.library_create_info->libraryCount; ++i) { |
| const auto lib = Get<PIPELINE_STATE>(pipeline.library_create_info->pLibraries[i]); |
| const auto lib_gpl_info = vku::FindStructInPNextChain<VkGraphicsPipelineLibraryCreateInfoEXT>(lib->PNext()); |
| 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 < pipeline.library_create_info->libraryCount; ++i) { |
| const auto lib = Get<PIPELINE_STATE>(pipeline.library_create_info->pLibraries[i]); |
| const auto lib_rendering_struct = lib->GetPipelineRenderingCreateInfo(); |
| skip |= ValidatePipelineLibraryFlags(lib->graphics_lib_type, *pipeline.library_create_info, lib_rendering_struct, |
| create_info_loc, i, "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06627"); |
| } |
| } |
| |
| if ((pre_raster_flags.second.init_type != GPLInitInfo::uninitialized) && |
| (fs_flags.second.init_type != GPLInitInfo::uninitialized)) { |
| const char *vuid = nullptr; |
| const bool only_libs = (pre_raster_flags.second.init_type == GPLInitInfo::from_link_info) && |
| (fs_flags.second.init_type == GPLInitInfo::from_link_info); |
| if (pre_raster_flags.second.init_type != fs_flags.second.init_type) { |
| vuid = "VUID-VkGraphicsPipelineCreateInfo-flags-06614"; |
| } else if (only_libs) { |
| vuid = "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06615"; |
| } |
| |
| // vuid != null => pre-raster and fragemnt shader state is defined by some combination of this library and pLibraries |
| if (vuid) { |
| // Check for consistent independent sets across libraries |
| const auto pre_raster_indset = (pre_raster_flags.first & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| const auto fs_indset = (fs_flags.first & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| if (pre_raster_indset ^ fs_indset) { |
| const char *pre_raster_str = (pre_raster_indset != 0) ? "defined with" : "not defined with"; |
| const char *fs_str = (fs_indset != 0) ? "defined with" : "not defined with"; |
| skip |= LogError( |
| vuid, device, 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 VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT, and the " |
| "fragment shader layout create flags (%s) are %s VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT", |
| string_VkPipelineLayoutCreateFlags(pre_raster_flags.first).c_str(), pre_raster_str, |
| string_VkPipelineLayoutCreateFlags(fs_flags.first).c_str(), fs_str); |
| } |
| |
| // Check for consistent shader bindings + layout across libraries |
| const char *const vuid_only_libs_binding = |
| (only_libs) ? "VUID-VkGraphicsPipelineCreateInfo-pLibraries-06758" : nullptr; |
| const auto &pre_raster_set_layouts = pre_raster_flags.second.layout_state->set_layouts; |
| const auto &fs_set_layouts = fs_flags.second.layout_state->set_layouts; |
| const auto num_set_layouts = std::max(pre_raster_set_layouts.size(), fs_set_layouts.size()); |
| for (size_t i = 0; i < num_set_layouts; ++i) { |
| const auto pre_raster_dsl = (i < pre_raster_set_layouts.size()) |
| ? pre_raster_set_layouts[i] |
| : vvl::base_type<decltype(pre_raster_set_layouts)>::value_type{}; |
| const auto fs_dsl = |
| (i < fs_set_layouts.size()) ? fs_set_layouts[i] : vvl::base_type<decltype(fs_set_layouts)>::value_type{}; |
| const char *vuid_tmp = nullptr; |
| std::ostringstream msg; |
| 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())) { |
| const auto pre_raster_layout_handle_str = |
| FormatHandle(pre_raster_flags.second.layout_state->Handle()); |
| const auto fs_layout_handle_str = FormatHandle(fs_flags.second.layout_state->Handle()); |
| if (pre_raster_flags.second.init_type == GPLInitInfo::from_gpl_info) { |
| vuid_tmp = "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 (fs_flags.second.init_type == GPLInitInfo::from_gpl_info) { |
| vuid_tmp = "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_tmp = vuid_only_libs_binding; |
| 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."; |
| } |
| 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())) { |
| const auto pre_raster_layout_handle_str = |
| FormatHandle(pre_raster_flags.second.layout_state->Handle()); |
| const auto fs_layout_handle_str = FormatHandle(fs_flags.second.layout_state->Handle()); |
| if (fs_flags.second.init_type == GPLInitInfo::from_gpl_info) { |
| vuid_tmp = "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."; |
| break; |
| } else if (pre_raster_flags.second.init_type == GPLInitInfo::from_gpl_info) { |
| vuid_tmp = "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 << "."; |
| break; |
| } else { |
| vuid_tmp = vuid_only_libs_binding; |
| 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."; |
| break; |
| } |
| } |
| } |
| } |
| if (vuid_tmp) { |
| skip |= LogError(vuid_tmp, device, create_info_loc, "%s", msg.str().c_str()); |
| } |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineBlendEnable(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| const auto &rp_state = pipeline.RenderPassState(); |
| if (!rp_state) { |
| return false; |
| } |
| const Location color_loc = create_info_loc.dot(Field::pColorBlendState); |
| |
| if (!rp_state->UsesDynamicRendering()) { |
| const auto subpass = pipeline.Subpass(); |
| const auto *subpass_desc = &rp_state->createInfo.pSubpasses[subpass]; |
| |
| for (uint32_t i = 0; i < pipeline.Attachments().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->createInfo.pAttachments[attachment]; |
| VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(attachment_desc.format); |
| |
| const auto *raster_state = pipeline.RasterizationState(); |
| if (raster_state && !raster_state->rasterizerDiscardEnable && pipeline.Attachments()[i].blendEnable && |
| !(format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR)) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06041", device, |
| color_loc.dot(Field::pAttachments, i).dot(Field::blendEnable), |
| "is VK_TRUE but format %s of the corresponding attachment description (subpass %" PRIu32 |
| ", attachment %" PRIu32 ") does not support VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT.", |
| string_VkFormat(attachment_desc.format), subpass, attachment); |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineInputAssemblyState(const PIPELINE_STATE &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 ((ia_state->primitiveRestartEnable == VK_TRUE) && |
| 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, VK_PRIMITIVE_TOPOLOGY_PATCH_LIST})) { |
| if (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { |
| if (!enabled_features.primitive_topology_list_restart_features.primitiveTopologyPatchListRestart) { |
| skip |= LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06253", device, ia_loc, |
| "topology is %s and primitiveRestartEnable is VK_TRUE and the " |
| "primitiveTopologyPatchListRestart feature was not enabled.", |
| string_VkPrimitiveTopology(topology)); |
| } |
| } else if (!enabled_features.primitive_topology_list_restart_features.primitiveTopologyListRestart) { |
| skip |= |
| LogError("VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06252", device, ia_loc, |
| "topology is %s and primitiveRestartEnable is VK_TRUE and the primitiveTopologyListRestart feature " |
| "was not enabled.", |
| string_VkPrimitiveTopology(topology)); |
| } |
| } |
| if ((enabled_features.core.geometryShader == VK_FALSE) && |
| 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, |
| "topology is %s and geometryShader feature was not enabled.", |
| string_VkPrimitiveTopology(topology)); |
| } |
| if ((enabled_features.core.tessellationShader == VK_FALSE) && (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)) { |
| skip |= LogError( "VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00430", device, ia_loc, |
| "topology is %s and tessellationShader feature was not enabled.", |
| string_VkPrimitiveTopology(topology)); |
| } |
| } |
| |
| const bool ignore_topology = pipeline.IsDynamic(VK_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::ValidateGraphicsPipelinePreRasterState(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| // Only validate once during creation |
| if (!pipeline.OwnsSubState(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.core.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.core.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.mesh_shader_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.mesh_shader_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::ValidateGraphicsPipelineColorBlendState(const PIPELINE_STATE &pipeline, |
| const 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(); |
| if (((color_blend_state->flags & VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM) != 0) && |
| (!rp_state || rp_state->renderPass() == VK_NULL_HANDLE)) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06482", device, color_loc.dot(Field::flags), |
| "includes " |
| "VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM, but " |
| "renderpass is not VK_NULL_HANDLE."); |
| } |
| |
| // VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV |
| const auto *attachment_sample_count_info = vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(pipeline.PNext()); |
| const auto *rendering_struct = vku::FindStructInPNextChain<VkPipelineRenderingCreateInfo>(pipeline.PNext()); |
| if (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 (subpass_desc && color_blend_state->attachmentCount != subpass_desc->colorAttachmentCount) { |
| 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->renderPass()).c_str(), pipeline.Subpass(), |
| subpass_desc->colorAttachmentCount); |
| } |
| const auto &pipe_attachments = pipeline.Attachments(); |
| if (!enabled_features.core.independentBlend) { |
| if (pipe_attachments.size() > 1) { |
| const auto *const attachments = &pipe_attachments[0]; |
| for (size_t i = 1; i < pipe_attachments.size(); i++) { |
| // Quoting the spec: "If [the independent blend] feature is not enabled, the VkPipelineColorBlendAttachmentState |
| // settings for all color attachments must be identical." VkPipelineColorBlendAttachmentState contains |
| // only attachment state, so memcmp is best suited for the comparison |
| if (memcmp(static_cast<const void *>(attachments), static_cast<const void *>(&attachments[i]), |
| sizeof(attachments[0]))) { |
| skip |= LogError("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; |
| } |
| } |
| } |
| } |
| if (!enabled_features.core.logicOp && (color_blend_state->logicOpEnable != VK_FALSE)) { |
| skip |= LogError("VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00606", device, |
| color_loc.dot(Field::logicOpEnable), "is VK_TRUE, but the logicOp feature was not enabled."); |
| } |
| for (size_t i = 0; i < pipe_attachments.size(); i++) { |
| const Location &attachment_loc = color_loc.dot(Field::pAttachments, (uint32_t)i); |
| if (IsSecondaryColorInputBlendFactor(pipe_attachments[i].srcColorBlendFactor)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| 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(pipe_attachments[i].srcColorBlendFactor)); |
| } |
| } |
| if (IsSecondaryColorInputBlendFactor(pipe_attachments[i].dstColorBlendFactor)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| 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(pipe_attachments[i].dstColorBlendFactor)); |
| } |
| } |
| if (IsSecondaryColorInputBlendFactor(pipe_attachments[i].srcAlphaBlendFactor)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| 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(pipe_attachments[i].srcAlphaBlendFactor)); |
| } |
| } |
| if (IsSecondaryColorInputBlendFactor(pipe_attachments[i].dstAlphaBlendFactor)) { |
| if (!enabled_features.core.dualSrcBlend) { |
| 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(pipe_attachments[i].dstAlphaBlendFactor)); |
| } |
| } |
| } |
| auto color_write = vku::FindStructInPNextChain<VkPipelineColorWriteCreateInfoEXT>(color_blend_state->pNext); |
| if (color_write) { |
| 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.color_write_features.colorWriteEnable) { |
| 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."); |
| } |
| } |
| } |
| } |
| const auto *color_blend_advanced = vku::FindStructInPNextChain<VkPipelineColorBlendAdvancedStateCreateInfoEXT>(color_blend_state->pNext); |
| if (color_blend_advanced) { |
| if (!phys_dev_ext_props.blend_operation_advanced_props.advancedBlendCorrelatedOverlap && |
| color_blend_advanced->blendOverlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT) { |
| skip |= LogError("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 (!phys_dev_ext_props.blend_operation_advanced_props.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 (!phys_dev_ext_props.blend_operation_advanced_props.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::ValidateGraphicsPipelineRasterizationState(const PIPELINE_STATE &pipeline, |
| const safe_VkSubpassDescription2 *subpass_desc, |
| const Location &create_info_loc) const { |
| bool skip = false; |
| const Location raster_loc = create_info_loc.dot(Field::pRasterizationState); |
| const auto raster_state = pipeline.RasterizationState(); |
| if (!raster_state) { |
| return skip; |
| } |
| if ((raster_state->depthClampEnable == VK_TRUE) && (!enabled_features.core.depthClamp)) { |
| 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(VK_DYNAMIC_STATE_DEPTH_BIAS) && (raster_state->depthBiasClamp != 0.0) && |
| (!enabled_features.core.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.core.alphaToOne)) { |
| 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 subpass uses a depth/stencil attachment, pDepthStencilState must be a pointer to a valid structure |
| const bool has_ds_state = (!pipeline.IsGraphicsLibrary() && pipeline.HasFullState()) || |
| ((pipeline.graphics_lib_type & (VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT)) != 0); |
| if (has_ds_state) { |
| if (subpass_desc && subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const Location ds_loc = create_info_loc.dot(Field::pDepthStencilState); |
| const auto ds_state = pipeline.DepthStencilState(); |
| if (!ds_state) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-alphaToCoverageEnable-08891", device, ds_loc, |
| "is NULL when rasterization is enabled " |
| "and subpass uses a depth/stencil attachment."); |
| } else if (ds_state->depthBoundsTestEnable == VK_TRUE) { |
| if (!enabled_features.core.depthBounds) { |
| skip |= LogError("VUID-VkPipelineDepthStencilStateCreateInfo-depthBoundsTestEnable-00598", device, |
| ds_loc.dot(Field::depthBoundsTestEnable), |
| "depthBoundsTestEnable is VK_TRUE, but depthBounds feature was not enabled."); |
| } |
| |
| if (!IsExtEnabled(device_extensions.vk_ext_depth_range_unrestricted) && |
| !pipeline.IsDynamic(VK_DYNAMIC_STATE_DEPTH_BOUNDS)) { |
| const float minDepthBounds = ds_state->minDepthBounds; |
| const float maxDepthBounds = ds_state->maxDepthBounds; |
| 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 subpass uses color attachments, pColorBlendState must be valid pointer |
| const bool has_color_blend_state = |
| !pipeline.IsGraphicsLibrary() || |
| ((pipeline.graphics_lib_type & (VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT)) != 0); |
| if (has_color_blend_state && subpass_desc) { |
| uint32_t color_attachment_count = 0; |
| for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; ++i) { |
| if (subpass_desc->pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) { |
| ++color_attachment_count; |
| } |
| } |
| |
| if (pipeline.fragment_output_state && (color_attachment_count > 0) && |
| !pipeline.fragment_output_state->color_blend_state && !pipeline.IsColorBlendStateDynamic()) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09030", device, |
| create_info_loc.dot(Field::pColorBlendState), |
| "is NULL when rasterization is enabled and " |
| "subpass uses color attachments."); |
| } |
| |
| if (GetBitSetCount(subpass_desc->viewMask) > 1) { |
| if (!enabled_features.core11.multiviewTessellationShader && |
| (pipeline.create_info_shaders & |
| (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06047", device, |
| create_info_loc.dot(Field::pSubpasses, pipeline.Subpass()).dot(Field::viewMask), |
| "is 0x%" PRIx32 |
| " and pStages includes tessellation shaders, but the " |
| "multiviewTessellationShader features was not enabled.", |
| subpass_desc->viewMask); |
| } |
| if (!enabled_features.core11.multiviewGeometryShader && |
| (pipeline.create_info_shaders & VK_SHADER_STAGE_GEOMETRY_BIT)) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06048", device, |
| create_info_loc.dot(Field::pSubpasses, pipeline.Subpass()).dot(Field::viewMask), |
| "is 0x%" PRIx32 |
| " and pStages includes geometry shader, but the " |
| "multiviewGeometryShader features was not enabled.", |
| subpass_desc->viewMask); |
| } |
| } |
| } |
| } |
| |
| auto provoking_vertex_state_ci = vku::FindStructInPNextChain<VkPipelineRasterizationProvokingVertexStateCreateInfoEXT>(raster_state->pNext); |
| if (provoking_vertex_state_ci && provoking_vertex_state_ci->provokingVertexMode == VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT && |
| !enabled_features.provoking_vertex_features.provokingVertexLast) { |
| skip |= |
| LogError("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."); |
| } |
| |
| const auto rasterization_state_stream_ci = vku::FindStructInPNextChain<VkPipelineRasterizationStateStreamCreateInfoEXT>(raster_state->pNext); |
| if (rasterization_state_stream_ci) { |
| if (!enabled_features.transform_feedback_features.geometryStreams) { |
| skip |= LogError("VUID-VkPipelineRasterizationStateStreamCreateInfoEXT-geometryStreams-02324", device, raster_loc, |
| "pNext chain includes VkPipelineRasterizationStateStreamCreateInfoEXT, but " |
| "geometryStreams feature was not enabled."); |
| } else if (phys_dev_ext_props.transform_feedback_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 >= |
| phys_dev_ext_props.transform_feedback_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, |
| phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams); |
| } |
| } |
| |
| const auto rasterization_conservative_state_ci = |
| vku::FindStructInPNextChain<VkPipelineRasterizationConservativeStateCreateInfoEXT>(raster_state->pNext); |
| if (rasterization_conservative_state_ci) { |
| if (rasterization_conservative_state_ci->extraPrimitiveOverestimationSize < 0.0f || |
| rasterization_conservative_state_ci->extraPrimitiveOverestimationSize > |
| phys_dev_ext_props.conservative_rasterization_props.maxExtraPrimitiveOverestimationSize) { |
| skip |= LogError("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 (const auto *depth_bias_representation = vku::FindStructInPNextChain<VkDepthBiasRepresentationInfoEXT>(raster_state->pNext); |
| depth_bias_representation != nullptr) { |
| ValidateDepthBiasRepresentationInfo(raster_loc, LogObjectList(device), *depth_bias_representation); |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateSampleLocationsInfo(const VkSampleLocationsInfoEXT *pSampleLocationsInfo, const Location &loc) const { |
| bool skip = false; |
| const VkSampleCountFlagBits sample_count = pSampleLocationsInfo->sampleLocationsPerPixel; |
| const uint32_t sample_total_size = pSampleLocationsInfo->sampleLocationGridSize.width * |
| pSampleLocationsInfo->sampleLocationGridSize.height * SampleCountSize(sample_count); |
| if (pSampleLocationsInfo->sampleLocationsCount != sample_total_size) { |
| skip |= LogError("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 ").", |
| pSampleLocationsInfo->sampleLocationsCount, pSampleLocationsInfo->sampleLocationGridSize.width, |
| pSampleLocationsInfo->sampleLocationGridSize.height, SampleCountSize(sample_count)); |
| } |
| if ((phys_dev_ext_props.sample_locations_props.sampleLocationSampleCounts & sample_count) == 0) { |
| skip |= |
| LogError("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 PIPELINE_STATE &pipeline, |
| const safe_VkSubpassDescription2 *subpass_desc, |
| const Location &create_info_loc) const { |
| bool skip = false; |
| const Location ms_loc = create_info_loc.dot(Field::pMultisampleState); |
| const auto *multisample_state = pipeline.MultisampleState(); |
| if (subpass_desc && multisample_state) { |
| const auto &rp_state = pipeline.RenderPassState(); |
| auto accum_color_samples = [subpass_desc, &rp_state](uint32_t &samples) { |
| for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; i++) { |
| const auto attachment = subpass_desc->pColorAttachments[i].attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| samples |= static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| } |
| } |
| }; |
| |
| const uint32_t raster_samples = SampleCountSize(multisample_state->rasterizationSamples); |
| if (!pipeline.IsDynamic(VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT)) { |
| if (!(IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples) || |
| IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples) || |
| (enabled_features.multisampled_render_to_single_sampled_features.multisampledRenderToSingleSampled))) { |
| uint32_t subpass_num_samples = 0; |
| |
| accum_color_samples(subpass_num_samples); |
| |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const auto attachment = subpass_desc->pDepthStencilAttachment->attachment; |
| subpass_num_samples |= static_cast<uint32_t>(rp_state->createInfo.pAttachments[attachment].samples); |
| } |
| |
| // subpass_num_samples is 0 when the subpass has no attachments or if all attachments are VK_ATTACHMENT_UNUSED. |
| // Only validate the value of subpass_num_samples if the subpass has attachments that are not VK_ATTACHMENT_UNUSED. |
| if (subpass_num_samples && (!IsPowerOfTwo(subpass_num_samples) || (subpass_num_samples != raster_samples))) { |
| skip |= LogError("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(device_extensions.vk_amd_mixed_attachment_samples)) { |
| VkSampleCountFlagBits max_sample_count = static_cast<VkSampleCountFlagBits>(0); |
| for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; ++i) { |
| if (subpass_desc->pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) { |
| max_sample_count = |
| std::max(max_sample_count, |
| rp_state->createInfo.pAttachments[subpass_desc->pColorAttachments[i].attachment].samples); |
| } |
| } |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| max_sample_count = |
| std::max(max_sample_count, |
| rp_state->createInfo.pAttachments[subpass_desc->pDepthStencilAttachment->attachment].samples); |
| } |
| const auto raster_state = pipeline.RasterizationState(); |
| if ((raster_state && raster_state->rasterizerDiscardEnable == VK_FALSE) && |
| (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(device_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->createInfo.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(device_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->createInfo.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->renderPass(), |
| 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); |
| } |
| } |
| |
| // 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->createInfo.pAttachments[color_attachment_ref.attachment]; |
| |
| switch (color_attachment.format) { |
| case VK_FORMAT_R8_UINT: |
| case VK_FORMAT_R8_SINT: |
| case VK_FORMAT_R16_UINT: |
| case VK_FORMAT_R16_SINT: |
| case VK_FORMAT_R32_UINT: |
| case VK_FORMAT_R32_SINT: |
| attachment_is_valid = true; |
| break; |
| default: |
| std::ostringstream str; |
| str << "references an attachment with an invalid format (" << string_VkFormat(color_attachment.format) |
| << ")."; |
| error_detail = str.str(); |
| break; |
| } |
| } else { |
| std::ostringstream str; |
| str << "references an invalid attachment. The subpass pColorAttachments[" |
| << coverage_to_color_state->coverageToColorLocation << "].attachment has the value VK_ATTACHMENT_UNUSED."; |
| error_detail = str.str(); |
| } |
| } else { |
| std::ostringstream str; |
| str << "references an non-existing attachment since the subpass colorAttachmentCount is " |
| << subpass_desc->colorAttachmentCount << "."; |
| error_detail = str.str(); |
| } |
| |
| if (!attachment_is_valid) { |
| skip |= LogError("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(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT) == false) && |
| (pipeline.IsDynamic(VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) == false)) { |
| 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(device_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->createInfo.pAttachments[attachment].samples); |
| } |
| } |
| |
| if ((subpass_desc->flags & VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM) != 0) { |
| if ((raster_samples != subpass_input_attachment_samples) && |
| !pipeline.IsDynamic(VK_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()); |
| } |
| } |
| } |
| } |
| |
| if (IsExtEnabled(device_extensions.vk_ext_graphics_pipeline_library)) { |
| if (pipeline.fragment_output_state && multisample_state == nullptr) { |
| // if VK_KHR_dynamic_rendering is not enabled, can be null renderpass if using GPL |
| if (pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().renderPass != VK_NULL_HANDLE) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderpass-06631", device, ms_loc, |
| "is NULL, but pipeline is being created with fragment shader that uses samples."); |
| } |
| } |
| } |
| |
| if (!multisample_state && pipeline.fragment_output_state) { |
| // Don't need to check for VK_EXT_extended_dynamic_state3 since it would be on if using these VkDynamicState |
| const bool dynamic_alpha_to_one = |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT) || !enabled_features.core.alphaToOne; |
| if (!pipeline.IsDynamic(VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT) || |
| !pipeline.IsDynamic(VK_DYNAMIC_STATE_SAMPLE_MASK_EXT) || |
| !pipeline.IsDynamic(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT) || !dynamic_alpha_to_one) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pMultisampleState-09026", device, ms_loc, "is NULL."); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineDepthStencilState(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| const Location ds_loc = create_info_loc.dot(Field::pDepthStencilState); |
| const auto ds_state = pipeline.DepthStencilState(); |
| const auto &rp_state = pipeline.RenderPassState(); |
| const bool null_rp = (!rp_state || rp_state->renderPass() == VK_NULL_HANDLE); |
| if (ds_state) { |
| if ((((ds_state->flags & VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM) != |
| 0) || |
| ((ds_state->flags & VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM) != |
| 0)) && |
| null_rp) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-06483", device, ds_loc.dot(Field::flags), |
| "is %s but renderPass is VK_NULL_HANDLE.", |
| string_VkPipelineDepthStencilStateCreateFlags(ds_state->flags).c_str()); |
| } |
| } else if (null_rp && pipeline.fragment_shader_state && !pipeline.fragment_output_state && |
| !pipeline.IsDepthStencilStateDynamic() && !IsExtEnabled(device_extensions.vk_ext_extended_dynamic_state3)) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09035", device, ds_loc, "is NULL."); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineDynamicState(const PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| auto get_state_index = [&pipeline](const VkDynamicState state) { |
| const auto dynamic_info = pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().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(VK_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(VK_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(VK_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(VK_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(VK_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.extended_dynamic_state_features.extendedDynamicState && |
| (pipeline.IsDynamic(VK_DYNAMIC_STATE_CULL_MODE) || pipeline.IsDynamic(VK_DYNAMIC_STATE_FRONT_FACE) || |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) || pipeline.IsDynamic(VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT) || |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT) || |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE) || |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE) || pipeline.IsDynamic(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE) || |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP) || pipeline.IsDynamic(VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE) || |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE) || pipeline.IsDynamic(VK_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.extended_dynamic_state2_features.extendedDynamicState2) { |
| if (pipeline.IsDynamic(VK_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(VK_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(VK_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.extended_dynamic_state2_features.extendedDynamicState2LogicOp && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state2_features.extendedDynamicState2PatchControlPoints && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3TessellationDomainOrigin && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3DepthClampEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3PolygonMode && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3RasterizationSamples && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3SampleMask && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3AlphaToCoverageEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3AlphaToOneEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3LogicOpEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ColorBlendEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ColorBlendEquation && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ColorWriteMask && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3RasterizationStream && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ConservativeRasterizationMode && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ExtraPrimitiveOverestimationSize && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3DepthClipEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3SampleLocationsEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ColorBlendAdvanced && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ProvokingVertexMode && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3LineRasterizationMode && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3LineStippleEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3DepthClipNegativeOneToOne && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ViewportWScalingEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ViewportSwizzle && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3CoverageToColorEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3CoverageToColorLocation && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3CoverageModulationMode && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3CoverageModulationTableEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3CoverageModulationTable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3CoverageReductionMode && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3RepresentativeFragmentTestEnable && |
| pipeline.IsDynamic(VK_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.extended_dynamic_state3_features.extendedDynamicState3ShadingRateImageEnable && |
| pipeline.IsDynamic(VK_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.vertex_input_dynamic_state_features.vertexInputDynamicState && |
| pipeline.IsDynamic(VK_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.color_write_features.colorWriteEnable && pipeline.IsDynamic(VK_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 PIPELINE_STATE &pipeline, |
| const Location &create_info_loc) const { |
| bool skip = false; |
| const auto *fragment_shading_rate_state = vku::FindStructInPNextChain<VkPipelineFragmentShadingRateStateCreateInfoKHR>(pipeline.PNext()); |
| if (!fragment_shading_rate_state || pipeline.IsDynamic(VK_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.fragment_shading_rate_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.fragment_shading_rate_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.fragment_shading_rate_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.fragment_shading_rate_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])); |
| } |
| |
| const auto combiner_ops = fragment_shading_rate_state->combinerOps; |
| if (pipeline.pre_raster_state || pipeline.fragment_shader_state) { |
| const auto enums = ValidParamValues<VkFragmentShadingRateCombinerOpKHR>(); |
| if (std::find(enums.begin(), enums.end(), combiner_ops[0]) == enums.end()) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicState-06567", device, |
| create_info_loc.pNext(Struct::VkPipelineFragmentShadingRateStateCreateInfoKHR, Field::combinerOps, 0), |
| "(0x%" PRIx32 ") is invalid.", combiner_ops[0]); |
| } |
| if (std::find(enums.begin(), enums.end(), combiner_ops[1]) == enums.end()) { |
| 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 PIPELINE_STATE &pipeline, const Location &create_info_loc) const { |
| bool skip = false; |
| const auto rendering_struct = vku::FindStructInPNextChain<VkPipelineRenderingCreateInfo>(pipeline.PNext()); |
| if (!rendering_struct) { |
| return skip; |
| } |
| const auto color_blend_state = pipeline.ColorBlendState(); |
| const auto raster_state = pipeline.RasterizationState(); |
| const bool has_rasterization = raster_state && (raster_state->rasterizerDiscardEnable == VK_FALSE); |
| if (has_rasterization) { |
| 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(device_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() && |
| pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().renderPass == VK_NULL_HANDLE) { |
| skip |= |
| LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-09037", device, create_info_loc.dot(Field::pColorBlendState), |
| "is NULL, but %s is %" PRIu32 ".", |
| create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::colorAttachmentCount).Fields().c_str(), |
| rendering_struct->colorAttachmentCount); |
| } |
| } |
| |
| if (rendering_struct->viewMask != 0) { |
| const VkShaderStageFlags stages = pipeline.create_info_shaders; |
| if (!enabled_features.core11.multiviewTessellationShader && |
| (stages & (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) { |
| skip |= |
| LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06057", device, |
| create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask), |
| "is %" PRIu32 |
| ") and pStages contains tesselation shaders, but the multiviewTessellationShader feature was not enabled.", |
| rendering_struct->viewMask); |
| } |
| |
| if (!enabled_features.core11.multiviewGeometryShader && (stages & VK_SHADER_STAGE_GEOMETRY_BIT)) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06058", device, |
| create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask), |
| "is %" PRIu32 |
| ") and pStages contains geometry shader, but the multiviewGeometryShader feature was not enabled.", |
| rendering_struct->viewMask); |
| } |
| |
| if (!enabled_features.mesh_shader_features.multiviewMeshShader && (stages & VK_SHADER_STAGE_MESH_BIT_EXT)) { |
| skip |= |
| LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-07064", device, |
| create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask), |
| "is %" PRIu32 ") and pStages contains mesh shader, but the multiviewMeshShader feature was not enabled.", |
| rendering_struct->viewMask); |
| } |
| |
| if (pipeline.GetCreateInfo<VkGraphicsPipelineCreateInfo>().renderPass == VK_NULL_HANDLE && raster_state) { |
| for (const auto &stage : pipeline.stage_states) { |
| if (stage.spirv_state->static_data_.has_builtin_layer) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06059", device, |
| create_info_loc.dot(Field::pMultisampleState), |
| "is NULL, but pipeline created with fragment shader state and renderPass != VK_NULL_HANDLE."); |
| } |
| } |
| } |
| } |
| |
| if (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) { |
| VkFormatFeatureFlags2KHR format_features = GetPotentialFormatFeatures(color_format); |
| if (((format_features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT) == 0) && |
| (color_blend_state && (color_index < color_blend_state->attachmentCount) && |
| (color_blend_state->pAttachments[color_index].blendEnable != VK_FALSE))) { |
| skip |= LogError( |
| "VUID-VkGraphicsPipelineCreateInfo-renderPass-06062", device, |
| create_info_loc.dot(Field::pColorBlendState).dot(Field::pAttachments, color_index).dot(Field::blendEnable), |
| "is VK_TRUE."); |
| } |
| |
| 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) potential format features are %s.", string_VkFormat(color_format), |
| string_VkFormatFeatureFlags2(format_features).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) potential format features are %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) potential format features are %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) { |
| 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.core11.multiview == VK_FALSE) && (rendering_struct->viewMask != 0)) { |
| skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-multiview-06577", device, |
| create_info_loc.pNext(Struct::VkPipelineRenderingCreateInfo, Field::viewMask), |
| "is %" PRIu32 ", 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 CMD_BUFFER_STATE *cb_state, const PIPELINE_STATE &pipeline, |
| const Location &loc) const { |
| bool skip = false; |
| |
| if (cb_state->inheritedViewportDepths.size() != 0) { |
| bool dyn_viewport = |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT) || pipeline.IsDynamic(VK_DYNAMIC_STATE_VIEWPORT); |
| bool dyn_scissor = pipeline.IsDynamic(VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT) || pipeline.IsDynamic(VK_DYNAMIC_STATE_SCISSOR); |
| if (!dyn_viewport || !dyn_scissor) { |
| const LogObjectList objlist(cb_state->commandBuffer(), pipeline.pipeline()); |
| skip |= LogError("VUID-vkCmdBindPipeline-commandBuffer-04808", objlist, loc, |
| "Graphics pipeline incompatible with viewport/scissor inheritance."); |
| } |
| const auto *discard_rectangle_state = vku::FindStructInPNextChain<VkPipelineDiscardRectangleStateCreateInfoEXT>(pipeline.PNext()); |
| if ((discard_rectangle_state && discard_rectangle_state->discardRectangleCount != 0) || |
| (pipeline.IsDynamic(VK_DYNAMIC_STATE_DISCARD_RECTANGLE_ENABLE_EXT))) { |
| if (!pipeline.IsDynamic(VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT)) { |
| const LogObjectList objlist(cb_state->commandBuffer(), pipeline.pipeline()); |
| 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 " |
| "VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleCount = %" PRIu32 |
| ", but without VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT.", |
| discard_rectangle_state->discardRectangleCount); |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateGraphicsPipelineShaderDynamicState(const PIPELINE_STATE &pipeline, const CMD_BUFFER_STATE &cb_state, |
| const Location &loc, const DrawDispatchVuid &vuid) const { |
| bool skip = false; |
| |
| for (auto &stage_state : pipeline.stage_states) { |
| const VkShaderStageFlagBits stage = stage_state.GetStage(); |
| if (stage == VK_SHADER_STAGE_VERTEX_BIT || stage == VK_SHADER_STAGE_GEOMETRY_BIT || stage == VK_SHADER_STAGE_MESH_BIT_EXT) { |
| if (!phys_dev_ext_props.fragment_shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports && |
| pipeline.IsDynamic(VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT) && cb_state.dynamic_state_value.viewport_count != 1) { |
| if (stage_state.entrypoint && stage_state.entrypoint->written_builtin_primitive_shading_rate_khr) { |
| skip |= LogError(vuid.viewport_count_primitive_shading_rate_04552, stage_state.module_state->Handle(), loc, |
| "%s shader of currently bound pipeline statically writes to PrimitiveShadingRateKHR built-in" |
| "but multiple viewports are set by the last call to vkCmdSetViewportWithCountEXT," |
| "and the primitiveFragmentShadingRateWithMultipleViewports limit is not supported.", |
| string_VkShaderStageFlagBits(stage)); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| // Validate draw-time state related to the PSO |
| bool CoreChecks::ValidatePipelineDrawtimeState(const LAST_BOUND_STATE &last_bound_state, const Location &loc) const { |
| bool skip = false; |
| const CMD_BUFFER_STATE &cb_state = last_bound_state.cb_state; |
| const PIPELINE_STATE *pipeline = last_bound_state.pipeline_state; |
| const auto ¤t_vtx_bfr_binding_info = cb_state.current_vertex_buffer_binding_info.vertex_buffer_bindings; |
| const DrawDispatchVuid &vuid = GetDrawDispatchVuid(loc.function); |
| |
| if (cb_state.activeRenderPass->UsesDynamicRendering()) { |
| if (pipeline) { |
| ValidatePipelineDynamicRenderpassDraw(last_bound_state, loc); |
| } |
| } else { |
| if (pipeline) { |
| ValidatePipelineRenderpassDraw(last_bound_state, loc); |
| } else if (last_bound_state.HasShaderObjects()) { |
| skip |= LogError(vuid.render_pass_began_08876, cb_state.commandBuffer(), loc, |
| "Shader objects must be used with dynamic rendering, but VkRenderPass %s is active.", |
| FormatHandle(cb_state.activeRenderPass->renderPass()).c_str()); |
| } |
| } |
| |
| bool primitives_generated_query_with_rasterizer_discard = |
| enabled_features.primitives_generated_query_features.primitivesGeneratedQueryWithRasterizerDiscard == VK_TRUE; |
| bool primitives_generated_query_with_non_zero_streams = |
| enabled_features.primitives_generated_query_features.primitivesGeneratedQueryWithNonZeroStreams == VK_TRUE; |
| if (!primitives_generated_query_with_rasterizer_discard || !primitives_generated_query_with_non_zero_streams) { |
| bool primitives_generated_query = false; |
| for (const auto &query : cb_state.activeQueries) { |
| auto query_pool_state = Get<QUERY_POOL_STATE>(query.pool); |
| if (query_pool_state && query_pool_state->createInfo.queryType == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT) { |
| primitives_generated_query = true; |
| break; |
| } |
| } |
| if (primitives_generated_query) { |
| bool rasterizer_discard_enabled = false; |
| if (pipeline && !pipeline->IsDynamic(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE)) { |
| rasterizer_discard_enabled = pipeline->RasterizationState()->rasterizerDiscardEnable; |
| } else { |
| rasterizer_discard_enabled = cb_state.dynamic_state_value.rasterizer_discard_enable; |
| } |
| if (!primitives_generated_query_with_rasterizer_discard && rasterizer_discard_enabled) { |
| LogObjectList objlist(cb_state.commandBuffer()); |
| if (pipeline) { |
| objlist.add(pipeline->pipeline()); |
| } |
| skip |= LogError(vuid.primitives_generated_06708, objlist, loc, |
| "a VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT query is active and pipeline was created with " |
| "VkPipelineRasterizationStateCreateInfo::rasterizerDiscardEnable set to VK_TRUE, but " |
| "primitivesGeneratedQueryWithRasterizerDiscard feature is not enabled."); |
| } |
| if (!primitives_generated_query_with_non_zero_streams && pipeline) { |
| const auto rasterization_state_stream_ci = |
| vku::FindStructInPNextChain<VkPipelineRasterizationStateStreamCreateInfoEXT>(pipeline->RasterizationState()->pNext); |
| if (rasterization_state_stream_ci && rasterization_state_stream_ci->rasterizationStream != 0) { |
| LogObjectList objlist(cb_state.commandBuffer()); |
| if (pipeline) { |
| objlist.add(pipeline->pipeline()); |
| } |
| skip |= LogError(vuid.primitives_generated_streams_06709, objlist, loc, |
| "a VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT query is active and pipeline was created with " |
| "VkPipelineRasterizationStateStreamCreateInfoEXT::rasterizationStream set to %" PRIu32 |
| ", but " |
| "primitivesGeneratedQueryWithNonZeroStreams feature is not enabled.", |
| rasterization_state_stream_ci->rasterizationStream); |
| } |
| } |
| } |
| } |
| |
| // Verify vertex & index buffer for unprotected command buffer. |
| // Because vertex & index buffer is read only, it doesn't need to care protected command buffer case. |
| if (enabled_features.core11.protectedMemory == VK_TRUE) { |
| for (const auto &buffer_binding : current_vtx_bfr_binding_info) { |
| if (buffer_binding.buffer_state && !buffer_binding.buffer_state->Destroyed()) { |
| skip |= ValidateProtectedBuffer(cb_state, *buffer_binding.buffer_state, loc, vuid.unprotected_command_buffer_02707, |
| "Buffer is vertex buffer"); |
| } |
| } |
| if (cb_state.index_buffer_binding.bound()) { |
| skip |= ValidateProtectedBuffer(cb_state, *cb_state.index_buffer_binding.buffer_state, loc, |
| vuid.unprotected_command_buffer_02707, "Buffer is index buffer"); |
| } |
| } |
| |
| // Verify vertex binding |
| // TODO #5954 - Add proper dynamic support |
| if (pipeline && pipeline->vertex_input_state && !pipeline->IsDynamic(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)) { |
| for (size_t i = 0; i < pipeline->vertex_input_state->binding_descriptions.size(); i++) { |
| const auto vertex_binding = pipeline->vertex_input_state->binding_descriptions[i].binding; |
| if (current_vtx_bfr_binding_info.size() < (vertex_binding + 1)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline()); |
| skip |= LogError(vuid.vertex_binding_04007, objlist, loc, |
| "%s expects that this Command Buffer's vertex binding Index %u should be set via " |
| "vkCmdBindVertexBuffers. This is because pVertexBindingDescriptions[%zu].binding value is %u.", |
| FormatHandle(*pipeline).c_str(), vertex_binding, i, vertex_binding); |
| } else if ((current_vtx_bfr_binding_info[vertex_binding].buffer_state == nullptr) && |
| !enabled_features.robustness2_features.nullDescriptor) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline()); |
| skip |= LogError(vuid.vertex_binding_null_04008, objlist, loc, |
| "Vertex binding %d must not be VK_NULL_HANDLE %s expects that this Command Buffer's vertex " |
| "binding Index %u should be set via " |
| "vkCmdBindVertexBuffers. This is because pVertexBindingDescriptions[%zu].binding value is %u.", |
| vertex_binding, FormatHandle(*pipeline).c_str(), vertex_binding, i, vertex_binding); |
| } |
| } |
| |
| // Verify vertex attribute address alignment |
| for (uint32_t i = 0; i < pipeline->vertex_input_state->vertex_attribute_descriptions.size(); i++) { |
| const auto &attribute_description = pipeline->vertex_input_state->vertex_attribute_descriptions[i]; |
| const uint32_t vertex_binding = attribute_description.binding; |
| const VkDeviceSize attribute_offset = attribute_description.offset; |
| |
| const auto &vertex_binding_map_it = pipeline->vertex_input_state->binding_to_index_map.find(vertex_binding); |
| if ((vertex_binding_map_it != pipeline->vertex_input_state->binding_to_index_map.cend()) && |
| (vertex_binding < current_vtx_bfr_binding_info.size()) && |
| ((current_vtx_bfr_binding_info[vertex_binding].buffer_state) || |
| enabled_features.robustness2_features.nullDescriptor)) { |
| uint32_t vertex_buffer_stride = |
| pipeline->vertex_input_state->binding_descriptions[vertex_binding_map_it->second].stride; |
| if (pipeline->IsDynamic(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT)) { |
| vertex_buffer_stride = static_cast<uint32_t>(current_vtx_bfr_binding_info[vertex_binding].stride); |
| const uint32_t attribute_binding_extent = |
| attribute_description.offset + vkuFormatElementSize(attribute_description.format); |
| if (vertex_buffer_stride != 0 && vertex_buffer_stride < attribute_binding_extent) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline()); |
| skip |= LogError(objlist, "VUID-vkCmdBindVertexBuffers2-pStrides-06209", |
| "The pStrides[%" PRIu32 "] (%" PRIu32 |
| ") parameter in the last call to %s is not 0 " |
| "and less than the extent of the binding for attribute %" PRIu32 " (%" PRIu32 ").", |
| vertex_binding, vertex_buffer_stride, loc.StringFunc(), i, attribute_binding_extent); |
| } |
| } |
| const VkDeviceSize vertex_buffer_offset = current_vtx_bfr_binding_info[vertex_binding].offset; |
| |
| // Use 1 as vertex/instance index to use buffer stride as well |
| const VkDeviceSize attrib_address = vertex_buffer_offset + vertex_buffer_stride + attribute_offset; |
| |
| const VkDeviceSize vtx_attrib_req_alignment = pipeline->vertex_input_state->vertex_attribute_alignments[i]; |
| |
| if (SafeModulo(attrib_address, vtx_attrib_req_alignment) != 0) { |
| const LogObjectList objlist(current_vtx_bfr_binding_info[vertex_binding].buffer_state->buffer(), |
| pipeline->pipeline()); |
| skip |= LogError( |
| vuid.vertex_binding_attribute_02721, objlist, 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 (%" PRIu32 ") + attribute offset (%" PRIu64 ").", |
| string_VkFormat(attribute_description.format), vtx_attrib_req_alignment, attrib_address, i, vertex_binding, |
| attribute_description.location, vertex_buffer_offset, vertex_buffer_stride, attribute_offset); |
| } |
| } else { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline()); |
| skip |= LogError(vuid.vertex_binding_attribute_02721, objlist, loc, |
| "pVertexAttributeDescriptions[%" PRIu32 "].binding (%" PRIu32 ") is an invalid value.", |
| vertex_binding, i); |
| } |
| } |
| } |
| |
| // 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) { |
| const auto *raster_state = pipeline->RasterizationState(); |
| if (!raster_state || (raster_state->rasterizerDiscardEnable == VK_FALSE)) { |
| if (cb_state.activeRenderPass->UsesDynamicRendering()) { |
| // TODO: Mirror the below VUs but using dynamic rendering |
| const auto dynamic_rendering_info = cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info; |
| } else { |
| const auto render_pass_info = cb_state.activeRenderPass->createInfo.ptr(); |
| const VkSubpassDescription2 *subpass_desc = &render_pass_info->pSubpasses[cb_state.GetActiveSubpass()]; |
| uint32_t i; |
| unsigned subpass_num_samples = 0; |
| |
| for (i = 0; i < subpass_desc->colorAttachmentCount; i++) { |
| const auto attachment = subpass_desc->pColorAttachments[i].attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| subpass_num_samples |= static_cast<unsigned>(render_pass_info->pAttachments[attachment].samples); |
| |
| const auto *imageview_state = cb_state.GetActiveAttachmentImageViewState(attachment); |
| const auto *color_blend_state = pipeline->ColorBlendState(); |
| if (imageview_state && color_blend_state && (attachment < color_blend_state->attachmentCount)) { |
| if ((imageview_state->format_features & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR) == 0 && |
| color_blend_state->pAttachments[i].blendEnable != VK_FALSE) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= |
| LogError(vuid.blend_enable_04727, objlist, loc, |
| "Image view's format features of the color attachment (%" PRIu32 |
| ") of the active subpass do not contain VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT " |
| "bit, but active pipeline's pAttachments[%" PRIu32 "].blendEnable is not VK_FALSE.", |
| attachment, attachment); |
| } |
| } |
| } |
| } |
| |
| if (subpass_desc->pDepthStencilAttachment && |
| subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) { |
| const auto attachment = subpass_desc->pDepthStencilAttachment->attachment; |
| subpass_num_samples |= static_cast<unsigned>(render_pass_info->pAttachments[attachment].samples); |
| } |
| |
| const VkSampleCountFlagBits rasterization_samples = last_bound_state.GetRasterizationSamples(); |
| if (!(IsExtEnabled(device_extensions.vk_amd_mixed_attachment_samples) || |
| IsExtEnabled(device_extensions.vk_nv_framebuffer_mixed_samples) || |
| enabled_features.multisampled_render_to_single_sampled_features.multisampledRenderToSingleSampled) && |
| ((subpass_num_samples & static_cast<unsigned>(rasterization_samples)) != subpass_num_samples)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.msrtss_rasterization_samples_07284, objlist, loc, |
| "In %s the sample count is %s while the current %s has %s and they need to be the same.", |
| FormatHandle(*pipeline).c_str(), string_VkSampleCountFlagBits(rasterization_samples), |
| FormatHandle(*cb_state.activeRenderPass).c_str(), |
| string_VkSampleCountFlags(static_cast<VkSampleCountFlags>(subpass_num_samples)).c_str()); |
| } |
| |
| const bool dynamic_line_raster_mode = pipeline->IsDynamic(VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT); |
| const bool dynamic_line_stipple_enable = pipeline->IsDynamic(VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT); |
| if (dynamic_line_stipple_enable || dynamic_line_raster_mode) { |
| const auto raster_line_state = |
| vku::FindStructInPNextChain<VkPipelineRasterizationLineStateCreateInfoEXT>(raster_state->pNext); |
| |
| const VkLineRasterizationModeEXT 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_EXT && |
| (!enabled_features.line_rasterization_features.stippledRectangularLines)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.stippled_rectangular_lines_07495, objlist, loc, |
| "lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT (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_EXT && |
| (!enabled_features.line_rasterization_features.stippledBresenhamLines)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.stippled_bresenham_lines_07496, objlist, loc, |
| "lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT (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_EXT && |
| (!enabled_features.line_rasterization_features.stippledSmoothLines)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= |
| LogError(vuid.stippled_smooth_lines_07497, objlist, loc, |
| "lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT (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_EXT && |
| (!enabled_features.line_rasterization_features.stippledRectangularLines || |
| !phys_dev_props.limits.strictLines)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError( |
| vuid.stippled_default_strict_07498, objlist, loc, |
| "lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT (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.line_rasterization_features.stippledRectangularLines ? "enabled" : "not enabled", |
| phys_dev_props.limits.strictLines ? "VK_TRUE" : "VK_FALSE"); |
| } |
| } |
| } |
| } |
| } |
| |
| if (enabled_features.fragment_shading_rate_features.primitiveFragmentShadingRate) { |
| skip |= ValidateGraphicsPipelineShaderDynamicState(*pipeline, cb_state, loc, vuid); |
| } |
| } |
| |
| if (pipeline && pipeline->IsDynamic(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT) && |
| cb_state.dynamic_state_value.alpha_to_coverage_enable) { |
| if (pipeline->fragment_shader_state && pipeline->fragment_shader_state->fragment_shader) { |
| // TODO - Find better way to get SPIR-V static data |
| std::shared_ptr<const SHADER_MODULE_STATE> module_state = pipeline->fragment_shader_state->fragment_shader; |
| const safe_VkPipelineShaderStageCreateInfo *stage_ci = pipeline->fragment_shader_state->fragment_shader_ci.get(); |
| auto entrypoint = module_state->spirv->FindEntrypoint(stage_ci->pName, stage_ci->stage); |
| |
| // TODO - DualSource blend has two outputs at location zero, so Index == 0 is the one that's required. |
| // Currently lack support to test each index. |
| if (entrypoint && !entrypoint->has_alpha_to_coverage_variable && !pipeline->DualSourceBlending()) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline()); |
| skip |= LogError(vuid.dynamic_alpha_to_coverage_component_08919, objlist, loc, |
| "vkCmdSetAlphaToCoverageEnableEXT set alphaToCoverageEnable to true but the bound pipeline " |
| "fragment shader doesn't declare a variable that covers Location 0, Component 3."); |
| } |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateShaderObjectDrawtimeState(const LAST_BOUND_STATE &last_bound_state, const Location &loc) const { |
| bool skip = false; |
| const CMD_BUFFER_STATE &cb_state = last_bound_state.cb_state; |
| const LogObjectList objlist(cb_state.commandBuffer()); |
| const DrawDispatchVuid &vuid = GetDrawDispatchVuid(loc.function); |
| |
| if (!last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::VERTEX)) { |
| skip |= LogError(vuid.vertex_shader_08684, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_VERTEX_BIT and either VK_NULL_HANDLE or a valid VK_SHADER_STAGE_VERTEX_BIT shader."); |
| } |
| if (enabled_features.core.tessellationShader && |
| !last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::TESSELLATION_CONTROL)) { |
| skip |= LogError(vuid.tessellation_control_shader_08685, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT and either VK_NULL_HANDLE or a valid " |
| "VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT shader."); |
| } |
| if (enabled_features.core.tessellationShader && |
| !last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::TESSELLATION_EVALUATION)) { |
| skip |= LogError(vuid.tessellation_evaluation_shader_08686, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT and either VK_NULL_HANDLE or a valid " |
| "VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT shader."); |
| } |
| if (enabled_features.core.geometryShader && !last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::GEOMETRY)) { |
| skip |= LogError(vuid.geometry_shader_08687, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_GEOMETRY_BIT and either VK_NULL_HANDLE or a valid VK_SHADER_STAGE_GEOMETRY_BIT shader."); |
| } |
| if (!last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::FRAGMENT)) { |
| skip |= LogError(vuid.fragment_shader_08688, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_FRAGMENT_BIT and either VK_NULL_HANDLE or a valid VK_SHADER_STAGE_FRAGMENT_BIT shader."); |
| } |
| if (enabled_features.mesh_shader_features.taskShader && !last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::TASK)) { |
| skip |= LogError(vuid.task_shader_08689, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_TASK_BIT and either VK_NULL_HANDLE or a valid VK_SHADER_STAGE_TASK_BIT shader."); |
| } |
| if (enabled_features.mesh_shader_features.meshShader && !last_bound_state.IsValidShaderOrNullBound(ShaderObjectStage::MESH)) { |
| skip |= LogError(vuid.mesh_shader_08690, objlist, loc, |
| "There is no graphics pipeline bound and vkCmdBindShadersEXT() was not called with stage " |
| "VK_SHADER_STAGE_MESH_BIT and either VK_NULL_HANDLE or a valid VK_SHADER_STAGE_MESH_BIT shader."); |
| } |
| |
| bool validVertShader = last_bound_state.GetShader(ShaderObjectStage::VERTEX); |
| bool validTaskShader = last_bound_state.GetShader(ShaderObjectStage::TASK); |
| bool validMeshShader = last_bound_state.GetShader(ShaderObjectStage::MESH); |
| |
| if (enabled_features.mesh_shader_features.taskShader || enabled_features.mesh_shader_features.meshShader) { |
| if ((validVertShader && validMeshShader) || (!validVertShader && !validMeshShader)) { |
| const std::string msg = validVertShader ? "Both vertex shader and mesh shader are bound" |
| : "Neither vertex shader nor mesh shader are bound"; |
| skip |= LogError(vuid.vert_mesh_shader_08693, objlist, loc, "%s.", msg.c_str()); |
| } |
| } |
| if (enabled_features.mesh_shader_features.taskShader && enabled_features.mesh_shader_features.meshShader) { |
| if (validMeshShader && |
| (last_bound_state.GetShaderState(ShaderObjectStage::MESH)->create_info.flags & |
| VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT) == 0 && |
| !validTaskShader) { |
| skip |= |
| LogError(vuid.task_mesh_shader_08694, objlist, loc, |
| "Mesh shader %s was created without VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT, but no task shader is bound.", |
| report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::MESH)).c_str()); |
| } else if (validMeshShader && |
| (last_bound_state.GetShaderState(ShaderObjectStage::MESH)->create_info.flags & |
| VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT) != 0 && |
| validTaskShader) { |
| skip |= LogError(vuid.task_mesh_shader_08695, objlist, loc, |
| "Mesh shader %s was created with VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT, but a task shader is bound.", |
| report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::MESH)).c_str()); |
| } |
| } |
| if (validVertShader && (validTaskShader || validMeshShader)) { |
| std::stringstream msg; |
| if (validTaskShader && validMeshShader) { |
| msg << "task shader " << report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::TASK)) |
| << "and mesh shader " << report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::MESH)) |
| << " are bound as well"; |
| } else if (validTaskShader) { |
| msg << "task shader " << report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::TASK)) |
| << " is bound as well"; |
| } else if (validMeshShader) { |
| msg << "mesh shader " << report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::MESH)) |
| << " is bound as well"; |
| } |
| skip |= LogError(vuid.vert_task_mesh_shader_08696, objlist, loc, "Vertex shader %s is bound, but %s.", |
| report_data->FormatHandle(last_bound_state.GetShader(ShaderObjectStage::MESH)).c_str(), msg.str().c_str()); |
| } |
| for (uint32_t i = 0; i < SHADER_OBJECT_STAGE_COUNT; ++i) { |
| if (i != static_cast<uint32_t>(ShaderObjectStage::COMPUTE) && last_bound_state.shader_object_states[i]) { |
| for (const auto &linkedShader : last_bound_state.shader_object_states[i]->linked_shaders) { |
| bool found = false; |
| for (uint32_t j = 0; j < SHADER_OBJECT_STAGE_COUNT; ++j) { |
| if (linkedShader == last_bound_state.GetShader(static_cast<ShaderObjectStage>(j))) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| const auto missingShader = Get<SHADER_OBJECT_STATE>(linkedShader); |
| skip |= |
| LogError(vuid.linked_shaders_08698, objlist, loc, |
| "Shader %s (%s) was created with VK_SHADER_CREATE_LINK_STAGE_BIT_EXT, but the linked %s " |
| "shader (%s) is not bound.", |
| report_data->FormatHandle(last_bound_state.GetShader(static_cast<ShaderObjectStage>(i))).c_str(), |
| string_VkShaderStageFlagBits(last_bound_state.shader_object_states[i]->create_info.stage), |
| report_data->FormatHandle(linkedShader).c_str(), |
| string_VkShaderStageFlagBits(missingShader->create_info.stage)); |
| break; |
| } |
| } |
| } |
| } |
| const VkShaderStageFlagBits graphics_stages[] = {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, |
| VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, VK_SHADER_STAGE_GEOMETRY_BIT, |
| VK_SHADER_STAGE_FRAGMENT_BIT}; |
| VkShaderStageFlagBits prev_stage = VK_SHADER_STAGE_ALL; |
| VkShaderStageFlagBits next_stage = VK_SHADER_STAGE_ALL; |
| for (const auto stage : graphics_stages) { |
| const auto state = last_bound_state.GetShaderState(VkShaderStageToShaderObjectStage(stage)); |
| if (state && next_stage != VK_SHADER_STAGE_ALL && state->create_info.stage != next_stage) { |
| skip |= LogError(vuid.linked_shaders_08699, objlist, loc, |
| "Shaders %s and %s were created with VK_SHADER_CREATE_LINK_STAGE_BIT_EXT without intermediate " |
| "stage %s linked, but %s shader is bound.", |
| string_VkShaderStageFlagBits(prev_stage), string_VkShaderStageFlagBits(next_stage), |
| string_VkShaderStageFlagBits(stage), string_VkShaderStageFlagBits(stage)); |
| break; |
| } |
| if (state) { |
| next_stage = VK_SHADER_STAGE_ALL; |
| if (!state->linked_shaders.empty()) { |
| prev_stage = stage; |
| for (const auto &linked_shader : state->linked_shaders) { |
| const auto &linked_state = Get<SHADER_OBJECT_STATE>(linked_shader); |
| if (linked_state->create_info.stage == state->create_info.nextStage) { |
| next_stage = static_cast<VkShaderStageFlagBits>(state->create_info.nextStage); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| const SHADER_OBJECT_STATE *first = nullptr; |
| for (const auto shader_state : last_bound_state.shader_object_states) { |
| if (shader_state && shader_state->IsGraphicsShaderState()) { |
| if (!first) { |
| first = shader_state; |
| } else { |
| bool pushConstsDifferent = |
| first->create_info.pushConstantRangeCount != shader_state->create_info.pushConstantRangeCount; |
| if (!pushConstsDifferent) { |
| bool found = false; |
| for (uint32_t i = 0; i < shader_state->create_info.pushConstantRangeCount; ++i) { |
| for (uint32_t j = 0; j < first->create_info.pushConstantRangeCount; ++j) { |
| if (shader_state->create_info.pPushConstantRanges[i] == first->create_info.pPushConstantRanges[j]) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| pushConstsDifferent = true; |
| break; |
| } |
| } |
| } |
| if (pushConstsDifferent) { |
| skip |= LogError(vuid.shaders_push_constants_08878, objlist, loc, |
| "Shaders %s and %s have different push constant ranges.", |
| string_VkShaderStageFlagBits(first->create_info.stage), |
| string_VkShaderStageFlagBits(shader_state->create_info.stage)); |
| } |
| bool descriptorLayoutsDifferent = first->create_info.setLayoutCount != shader_state->create_info.setLayoutCount; |
| if (!descriptorLayoutsDifferent) { |
| bool found = false; |
| for (uint32_t i = 0; i < shader_state->create_info.setLayoutCount; ++i) { |
| for (uint32_t j = 0; j < first->create_info.setLayoutCount; ++j) { |
| if (shader_state->create_info.pSetLayouts[i] == first->create_info.pSetLayouts[j]) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| descriptorLayoutsDifferent = true; |
| break; |
| } |
| } |
| } |
| if (descriptorLayoutsDifferent) { |
| skip |= LogError(vuid.shaders_descriptor_layouts_08879, objlist, loc, |
| "Shaders %s and %s have different descriptor set layouts.", |
| string_VkShaderStageFlagBits(first->create_info.stage), |
| string_VkShaderStageFlagBits(shader_state->create_info.stage)); |
| } |
| } |
| } |
| } |
| |
| if (loc.function != Func::vkCmdDrawMeshTasksNV && loc.function != Func::vkCmdDrawMeshTasksIndirectNV && |
| loc.function != Func::vkCmdDrawMeshTasksIndirectCountNV && loc.function != Func::vkCmdDrawMeshTasksEXT && |
| loc.function != Func::vkCmdDrawMeshTasksIndirectEXT && loc.function != Func::vkCmdDrawMeshTasksIndirectCountEXT) { |
| ValidateShaderObjectGraphicsDrawtimeState(last_bound_state, loc); |
| } |
| |
| return skip; |
| } |
| |
| bool CoreChecks::ValidateShaderObjectGraphicsDrawtimeState(const LAST_BOUND_STATE &last_bound_state, const Location &loc) const { |
| bool skip = false; |
| |
| const CMD_BUFFER_STATE &cb_state = last_bound_state.cb_state; |
| const LogObjectList objlist(cb_state.commandBuffer()); |
| const DrawDispatchVuid &vuid = GetDrawDispatchVuid(loc.function); |
| |
| bool validTaskShader = last_bound_state.GetShader(ShaderObjectStage::TASK); |
| bool validMeshShader = last_bound_state.GetShader(ShaderObjectStage::MESH); |
| if (validTaskShader || validMeshShader) { |
| std::stringstream msg; |
| if (validTaskShader && validMeshShader) { |
| msg << "Task and mesh shaders are bound."; |
| } else if (validTaskShader) { |
| msg << "Task shader is bound."; |
| } else { |
| msg << "Mesh shader is bound."; |
| } |
| skip |= LogError(vuid.draw_shaders_no_task_mesh_08885, objlist, loc, "%s", msg.str().c_str()); |
| } |
| |
| return skip; |
| } |
| |
| // Verify that PSO creation renderPass is compatible with active (non-dynamic) renderPass |
| bool CoreChecks::ValidatePipelineRenderpassDraw(const LAST_BOUND_STATE &last_bound_state, const Location &loc) const { |
| bool skip = false; |
| const CMD_BUFFER_STATE &cb_state = last_bound_state.cb_state; |
| const PIPELINE_STATE &pipeline = *last_bound_state.pipeline_state; |
| const DrawDispatchVuid &vuid = GetDrawDispatchVuid(loc.function); |
| |
| const auto &rp_state = pipeline.RenderPassState(); |
| // TODO: AMD extension codes are included here, but actual function entrypoints are not yet intercepted |
| if (cb_state.activeRenderPass->renderPass() != rp_state->renderPass()) { |
| // renderPass that PSO was created with must be compatible with active renderPass that PSO is being used with |
| skip |= ValidateRenderPassCompatibility("active render pass", *cb_state.activeRenderPass.get(), "pipeline state object", |
| *rp_state.get(), loc, vuid.render_pass_compatible_02684); |
| } |
| const auto subpass = pipeline.Subpass(); |
| if (subpass != cb_state.GetActiveSubpass()) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline.pipeline(), cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.subpass_index_02685, objlist, loc, |
| "Pipeline was built for subpass %" PRIu32 " but used in subpass %" PRIu32 ".", subpass, |
| cb_state.GetActiveSubpass()); |
| } |
| const safe_VkAttachmentReference2 *ds_attachment = |
| cb_state.activeRenderPass->createInfo.pSubpasses[cb_state.GetActiveSubpass()].pDepthStencilAttachment; |
| if (ds_attachment != nullptr) { |
| // Check if depth stencil attachment was created with sample location compatible bit |
| if (pipeline.SampleLocationEnabled() == VK_TRUE) { |
| const uint32_t attachment = ds_attachment->attachment; |
| if (attachment != VK_ATTACHMENT_UNUSED) { |
| const auto *imageview_state = cb_state.GetActiveAttachmentImageViewState(attachment); |
| if (imageview_state != nullptr) { |
| const auto *image_state = imageview_state->image_state.get(); |
| if (image_state != nullptr) { |
| if ((image_state->createInfo.flags & VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT) == 0) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline.pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.sample_location_02689, objlist, loc, |
| "sampleLocationsEnable is true for the pipeline, but the subpass (%u) depth " |
| "stencil attachment's VkImage was not created with " |
| "VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT.", |
| cb_state.GetActiveSubpass()); |
| } |
| } |
| } |
| } |
| } |
| const auto ds_state = pipeline.DepthStencilState(); |
| if (ds_state) { |
| if (IsImageLayoutDepthReadOnly(ds_attachment->layout) && last_bound_state.IsDepthWriteEnable()) { |
| const LogObjectList objlist(pipeline.pipeline(), cb_state.activeRenderPass->renderPass(), cb_state.commandBuffer()); |
| skip |= LogError(vuid.depth_read_only_06886, objlist, loc, |
| "depthWriteEnable is VK_TRUE, while the layout (%s) of " |
| "the depth aspect of the depth/stencil attachment in the render pass is read only.", |
| string_VkImageLayout(ds_attachment->layout)); |
| } |
| |
| VkStencilOpState front = last_bound_state.GetStencilOpStateFront(); |
| VkStencilOpState back = last_bound_state.GetStencilOpStateBack(); |
| |
| const bool all_keep_op = ((front.failOp == VK_STENCIL_OP_KEEP) && (front.passOp == VK_STENCIL_OP_KEEP) && |
| (front.depthFailOp == VK_STENCIL_OP_KEEP) && (back.failOp == VK_STENCIL_OP_KEEP) && |
| (back.passOp == VK_STENCIL_OP_KEEP) && (back.depthFailOp == VK_STENCIL_OP_KEEP)); |
| |
| const bool write_mask_enabled = (front.writeMask != 0) && (back.writeMask != 0); |
| |
| if (!all_keep_op && write_mask_enabled) { |
| const bool is_stencil_layout_read_only = [&]() { |
| // Look for potential dedicated stencil layout |
| if (const auto *stencil_layout = vku::FindStructInPNextChain<VkAttachmentReferenceStencilLayoutKHR>(ds_attachment->pNext); |
| stencil_layout) |
| return IsImageLayoutStencilReadOnly(stencil_layout->stencilLayout); |
| // Else depth and stencil share same layout |
| return IsImageLayoutStencilReadOnly(ds_attachment->layout); |
| }(); |
| |
| if (is_stencil_layout_read_only) { |
| const LogObjectList objlist(pipeline.pipeline(), cb_state.activeRenderPass->renderPass(), |
| cb_state.commandBuffer()); |
| skip |= LogError(vuid.stencil_read_only_06887, objlist, loc, |
| "The layout (%s) of the stencil aspect of the depth/stencil attachment in the render pass " |
| "is read only but not all stencil ops are VK_STENCIL_OP_KEEP.\n" |
| "front = { .failOp = %s, .passOp = %s , .depthFailOp = %s }\n" |
| "back = { .failOp = %s, .passOp = %s, .depthFailOp = %s }\n", |
| string_VkImageLayout(ds_attachment->layout), string_VkStencilOp(front.failOp), |
| string_VkStencilOp(front.passOp), string_VkStencilOp(front.depthFailOp), |
| string_VkStencilOp(back.failOp), string_VkStencilOp(back.passOp), |
| string_VkStencilOp(back.depthFailOp)); |
| } |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineDynamicRenderpassDraw(const LAST_BOUND_STATE &last_bound_state, const Location &loc) const { |
| bool skip = false; |
| const CMD_BUFFER_STATE &cb_state = last_bound_state.cb_state; |
| const PIPELINE_STATE *pipeline = last_bound_state.pipeline_state; |
| const DrawDispatchVuid &vuid = GetDrawDispatchVuid(loc.function); |
| const auto &rendering_info = cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info; |
| const auto &rp_state = pipeline->RenderPassState(); |
| if (rp_state) { |
| const auto rendering_view_mask = cb_state.activeRenderPass->GetDynamicRenderingViewMask(); |
| if (rp_state->renderPass() != VK_NULL_HANDLE) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), rp_state->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_06198, objlist, loc, |
| "Currently bound pipeline %s must have been created with a " |
| "VkGraphicsPipelineCreateInfo::renderPass equal to VK_NULL_HANDLE", |
| FormatHandle(*pipeline).c_str()); |
| } |
| |
| const auto pipeline_rendering_ci = rp_state->dynamic_rendering_pipeline_create_info; |
| |
| if (pipeline_rendering_ci.viewMask != rendering_view_mask) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_view_mask_06178, objlist, loc, |
| "Currently bound pipeline %s viewMask ([%" PRIu32 |
| ") must be equal to VkRenderingInfo::viewMask ([%" PRIu32 ")", |
| FormatHandle(*pipeline).c_str(), pipeline_rendering_ci.viewMask, rendering_view_mask); |
| } |
| |
| const auto color_attachment_count = pipeline_rendering_ci.colorAttachmentCount; |
| const auto rendering_color_attachment_count = cb_state.activeRenderPass->GetDynamicRenderingColorAttachmentCount(); |
| if (color_attachment_count && (color_attachment_count != rendering_color_attachment_count)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_color_count_06179, objlist, loc, |
| "Currently bound pipeline %s VkPipelineRenderingCreateInfo::colorAttachmentCount (%" PRIu32 |
| ") must be equal to VkRenderingInfo::colorAttachmentCount (%" PRIu32 ")", |
| FormatHandle(*pipeline).c_str(), pipeline_rendering_ci.colorAttachmentCount, |
| rendering_color_attachment_count); |
| } |
| |
| for (uint32_t i = 0; i < rendering_info.colorAttachmentCount; ++i) { |
| if (enabled_features.dynamic_rendering_unused_attachments_features.dynamicRenderingUnusedAttachments) { |
| if (rendering_info.pColorAttachments[i].imageView != VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[i].imageView); |
| 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])) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_unused_attachments_08911, objlist, loc, |
| "VkRenderingInfo::pColorAttachments[%" PRIu32 |
| "].imageView format (%s) must match 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.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_undefined_color_formats_08912, objlist, loc, |
| "VkRenderingInfo::pColorAttachments[%" PRIu32 |
| "].imageView is VK_NULL_HANDLE, but corresponding format in " |
| "VkPipelineRenderingCreateInfo::pColorAttachmentFormats[%" PRIu32 "] is %s.", |
| i, i, string_VkFormat(pipeline_rendering_ci.pColorAttachmentFormats[i])); |
| } |
| } else { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[i].imageView); |
| if ((pipeline_rendering_ci.colorAttachmentCount > i) && |
| view_state->create_info.format != pipeline_rendering_ci.pColorAttachmentFormats[i]) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_color_formats_08910, objlist, loc, |
| "VkRenderingInfo::pColorAttachments[%" PRIu32 |
| "].imageView format (%s) must match 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.dynamic_rendering_unused_attachments_features.dynamicRenderingUnusedAttachments) { |
| if (rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView); |
| if ((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.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_unused_attachments_08915, objlist, loc, |
| "VkRenderingInfo::pDepthAttachment->imageView format (%s) must match 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.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= |
| LogError(vuid.dynamic_rendering_undefined_depth_format_08916, objlist, loc, |
| "VkRenderingInfo::pDepthAttachment.imageView is VK_NULL_HANDLE, but corresponding format in " |
| "VkPipelineRenderingCreateInfo::depthAttachmentFormat is %s.", |
| string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat)); |
| } |
| } else { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView); |
| if (view_state->create_info.format != pipeline_rendering_ci.depthAttachmentFormat) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_depth_format_08914, objlist, loc, |
| "VkRenderingInfo::pDepthAttachment->imageView format (%s) must match corresponding format " |
| "in VkPipelineRenderingCreateInfo::depthAttachmentFormat (%s)", |
| string_VkFormat(view_state->create_info.format), |
| string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat)); |
| } |
| } |
| } |
| } else if (cb_state.activeRenderPass->use_dynamic_rendering_inherited) { |
| if (cb_state.activeRenderPass->inheritance_rendering_info.depthAttachmentFormat != |
| pipeline_rendering_ci.depthAttachmentFormat) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_depth_format_08914, objlist, loc, |
| "VkCommandBufferInheritanceRenderingInfo::depthAttachmentFormat (%s) must match corresponding " |
| "format in VkPipelineRenderingCreateInfo::depthAttachmentFormat (%s)", |
| string_VkFormat(cb_state.activeRenderPass->inheritance_rendering_info.depthAttachmentFormat), |
| string_VkFormat(pipeline_rendering_ci.depthAttachmentFormat)); |
| } |
| } |
| |
| if (rendering_info.pStencilAttachment) { |
| if (enabled_features.dynamic_rendering_unused_attachments_features.dynamicRenderingUnusedAttachments) { |
| if (rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView); |
| if ((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.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_unused_attachments_08918, objlist, loc, |
| "VkRenderingInfo::pStencilAttachment->imageView format (%s) must match 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.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_undefined_stencil_format_08916, objlist, loc, |
| "VkRenderingInfo::pStencilAttachment.imageView is VK_NULL_HANDLE, but " |
| "corresponding format in " |
| "VkPipelineRenderingCreateInfo::stencilAttachmentFormat is %s.", |
| string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat)); |
| } |
| } else { |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView); |
| if (view_state->create_info.format != pipeline_rendering_ci.stencilAttachmentFormat) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_stencil_format_08917, objlist, loc, |
| "VkRenderingInfo::pStencilAttachment->imageView format (%s) must match corresponding " |
| "format in VkPipelineRenderingCreateInfo::stencilAttachmentFormat (%s)", |
| string_VkFormat(view_state->create_info.format), |
| string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat)); |
| } |
| } |
| } |
| } else if (cb_state.activeRenderPass->use_dynamic_rendering_inherited) { |
| if (cb_state.activeRenderPass->inheritance_rendering_info.stencilAttachmentFormat != |
| pipeline_rendering_ci.stencilAttachmentFormat) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_stencil_format_08917, objlist, loc, |
| "VkCommandBufferInheritanceRenderingInfo::stencilAttachmentFormat (%s) must match corresponding " |
| "format in VkPipelineRenderingCreateInfo::stencilAttachmentFormat (%s)", |
| string_VkFormat(cb_state.activeRenderPass->inheritance_rendering_info.stencilAttachmentFormat), |
| string_VkFormat(pipeline_rendering_ci.stencilAttachmentFormat)); |
| } |
| } |
| |
| auto rendering_fragment_shading_rate_attachment_info = |
| vku::FindStructInPNextChain<VkRenderingFragmentShadingRateAttachmentInfoKHR>(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_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_fsr_06183, objlist, loc, |
| "Currently bound graphics pipeline %s must have been created with " |
| "VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR", |
| FormatHandle(*pipeline).c_str()); |
| } |
| } |
| |
| auto rendering_fragment_shading_rate_density_map = |
| vku::FindStructInPNextChain<VkRenderingFragmentDensityMapAttachmentInfoEXT>(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.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_fdm_06184, objlist, 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()); |
| } |
| } |
| |
| if ((pipeline->active_shaders & VK_SHADER_STAGE_FRAGMENT_BIT) != 0) { |
| for (uint32_t i = 0; i < rendering_info.colorAttachmentCount; ++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 && |
| rendering_info.pColorAttachments[i].imageView != VK_NULL_HANDLE) { |
| if (i >= pipeline_rendering_ci.colorAttachmentCount || |
| pipeline_rendering_ci.pColorAttachmentFormats[i] == VK_FORMAT_UNDEFINED) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass(), |
| rendering_info.pColorAttachments[i].imageView); |
| skip |= LogError(vuid.color_attachment_08963, objlist, 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(rendering_info.pColorAttachments[i].imageView).c_str(), |
| FormatHandle(*pipeline).c_str(), i); |
| } |
| } |
| } |
| } |
| if (last_bound_state.IsDepthTestEnable() && last_bound_state.IsDepthWriteEnable() && rp_state->use_dynamic_rendering && |
| rendering_info.pDepthAttachment && rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| if (pipeline_rendering_ci.depthAttachmentFormat == VK_FORMAT_UNDEFINED) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.depth_attachment_08964, objlist, loc, |
| "VkRenderingInfo::pDepthAttachment is %s, but currently bound graphics pipeline %s was created " |
| "with VkPipelineRenderingCreateInfo::depthAttachmentFormat equal to VK_FORMAT_UNDEFINED", |
| FormatHandle(rendering_info.pDepthAttachment->imageView).c_str(), FormatHandle(*pipeline).c_str()); |
| } |
| } |
| if (last_bound_state.IsStencilTestEnable() && rp_state->use_dynamic_rendering && rendering_info.pStencilAttachment && |
| rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| if (pipeline_rendering_ci.stencilAttachmentFormat == VK_FORMAT_UNDEFINED) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= |
| LogError(vuid.stencil_attachment_08965, objlist, loc, |
| "VkRenderingInfo::pStencilAttachment is %s, but currently bound graphics pipeline %s was created with " |
| "VkPipelineRenderingCreateInfo::stencilAttachmentFormat equal to VK_FORMAT_UNDEFINED", |
| FormatHandle(rendering_info.pStencilAttachment->imageView).c_str(), FormatHandle(*pipeline).c_str()); |
| } |
| } |
| } |
| |
| // VkAttachmentSampleCountInfoAMD == VkAttachmentSampleCountInfoNV |
| auto p_attachment_sample_count_info = vku::FindStructInPNextChain<VkAttachmentSampleCountInfoAMD>(pipeline->PNext()); |
| |
| if (p_attachment_sample_count_info) { |
| for (uint32_t i = 0; i < rendering_info.colorAttachmentCount; ++i) { |
| if (rendering_info.pColorAttachments[i].imageView == VK_NULL_HANDLE) { |
| continue; |
| } |
| auto color_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[i].imageView); |
| auto color_image_samples = Get<IMAGE_STATE>(color_view_state->create_info.image)->createInfo.samples; |
| |
| if (p_attachment_sample_count_info && |
| (color_image_samples != p_attachment_sample_count_info->pColorAttachmentSamples[i])) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_color_sample_06185, objlist, loc, |
| "Color attachment (%" PRIu32 |
| ") sample count (%s) must match corresponding VkAttachmentSampleCountInfoAMD " |
| "sample count (%s)", |
| i, string_VkSampleCountFlagBits(color_image_samples), |
| string_VkSampleCountFlagBits(p_attachment_sample_count_info->pColorAttachmentSamples[i])); |
| } |
| } |
| |
| if (rendering_info.pDepthAttachment != nullptr) { |
| auto depth_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView); |
| auto depth_image_samples = Get<IMAGE_STATE>(depth_view_state->create_info.image)->createInfo.samples; |
| |
| if (p_attachment_sample_count_info) { |
| if (depth_image_samples != p_attachment_sample_count_info->depthStencilAttachmentSamples) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_depth_sample_06186, objlist, loc, |
| "Depth attachment sample count (%s) must match corresponding " |
| "VkAttachmentSampleCountInfoAMD sample " |
| "count (%s)", |
| string_VkSampleCountFlagBits(depth_image_samples), |
| string_VkSampleCountFlagBits(p_attachment_sample_count_info->depthStencilAttachmentSamples)); |
| } |
| } |
| } |
| |
| if (rendering_info.pStencilAttachment != nullptr) { |
| auto stencil_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView); |
| auto stencil_image_samples = Get<IMAGE_STATE>(stencil_view_state->create_info.image)->createInfo.samples; |
| |
| if (p_attachment_sample_count_info) { |
| if (stencil_image_samples != p_attachment_sample_count_info->depthStencilAttachmentSamples) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_stencil_sample_06187, objlist, loc, |
| "Stencil attachment sample count (%s) must match corresponding " |
| "VkAttachmentSampleCountInfoAMD " |
| "sample count (%s)", |
| string_VkSampleCountFlagBits(stencil_image_samples), |
| string_VkSampleCountFlagBits(p_attachment_sample_count_info->depthStencilAttachmentSamples)); |
| } |
| } |
| } |
| } else if (!enabled_features.multisampled_render_to_single_sampled_features.multisampledRenderToSingleSampled) { |
| const VkSampleCountFlagBits rasterization_samples = last_bound_state.GetRasterizationSamples(); |
| for (uint32_t i = 0; i < rendering_info.colorAttachmentCount; ++i) { |
| if (rendering_info.pColorAttachments[i].imageView == VK_NULL_HANDLE) { |
| continue; |
| } |
| auto view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pColorAttachments[i].imageView); |
| auto samples = Get<IMAGE_STATE>(view_state->create_info.image)->createInfo.samples; |
| |
| if (samples != rasterization_samples) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_07285, objlist, loc, |
| "Color attachment (%" PRIu32 |
| ") sample count (%s) must match corresponding VkPipelineMultisampleStateCreateInfo " |
| "sample count (%s)", |
| i, string_VkSampleCountFlagBits(samples), string_VkSampleCountFlagBits(rasterization_samples)); |
| } |
| } |
| |
| if ((rendering_info.pDepthAttachment != nullptr) && (rendering_info.pDepthAttachment->imageView != VK_NULL_HANDLE)) { |
| const auto &depth_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pDepthAttachment->imageView); |
| const auto &depth_image_samples = Get<IMAGE_STATE>(depth_view_state->create_info.image)->createInfo.samples; |
| if (depth_image_samples != rasterization_samples) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_07286, objlist, loc, |
| "Depth attachment sample count (%s) must match corresponding " |
| "VkPipelineMultisampleStateCreateInfo::rasterizationSamples count (%s)", |
| string_VkSampleCountFlagBits(depth_image_samples), |
| string_VkSampleCountFlagBits(rasterization_samples)); |
| } |
| } |
| |
| if ((rendering_info.pStencilAttachment != nullptr) && (rendering_info.pStencilAttachment->imageView != VK_NULL_HANDLE)) { |
| const auto &stencil_view_state = Get<IMAGE_VIEW_STATE>(rendering_info.pStencilAttachment->imageView); |
| const auto &stencil_image_samples = Get<IMAGE_STATE>(stencil_view_state->create_info.image)->createInfo.samples; |
| if (stencil_image_samples != rasterization_samples) { |
| const LogObjectList objlist(cb_state.commandBuffer(), pipeline->pipeline(), |
| cb_state.activeRenderPass->renderPass()); |
| skip |= LogError(vuid.dynamic_rendering_07287, objlist, loc, |
| "Stencil attachment sample count (%s) must match corresponding " |
| "VkPipelineMultisampleStateCreateInfo::rasterizationSamples count (%s)", |
| string_VkSampleCountFlagBits(stencil_image_samples), |
| string_VkSampleCountFlagBits(rasterization_samples)); |
| } |
| } |
| } |
| return skip; |
| } |
| |
| bool CoreChecks::ValidatePipelineVertexDivisors(const safe_VkPipelineVertexInputStateCreateInfo &input_state, |
| const std::vector<VkVertexInputBindingDescription> &binding_descriptions, |
| const Location &loc) const { |
| bool skip = false; |
| const auto divisor_state_info = vku::FindStructInPNextChain<VkPipelineVertexInputDivisorStateCreateInfoEXT>(input_state.pNext); |
| if (divisor_state_info) { |
| const VkPhysicalDeviceLimits *device_limits = &phys_dev_props.limits; |
| for (uint32_t j = 0; j < divisor_state_info->vertexBindingDivisorCount; j++) { |
| const Location divisor_loc = |
| loc.pNext(Struct::VkVertexInputBindingDivisorDescriptionEXT, Field::pVertexBindingDivisors, j); |
| const auto *vibdd = &(divisor_state_info->pVertexBindingDivisors[j]); |
| if (vibdd->binding >= device_limits->maxVertexInputBindings) { |
| skip |= |
| LogError("VUID-VkVertexInputBindingDivisorDescriptionEXT-binding-01869", device, |
| divisor_loc.dot(Field::binding), "(%" PRIu32 ") exceeds device maxVertexInputBindings (%" PRIu32 ").", |
| vibdd->binding, device_limits->maxVertexInputBindings); |
| } |
| if (vibdd->divisor > phys_dev_ext_props.vtx_attrib_divisor_props.maxVertexAttribDivisor) { |
| skip |= |
| LogError("VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870", device, |
| divisor_loc.dot(Field::divisor), "(%" PRIu32 ") exceeds device maxVertexAttribDivisor (%" PRIu32 ").", |
| vibdd->divisor, phys_dev_ext_props.vtx_attrib_divisor_props.maxVertexAttribDivisor); |
| } |
| if ((0 == vibdd->divisor) && !enabled_features.vtx_attrib_divisor_features.vertexAttributeInstanceRateZeroDivisor) { |
| skip |= LogError("VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228", |
| device, divisor_loc.dot(Field::divisor), |
| "is (%" PRIu32 ") but vertexAttributeInstanceRateZeroDivisor feature was not enabled.", |
| vibdd->divisor); |
| } |
| if ((1 != vibdd->divisor) && !enabled_features.vtx_attrib_divisor_features.vertexAttributeInstanceRateDivisor) { |
| skip |= |
| LogError("VUID-VkVertexInputBindingDivisorDescriptionEXT-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 failed_01871 = true; |
| for (size_t k = 0; k < binding_descriptions.size(); k++) { |
| if ((vibdd->binding == binding_descriptions[k].binding) && |
| (VK_VERTEX_INPUT_RATE_INSTANCE == binding_descriptions[k].inputRate)) { |
| failed_01871 = false; |
| break; |
| } |
| } |
| if (failed_01871) { // Description not found, or has incorrect inputRate value |
| skip |= LogError("VUID-VkVertexInputBindingDivisorDescriptionEXT-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 >= 1 && flags_count <= 2) { |
| // 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<PIPELINE_STATE>(link_info.pLibraries[i]); |
| const auto lib_rendering_struct = lib->GetPipelineRenderingCreateInfo(); |
| const bool other_flag = (lib->graphics_lib_type & flags) && (lib->graphics_lib_type & ~lib_flags); |
| if (other_flag) { |
| if (current_pipeline) { |
| if (lib->GetCreateInfo<VkGraphicsPipelineCreateInfo>().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_struct ? lib_rendering_struct->viewMask : 0; |
| if (view_mask != lib_view_mask) { |
| skip |= LogError(vuid, device, loc, |
| "pLibraries[%" PRIu32 "] is (flags = %s and viewMask = %" PRIu32 "), but pLibraries[%" PRIu32 |
| "] is (flags = %s and viewMask %" PRIu32 ").", |
| lib_index, string_VkGraphicsPipelineLibraryFlagsEXT(lib_flags).c_str(), view_mask, i, |
| string_VkGraphicsPipelineLibraryFlagsEXT(lib->graphics_lib_type).c_str(), lib_view_mask); |
| } |
| } |
| } |
| } |
| |
| return skip; |
| } |