blob: f085c9eb3a12ff6395b688b8fe5c4b56dd301b9a [file] [log] [blame]
/* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Copyright (C) 2015-2026 Google Inc.
* Modifications Copyright (C) 2020,2025-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "state_tracker/pipeline_state.h"
#include <vulkan/vulkan_core.h>
#include "error_message/error_location.h"
#include "generated/dynamic_state_helper.h"
#include "state_tracker/descriptor_sets.h"
#include "state_tracker/cmd_buffer_state.h"
#include "state_tracker/pipeline_layout_state.h"
#include "state_tracker/render_pass_state.h"
#include "state_tracker/state_object.h"
#include "chassis/chassis_modification_state.h"
namespace vvl {
static vku::safe_VkGraphicsPipelineCreateInfo MakeGraphicsCreateInfo(const VkGraphicsPipelineCreateInfo &ci,
std::shared_ptr<const vvl::RenderPass> rpstate,
const DeviceState &state_data) {
bool use_color = false;
bool use_depth_stencil = false;
if (ci.renderPass == VK_NULL_HANDLE) {
auto dynamic_rendering = vku::FindStructInPNextChain<VkPipelineRenderingCreateInfo>(ci.pNext);
if (dynamic_rendering) {
use_color = (dynamic_rendering->colorAttachmentCount > 0);
use_depth_stencil = (dynamic_rendering->depthAttachmentFormat != VK_FORMAT_UNDEFINED) ||
(dynamic_rendering->stencilAttachmentFormat != VK_FORMAT_UNDEFINED);
} else {
// if this is true, will be caught later by VU
use_color = ci.pColorBlendState && ci.pColorBlendState->attachmentCount > 0;
}
} else if (rpstate) {
use_color = rpstate->UsesColorAttachment(ci.subpass);
use_depth_stencil = rpstate->UsesDepthStencilAttachment(ci.subpass);
}
vku::PNextCopyState copy_state = {
[&state_data, &ci](VkBaseOutStructure *safe_struct, const VkBaseOutStructure *in_struct) -> bool {
return Pipeline::PnextRenderingInfoCustomCopy(state_data, ci, safe_struct, in_struct);
}};
return vku::safe_VkGraphicsPipelineCreateInfo(&ci, use_color, use_depth_stencil, &copy_state);
}
static std::shared_ptr<vvl::ShaderModule> GetShaderModuleFromInlinedSpirv(
const DeviceState &state_data, const vku::safe_VkPipelineShaderStageCreateInfo &shader_stage_ci,
spirv::StatelessData *stateless_data) {
// Using VkShaderModuleCreateInfo to inline SPIR-V with VK_KHR_maintenance5
if (const auto shader_ci = vku::FindStructInPNextChain<VkShaderModuleCreateInfo>(shader_stage_ci.pNext)) {
// TODO - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10029
// This works for Compute because there is a single shader, but stateless_data will need to be indexed into
// We might have to remove (or increase) kCommonMaxGraphicsShaderStages as well
auto spirv_module =
vvl::CreateSpirvModuleState(shader_ci->codeSize, shader_ci->pCode, state_data.global_settings, stateless_data);
if (stateless_data) {
stateless_data->pipeline_pnext_module = spirv_module;
}
return std::make_shared<vvl::ShaderModule>(VK_NULL_HANDLE, spirv_module);
} else {
// VK_EXT_shader_module_identifier could legally provide a null module handle
return std::make_shared<vvl::ShaderModule>();
}
}
uint32_t Pipeline::CountDescriptorHeapEmbeddedSamplers(const Pipeline& pipe_state) {
uint32_t count = 0;
if (pipe_state.descriptor_heap_mode) {
for (uint32_t i = 0; i < pipe_state.shader_stages_ci.size(); ++i) {
const auto& stage_ci = pipe_state.shader_stages_ci[i];
count += ::CountDescriptorHeapEmbeddedSamplers(stage_ci.pNext);
}
}
return count;
}
std::vector<ShaderStageState> Pipeline::GetStageStates(const DeviceState &state_data, const Pipeline &pipe_state,
VkPipelineLayout pipeline_layout, spirv::StatelessData *stateless_data) {
std::vector<ShaderStageState> stage_states;
std::vector<VkShaderStageFlagBits> lookup_in_library_stages = {VK_SHADER_STAGE_VERTEX_BIT,
VK_SHADER_STAGE_FRAGMENT_BIT,
VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
VK_SHADER_STAGE_GEOMETRY_BIT,
VK_SHADER_STAGE_TASK_BIT_EXT,
VK_SHADER_STAGE_MESH_BIT_EXT};
// Because GPL, we can't just "know our pipeline layout" here and have to do a state lookup to get it
const DescriptorSetLayoutList *descriptor_set_layouts = nullptr;
if (const auto pipeline_layout_state = state_data.Get<vvl::PipelineLayout>(pipeline_layout)) {
descriptor_set_layouts = &pipeline_layout_state->set_layouts;
}
for (size_t stage_index = 0; stage_index < pipe_state.shader_stages_ci.size(); ++stage_index) {
if (pipe_state.pipeline_type == VK_PIPELINE_BIND_POINT_GRAPHICS &&
!pipe_state.OwnsLibState(pipe_state.fragment_shader_state) && !pipe_state.OwnsLibState(pipe_state.pre_raster_state)) {
continue; // pStages are ignored if not using one of these libraries
}
const auto &stage_ci = pipe_state.shader_stages_ci[stage_index];
auto module_state = state_data.Get<vvl::ShaderModule>(stage_ci.module);
if (!module_state && pipe_state.pipeline_cache) {
// Attempt to look up the pipeline cache for shader module data
module_state = pipe_state.pipeline_cache->GetStageModule(pipe_state, stage_index);
}
if (!module_state) {
// See if the module is referenced in a library state
module_state = pipe_state.GetGraphicsLibraryStateShader(stage_ci.stage);
}
if (!module_state || !module_state->spirv) {
// Inlined Graphics shaders will still use GetLibraryStateShader to get module_state
// We can hit this only if using GPL and an invalid stage is passed in, the error will be caught elsewhere
if (pipe_state.pipeline_type == VK_PIPELINE_BIND_POINT_GRAPHICS) {
continue;
}
module_state = GetShaderModuleFromInlinedSpirv(state_data, stage_ci, stateless_data);
}
stage_states.emplace_back(&stage_ci, nullptr, descriptor_set_layouts, module_state, module_state->spirv, pipeline_layout,
pipe_state.descriptor_heap_mode);
// If stage was found, do not try to look for it in library
auto found_stage = std::find(lookup_in_library_stages.begin(), lookup_in_library_stages.end(), stage_ci.stage);
if (found_stage != lookup_in_library_stages.end()) {
const size_t last_library_stage_i = lookup_in_library_stages.size() - 1;
std::swap(*found_stage, lookup_in_library_stages[last_library_stage_i]);
lookup_in_library_stages.resize(last_library_stage_i);
}
}
for (const auto &stage_flag : lookup_in_library_stages) {
// Check if stage has been supplied by a library
std::shared_ptr<const vvl::ShaderModule> module_state = nullptr;
const vku::safe_VkPipelineShaderStageCreateInfo *stage_ci = nullptr;
switch (stage_flag) {
case VK_SHADER_STAGE_VERTEX_BIT:
if (pipe_state.pre_raster_state && pipe_state.pre_raster_state->vertex_shader) {
module_state = pipe_state.pre_raster_state->vertex_shader;
stage_ci = pipe_state.pre_raster_state->vertex_shader_ci;
}
break;
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
if (pipe_state.pre_raster_state && pipe_state.pre_raster_state->tessc_shader) {
module_state = pipe_state.pre_raster_state->tessc_shader;
stage_ci = pipe_state.pre_raster_state->tessc_shader_ci;
}
break;
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
if (pipe_state.pre_raster_state && pipe_state.pre_raster_state->tesse_shader) {
module_state = pipe_state.pre_raster_state->tesse_shader;
stage_ci = pipe_state.pre_raster_state->tesse_shader_ci;
}
break;
case VK_SHADER_STAGE_GEOMETRY_BIT:
if (pipe_state.pre_raster_state && pipe_state.pre_raster_state->geometry_shader) {
module_state = pipe_state.pre_raster_state->geometry_shader;
stage_ci = pipe_state.pre_raster_state->geometry_shader_ci;
}
break;
case VK_SHADER_STAGE_TASK_BIT_EXT:
if (pipe_state.pre_raster_state && pipe_state.pre_raster_state->task_shader) {
module_state = pipe_state.pre_raster_state->task_shader;
stage_ci = pipe_state.pre_raster_state->task_shader_ci;
}
break;
case VK_SHADER_STAGE_MESH_BIT_EXT:
if (pipe_state.pre_raster_state && pipe_state.pre_raster_state->mesh_shader) {
module_state = pipe_state.pre_raster_state->mesh_shader;
stage_ci = pipe_state.pre_raster_state->mesh_shader_ci;
}
break;
case VK_SHADER_STAGE_FRAGMENT_BIT:
if (pipe_state.fragment_shader_state && pipe_state.fragment_shader_state->fragment_shader) {
module_state = pipe_state.fragment_shader_state->fragment_shader;
stage_ci = pipe_state.fragment_shader_state->fragment_shader_ci.get();
}
break;
default:
// no-op
break;
}
if (!stage_ci) {
continue;
}
stage_states.emplace_back(stage_ci, nullptr, descriptor_set_layouts, module_state, module_state->spirv, pipeline_layout,
pipe_state.descriptor_heap_mode);
}
return stage_states;
}
std::vector<ShaderStageState> Pipeline::GetDataGraphStageStates(const DeviceState &state_data, Pipeline &pipe_state, VkPipelineLayout pipeline_layout, spirv::StatelessData *stateless_data) {
assert(VK_STRUCTURE_TYPE_DATA_GRAPH_PIPELINE_CREATE_INFO_ARM == pipe_state.GetCreateInfoSType());
std::vector<ShaderStageState> stage_states;
auto create_info = pipe_state.DataGraphCreateInfo();
const DescriptorSetLayoutList *descriptor_set_layouts = nullptr;
if (const auto pipeline_layout_state = state_data.Get<vvl::PipelineLayout>(pipeline_layout)) {
descriptor_set_layouts = &pipeline_layout_state->set_layouts;
}
// The ShaderModule for a datagraph can be defined in 2 ways (but not both, see VU 9873):
// - as the 'module' member of the VkDataGraphPipelineShaderModuleCreateInfoARM structure
auto *dg_shader_ci = vku::FindStructInPNextChain<VkDataGraphPipelineShaderModuleCreateInfoARM>(create_info.pNext);
// - or with a VkShaderModuleCreateInfo
auto *shader_ci = vku::FindStructInPNextChain<VkShaderModuleCreateInfo>(create_info.pNext);
std::shared_ptr<const vvl::ShaderModule> module_state = nullptr;
if (dg_shader_ci && dg_shader_ci->module != VK_NULL_HANDLE) {
module_state = state_data.Get<vvl::ShaderModule>(dg_shader_ci->module);
} else if (shader_ci && shader_ci->pCode && shader_ci->codeSize > 0) {
auto spirv_module = vvl::CreateSpirvModuleState(shader_ci->codeSize, shader_ci->pCode, state_data.global_settings, stateless_data);
module_state = std::make_shared<const vvl::ShaderModule>(VK_NULL_HANDLE, spirv_module);
}
if (module_state) {
// The existence of dg_shader_ci is guaranteed if we have a module. Having dg_shader_ci == NULL is a very contrived situation,
// see test NegativeDataGraph.DataGraphWrongCreateInfoStructs
assert(dg_shader_ci);
pipe_state.data_graph_shader_stage_ci = vku::InitStructHelper();
pipe_state.data_graph_shader_stage_ci.module = module_state->VkHandle();
pipe_state.data_graph_shader_stage_ci.pName = dg_shader_ci->pName;
pipe_state.data_graph_shader_stage_ci.stage = VK_SHADER_STAGE_ALL;
vku::safe_VkPipelineShaderStageCreateInfo safe_stage_ci(&pipe_state.data_graph_shader_stage_ci);
stage_states.emplace_back(&safe_stage_ci, nullptr, descriptor_set_layouts, module_state, module_state->spirv,
pipeline_layout, pipe_state.descriptor_heap_mode);
}
return stage_states;
}
std::vector<ShaderStageState> Pipeline::GetRayTracingStageStates(
const DeviceState &state_data, const Pipeline &pipe_state, VkPipelineLayout pipeline_layout,
std::vector<spirv::StatelessData> *inout_per_shader_stateless_data) {
std::vector<ShaderStageState> stage_states;
// Duplicated code because we decided to form GetStageStates for RTX sadly
const DescriptorSetLayoutList *descriptor_set_layouts = nullptr;
if (const auto pipeline_layout_state = state_data.Get<vvl::PipelineLayout>(pipeline_layout)) {
descriptor_set_layouts = &pipeline_layout_state->set_layouts;
}
for (size_t stage_index = 0; stage_index < pipe_state.shader_stages_ci.size(); ++stage_index) {
const auto &stage_ci = pipe_state.shader_stages_ci[stage_index];
auto module_state = state_data.Get<vvl::ShaderModule>(stage_ci.module);
if (!module_state && pipe_state.pipeline_cache) {
// Attempt to look up the pipeline cache for shader module data
module_state = pipe_state.pipeline_cache->GetStageModule(pipe_state, stage_index);
}
if (!module_state || !module_state->spirv) {
if (const auto shader_ci = vku::FindStructInPNextChain<VkShaderModuleCreateInfo>(stage_ci.pNext)) {
(void)shader_ci;
inout_per_shader_stateless_data->resize(inout_per_shader_stateless_data->size() + 1);
spirv::StatelessData *stateless_data =
&inout_per_shader_stateless_data->at(inout_per_shader_stateless_data->size() - 1);
module_state = GetShaderModuleFromInlinedSpirv(state_data, stage_ci, stateless_data);
}
}
if (module_state) {
stage_states.emplace_back(&stage_ci, nullptr, descriptor_set_layouts, module_state, module_state->spirv,
pipeline_layout, pipe_state.descriptor_heap_mode);
}
}
const vku::safe_VkRayTracingPipelineCreateInfoCommon &rt_ci = pipe_state.RayTracingCreateInfo();
if (rt_ci.pLibraryInfo) {
for (VkPipeline lib : vvl::make_span(rt_ci.pLibraryInfo->pLibraries, rt_ci.pLibraryInfo->libraryCount)) {
auto lib_state = state_data.Get<vvl::Pipeline>(lib);
ASSERT_AND_CONTINUE(lib_state);
std::vector<ShaderStageState> lib_stage_stages =
GetRayTracingStageStates(state_data, *lib_state, pipeline_layout, inout_per_shader_stateless_data);
for (size_t i = 0; i < lib_stage_stages.size(); ++i) {
stage_states.emplace_back(lib_stage_stages[i]);
}
}
}
return stage_states;
}
static uint32_t GetCreateInfoShaders(const Pipeline &pipe_state) {
uint32_t result = 0;
if (pipe_state.pipeline_type == VK_PIPELINE_BIND_POINT_GRAPHICS && !pipe_state.OwnsLibState(pipe_state.fragment_shader_state) &&
!pipe_state.OwnsLibState(pipe_state.pre_raster_state)) {
return result; // pStages are ignored if not using one of these libraries
}
for (const auto &stage_ci : pipe_state.shader_stages_ci) {
result |= stage_ci.stage;
}
return result;
}
static uint32_t GetLinkingShaders(const VkPipelineLibraryCreateInfoKHR *link_info, const DeviceState &state_data) {
uint32_t result = 0;
if (link_info) {
for (uint32_t i = 0; i < link_info->libraryCount; ++i) {
const auto &state = state_data.Get<vvl::Pipeline>(link_info->pLibraries[i]);
if (state) {
result |= state->active_shaders;
}
}
}
return result;
}
static CBDynamicFlags GetGraphicsDynamicState(Pipeline &pipe_state) {
CBDynamicFlags flags = 0;
// "Dynamic state values set via pDynamicState must be ignored if the state they correspond to is not otherwise statically set
// by one of the state subsets used to create the pipeline."
//
// we only care here if the pipeline was created with the library, not linked
const bool has_vertex_input_state = pipe_state.OwnsLibState(pipe_state.vertex_input_state);
const bool has_pre_raster_state = pipe_state.OwnsLibState(pipe_state.pre_raster_state);
const bool has_fragment_shader_state = pipe_state.OwnsLibState(pipe_state.fragment_shader_state);
const bool has_fragment_output_state = pipe_state.OwnsLibState(pipe_state.fragment_output_state);
const auto *dynamic_state_ci = pipe_state.GraphicsCreateInfo().pDynamicState;
if (dynamic_state_ci) {
for (const VkDynamicState vk_dynamic_state :
vvl::make_span(dynamic_state_ci->pDynamicStates, dynamic_state_ci->dynamicStateCount)) {
// Check if should ignore or not before converting and adding
switch (vk_dynamic_state) {
// VkPipelineVertexInputStateCreateInfo
case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
case VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE:
// VkPipelineInputAssemblyStateCreateInfo
case VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY:
case VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE: {
if (has_vertex_input_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
// VkPipelineViewportStateCreateInfo
case VK_DYNAMIC_STATE_VIEWPORT:
case VK_DYNAMIC_STATE_SCISSOR:
case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
case VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV:
case VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV:
case VK_DYNAMIC_STATE_SHADING_RATE_IMAGE_ENABLE_NV:
case VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV:
case VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_ENABLE_NV:
case VK_DYNAMIC_STATE_VIEWPORT_SWIZZLE_NV:
case VK_DYNAMIC_STATE_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT:
case VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_ENABLE_NV:
case VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV:
// VkPipelineRasterizationStateCreateInfo
case VK_DYNAMIC_STATE_LINE_WIDTH:
case VK_DYNAMIC_STATE_DEPTH_BIAS:
case VK_DYNAMIC_STATE_CULL_MODE:
case VK_DYNAMIC_STATE_FRONT_FACE:
case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE:
case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE:
case VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT:
case VK_DYNAMIC_STATE_POLYGON_MODE_EXT:
case VK_DYNAMIC_STATE_LINE_STIPPLE:
case VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT:
case VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT:
case VK_DYNAMIC_STATE_RASTERIZATION_STREAM_EXT:
case VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT:
case VK_DYNAMIC_STATE_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE_EXT:
case VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT:
case VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT:
case VK_DYNAMIC_STATE_DEPTH_CLAMP_RANGE_EXT:
// VkPipelineTessellationStateCreateInfo
case VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT:
case VK_DYNAMIC_STATE_TESSELLATION_DOMAIN_ORIGIN_EXT:
// VkPipelineDiscardRectangleStateCreateInfoEXT
case VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT:
case VK_DYNAMIC_STATE_DISCARD_RECTANGLE_ENABLE_EXT:
case VK_DYNAMIC_STATE_DISCARD_RECTANGLE_MODE_EXT: {
if (has_pre_raster_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
// VkPipelineFragmentShadingRateStateCreateInfoKHR
case VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR: {
if (has_pre_raster_state || has_fragment_shader_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
// VkPipelineDepthStencilStateCreateInfo
case VK_DYNAMIC_STATE_DEPTH_BOUNDS:
case VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE:
case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK:
case VK_DYNAMIC_STATE_STENCIL_WRITE_MASK:
case VK_DYNAMIC_STATE_STENCIL_REFERENCE:
case VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE:
case VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE:
case VK_DYNAMIC_STATE_DEPTH_COMPARE_OP:
case VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE:
case VK_DYNAMIC_STATE_STENCIL_OP:
// VkPipelineRepresentativeFragmentTestStateCreateInfoNV
case VK_DYNAMIC_STATE_REPRESENTATIVE_FRAGMENT_TEST_ENABLE_NV: {
if (has_fragment_shader_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
/// VkPipelineColorBlendStateCreateInfo
case VK_DYNAMIC_STATE_BLEND_CONSTANTS:
case VK_DYNAMIC_STATE_LOGIC_OP_EXT:
case VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT:
case VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT:
case VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT:
case VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT:
case VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT:
case VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT: {
if (has_fragment_output_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
// VkPipelineMultisampleStateCreateInfo
case VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT:
case VK_DYNAMIC_STATE_SAMPLE_MASK_EXT:
case VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT:
case VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT:
case VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT:
case VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT:
case VK_DYNAMIC_STATE_COVERAGE_TO_COLOR_ENABLE_NV:
case VK_DYNAMIC_STATE_COVERAGE_TO_COLOR_LOCATION_NV:
case VK_DYNAMIC_STATE_COVERAGE_MODULATION_MODE_NV:
case VK_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_ENABLE_NV:
case VK_DYNAMIC_STATE_COVERAGE_MODULATION_TABLE_NV:
case VK_DYNAMIC_STATE_COVERAGE_REDUCTION_MODE_NV: {
if (has_fragment_shader_state || has_fragment_output_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
// VkRenderPass and subpass parameter
case VK_DYNAMIC_STATE_ATTACHMENT_FEEDBACK_LOOP_ENABLE_EXT: {
if (has_pre_raster_state || has_fragment_shader_state || has_fragment_output_state) {
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
}
break;
}
// not valid and shouldn't see
case VK_DYNAMIC_STATE_RAY_TRACING_PIPELINE_STACK_SIZE_KHR:
case VK_DYNAMIC_STATE_MAX_ENUM:
assert(false);
break;
}
}
}
// apply linked library's dynamic state
if (!has_vertex_input_state && pipe_state.vertex_input_state) {
flags |= pipe_state.vertex_input_state->parent.dynamic_state;
}
if (!has_pre_raster_state && pipe_state.pre_raster_state) {
flags |= pipe_state.pre_raster_state->parent.dynamic_state;
}
if (!has_fragment_shader_state && pipe_state.fragment_shader_state) {
flags |= pipe_state.fragment_shader_state->parent.dynamic_state;
}
if (!has_fragment_output_state && pipe_state.fragment_output_state) {
flags |= pipe_state.fragment_output_state->parent.dynamic_state;
}
return flags;
}
// We "view" things as being dynamic if the pipeline does not have those shader stages, the spec says:
// "If the state is not included ... in the new pipeline object, then that command buffer state is not disturbed. For example, mesh
// shading pipelines do not include vertex input state and therefore do not disturb any such command buffer state."
static CBDynamicFlags GetIgnoredDynamicState(Pipeline &pipe_state) {
CBDynamicFlags flags = 0;
if ((pipe_state.active_shaders & VK_SHADER_STAGE_VERTEX_BIT) == 0) {
flags |= kVertexDynamicState;
}
if ((pipe_state.active_shaders & VK_SHADER_STAGE_FRAGMENT_BIT) == 0) {
flags |= kFragmentDynamicState;
}
if ((pipe_state.active_shaders & VK_SHADER_STAGE_GEOMETRY_BIT) == 0) {
flags |= kGeometryDynamicState;
}
if ((pipe_state.active_shaders & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) == 0) {
flags |= kTessControlDynamicState;
}
if ((pipe_state.active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) == 0) {
flags |= kTessEvalDynamicState;
}
return flags;
}
// This will only get the topology if possible
static VkPrimitiveTopology GetRasterizationInputTopology(const Pipeline &pipe_state, const DeviceState &state) {
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
if (!pipe_state.RasterizationState()) {
return topology;
}
// Get Clip Space Topology first
if (pipe_state.active_shaders & (VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_MESH_BIT_EXT)) {
for (const ShaderStageState &shader_stage_state : pipe_state.stage_states) {
if (shader_stage_state.GetStage() == VK_SHADER_STAGE_MESH_BIT_EXT ||
shader_stage_state.GetStage() == VK_SHADER_STAGE_GEOMETRY_BIT) {
if (shader_stage_state.spirv_state && shader_stage_state.entrypoint) {
topology = shader_stage_state.entrypoint->execution_mode.GetGeometryMeshOutputTopology();
break;
}
}
}
} else if (pipe_state.active_shaders & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
for (const ShaderStageState &shader_stage_state : pipe_state.stage_states) {
const VkShaderStageFlagBits stage = shader_stage_state.GetStage();
if (stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT || stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) {
if (shader_stage_state.spirv_state && shader_stage_state.entrypoint) {
if (shader_stage_state.entrypoint->execution_mode.Has(spirv::ExecutionModeSet::point_mode_bit)) {
// In tessellation shaders, PointMode is separate and trumps the tessellation topology.
// Can be found in both tessellation shaders
topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
} else if (stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
topology = shader_stage_state.entrypoint->execution_mode.GetTessellationEvalOutputTopology();
}
}
}
}
} else if (pipe_state.active_shaders & VK_SHADER_STAGE_VERTEX_BIT) {
if (pipe_state.IsDynamic(CB_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY) &&
state.phys_dev_ext_props.extended_dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted) {
return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; // will detect at draw time
}
if (!pipe_state.InputAssemblyState()) {
return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
}
topology = pipe_state.InputAssemblyState()->topology;
}
// Now apply the Polygon mode
VkPolygonMode polygon_mode = pipe_state.RasterizationState()->polygonMode;
// If we have point topology now, the polygon won't effect it
if (IsPointTopology(topology)) {
return topology;
} else if (pipe_state.IsDynamic(CB_DYNAMIC_STATE_POLYGON_MODE_EXT)) {
return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; // will detect at draw time
} else if (polygon_mode == VK_POLYGON_MODE_POINT && (IsLineTopology(topology) || IsTriangleTopology(topology))) {
topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
} else if (polygon_mode == VK_POLYGON_MODE_LINE && IsTriangleTopology(topology)) {
topology = TriangleToLineTopology(topology);
}
return topology;
}
static CBDynamicFlags GetRayTracingDynamicState(Pipeline &pipe_state) {
CBDynamicFlags flags = 0;
const auto *dynamic_state_ci = pipe_state.RayTracingCreateInfo().pDynamicState;
if (dynamic_state_ci) {
for (const VkDynamicState vk_dynamic_state :
vvl::make_span(dynamic_state_ci->pDynamicStates, dynamic_state_ci->dynamicStateCount)) {
switch (vk_dynamic_state) {
case VK_DYNAMIC_STATE_RAY_TRACING_PIPELINE_STACK_SIZE_KHR:
flags.set(ConvertToCBDynamicState(vk_dynamic_state));
break;
default:
break;
}
}
}
return flags;
}
static bool UsesPipelineRobustness(const void *pNext, const Pipeline &pipe_state) {
bool result = false;
const auto robustness_info = vku::FindStructInPNextChain<VkPipelineRobustnessCreateInfo>(pNext);
if (!robustness_info) {
return false;
}
result |= (robustness_info->storageBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2) ||
(robustness_info->storageBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS);
result |= (robustness_info->uniformBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2) ||
(robustness_info->uniformBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS);
if (!result) {
for (const auto &stage_ci : pipe_state.shader_stages_ci) {
const auto stage_robustness_info = vku::FindStructInPNextChain<VkPipelineRobustnessCreateInfo>(stage_ci.pNext);
if (stage_robustness_info) {
result |=
(stage_robustness_info->storageBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2) ||
(stage_robustness_info->storageBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS);
result |=
(stage_robustness_info->uniformBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2) ||
(stage_robustness_info->uniformBuffers == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS);
}
}
}
return result;
}
static bool UsesPipelineVertexRobustness(const void *pNext, const Pipeline &pipe_state) {
bool result = false;
const auto robustness_info = vku::FindStructInPNextChain<VkPipelineRobustnessCreateInfo>(pNext);
if (!robustness_info) {
return false;
}
result |= (robustness_info->vertexInputs == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2) ||
(robustness_info->vertexInputs == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS);
if (!result) {
for (const auto &stage_ci : pipe_state.shader_stages_ci) {
const auto stage_robustness_info = vku::FindStructInPNextChain<VkPipelineRobustnessCreateInfo>(stage_ci.pNext);
if (stage_robustness_info) {
result |= (stage_robustness_info->vertexInputs == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2) ||
(stage_robustness_info->vertexInputs == VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS);
}
}
}
return result;
}
static bool IgnoreColorAttachments(const DeviceState &state_data, Pipeline &pipe_state) {
// If the libraries used to create this pipeline are ignoring color attachments, this pipeline should as well
if (pipe_state.library_create_info) {
for (uint32_t i = 0; i < pipe_state.library_create_info->libraryCount; i++) {
const auto lib = state_data.Get<vvl::Pipeline>(pipe_state.library_create_info->pLibraries[i]);
if (lib->ignore_color_attachments) return true;
}
}
// According to the spec, pAttachments is to be ignored if the pipeline is created with
// VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT
// and VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT dynamic states set
return pipe_state.ColorBlendState() && (pipe_state.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT) &&
pipe_state.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT) &&
pipe_state.IsDynamic(CB_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT) &&
pipe_state.IsDynamic(CB_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT));
}
static bool UsesShaderModuleId(const Pipeline &pipe_state) {
if (pipe_state.shader_stages_ci.data() == nullptr) {
return false;
}
for (const auto &stage_ci : pipe_state.shader_stages_ci) {
// if using GPL, can have null pStages
if (stage_ci.ptr()) {
const auto module_id_info =
vku::FindStructInPNextChain<VkPipelineShaderStageModuleIdentifierCreateInfoEXT>(stage_ci.pNext);
if (module_id_info && (module_id_info->identifierSize > 0)) {
return true;
}
}
}
return false;
}
static vvl::unordered_set<uint32_t> GetFSOutputLocations(const std::vector<ShaderStageState> &stage_states) {
vvl::unordered_set<uint32_t> result;
for (const auto &stage_state : stage_states) {
if (!stage_state.entrypoint) {
continue;
}
if (stage_state.GetStage() == VK_SHADER_STAGE_FRAGMENT_BIT) {
for (const auto *variable : stage_state.entrypoint->user_defined_interface_variables) {
if ((variable->storage_class != spv::StorageClassOutput) || variable->interface_slots.empty()) {
continue; // not an output interface
}
// It is not allowed to have Block Fragment or 64-bit vectors output in Frag shader
// This means all Locations in slots will be the same
result.insert(variable->interface_slots[0].Location());
}
break; // found
}
}
return result;
}
static VkPipelineCreateFlags2 GetPipelineCreateFlags(const void *pNext, VkPipelineCreateFlags2 flags) {
const auto flags2 = vku::FindStructInPNextChain<VkPipelineCreateFlags2CreateInfo>(pNext);
if (flags2) {
return flags2->flags;
}
return flags;
}
const Location Pipeline::GetCreateFlagsLoc(const Location &create_info_loc) const {
if (vku::FindStructInPNextChain<VkPipelineCreateFlags2CreateInfo>(GetCreateInfoPNext())) {
return create_info_loc.pNext(Struct::VkPipelineCreateFlags2CreateInfo, Field::flags);
} else {
return create_info_loc.dot(Field::flags);
}
}
std::shared_ptr<VertexInputState> Pipeline::CreateVertexInputState(const Pipeline &p, const DeviceState &state,
const vku::safe_VkGraphicsPipelineCreateInfo &create_info) {
const auto lib_type = GetGraphicsLibType(create_info);
if (lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) {
// Creating a vertex input graphics library
return std::make_shared<VertexInputState>(p, create_info);
} else if (p.library_create_info) {
// Linking it in for final pipeline
auto ss = GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT>(state, *p.library_create_info);
// null if linking together 2 other libraries
if (ss) {
return ss;
}
} else if (lib_type == static_cast<VkGraphicsPipelineLibraryFlagsEXT>(0)) {
// Not a graphics library (normal pipeline creation)
return std::make_shared<VertexInputState>(p, create_info);
}
// Creating another pipeline library using this library
return {};
}
std::shared_ptr<PreRasterState> Pipeline::CreatePreRasterState(
const Pipeline &p, const DeviceState &state, const vku::safe_VkGraphicsPipelineCreateInfo &create_info,
const std::shared_ptr<const vvl::RenderPass> &rp, spirv::StatelessData stateless_data[kCommonMaxGraphicsShaderStages]) {
const auto lib_type = GetGraphicsLibType(create_info);
if (lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) {
// Creating a pre-raster graphics library
return std::make_shared<PreRasterState>(p, state, create_info, rp, stateless_data);
} else if (p.library_create_info) {
// Linking it in for final pipeline
auto ss = GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(state, *p.library_create_info);
// null if linking together 2 other libraries
if (ss) {
return ss;
}
} else if (lib_type == static_cast<VkGraphicsPipelineLibraryFlagsEXT>(0)) {
// Not a graphics library (normal pipeline creation)
return std::make_shared<PreRasterState>(p, state, create_info, rp, stateless_data);
}
// Creating another pipeline library using this library
return {};
}
std::shared_ptr<FragmentShaderState> Pipeline::CreateFragmentShaderState(
const Pipeline &p, const DeviceState &state, const VkGraphicsPipelineCreateInfo &create_info,
const vku::safe_VkGraphicsPipelineCreateInfo &safe_create_info, const std::shared_ptr<const vvl::RenderPass> &rp,
spirv::StatelessData stateless_data[kCommonMaxGraphicsShaderStages]) {
const auto lib_type = GetGraphicsLibType(create_info);
if (lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) {
// Creating a fragment shader graphics library
return std::make_shared<FragmentShaderState>(p, state, create_info, rp, stateless_data);
} else if (p.library_create_info) {
// Linking it in for final pipeline
auto ss = GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT>(state, *p.library_create_info);
// null if linking together 2 other libraries
if (ss && EnablesRasterizationStates(p.pre_raster_state)) {
return ss;
}
} else if ((lib_type == static_cast<VkGraphicsPipelineLibraryFlagsEXT>(0)) && EnablesRasterizationStates(p.pre_raster_state)) {
// Not a graphics library (normal pipeline creation)
//
// No fragment shader _should_ imply no fragment shader state, however, for historical (GL) reasons, a pipeline _can_
// be created with a VS but no FS and still have valid fragment shader state.
// See https://gitlab.khronos.org/vulkan/vulkan/-/issues/3178 for more details.
return std::make_shared<FragmentShaderState>(p, state, safe_create_info, rp, stateless_data);
}
// Creating another pipeline library using this library
return {};
}
// Pointers that should be ignored have been set to null in safe_create_info, but if this is a graphics library we need the "raw"
// create_info.
std::shared_ptr<FragmentOutputState> Pipeline::CreateFragmentOutputState(
const Pipeline &p, const DeviceState &state, const VkGraphicsPipelineCreateInfo &create_info,
const vku::safe_VkGraphicsPipelineCreateInfo &safe_create_info, const std::shared_ptr<const vvl::RenderPass> &rp) {
// If this pipeline is being created a non-executable (i.e., does not contain complete state) pipeline with FO state, then
// unconditionally set this pipeline's FO state.
const auto lib_type = GetGraphicsLibType(create_info);
if (lib_type & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) {
// Creating a fragment output graphics library
return std::make_shared<FragmentOutputState>(p, create_info, rp);
} else if (p.library_create_info) {
// Linking it in for final pipeline
//
// If this pipeline is linking in a library that contains FO state, check to see if the FO state is valid before creating it
// for this pipeline
auto ss = GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT>(state, *p.library_create_info);
// null if linking together 2 other libraries
if (ss && EnablesRasterizationStates(p.pre_raster_state)) {
return ss;
}
} else if ((lib_type == static_cast<VkGraphicsPipelineLibraryFlagsEXT>(0)) && EnablesRasterizationStates(p.pre_raster_state)) {
// Not a graphics library (normal pipeline creation)
return std::make_shared<FragmentOutputState>(p, safe_create_info, rp);
}
// Creating another pipeline library using this library
return {};
}
std::vector<std::shared_ptr<const vvl::PipelineLayout>> Pipeline::PipelineLayoutStateUnion() const {
std::vector<std::shared_ptr<const vvl::PipelineLayout>> ret;
ret.reserve(2);
// Only need to check pre-raster _or_ fragment shader layout; if either one is not merged_graphics_layout, then
// merged_graphics_layout is a union
if (pre_raster_state) {
if (pre_raster_state->pipeline_layout != fragment_shader_state->pipeline_layout) {
return {pre_raster_state->pipeline_layout, fragment_shader_state->pipeline_layout};
} else {
return {pre_raster_state->pipeline_layout};
}
}
return {merged_graphics_layout};
}
// See DeviceState::PreCallValidateCreateGraphicsPipelines() why we need this awful function
const VkPipelineRenderingCreateInfo& Pipeline::GetRenderPassPipelineRenderingCreateInfo() const {
return *(rp_state->dynamic_pipeline_rendering_create_info.ptr());
}
// Currently will return vvl::ShaderModule with no SPIR-V
std::shared_ptr<const vvl::ShaderModule> Pipeline::GetGraphicsLibraryStateShader(VkShaderStageFlagBits state) const {
switch (state) {
case VK_SHADER_STAGE_VERTEX_BIT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(*this);
return (lib_state) ? lib_state->vertex_shader : nullptr;
break;
}
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(*this);
return (lib_state) ? lib_state->tessc_shader : nullptr;
break;
}
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(*this);
return (lib_state) ? lib_state->tesse_shader : nullptr;
break;
}
case VK_SHADER_STAGE_GEOMETRY_BIT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(*this);
return (lib_state) ? lib_state->geometry_shader : nullptr;
break;
}
case VK_SHADER_STAGE_TASK_BIT_EXT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(*this);
return (lib_state) ? lib_state->task_shader : nullptr;
break;
}
case VK_SHADER_STAGE_MESH_BIT_EXT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT>(*this);
return (lib_state) ? lib_state->mesh_shader : nullptr;
break;
}
case VK_SHADER_STAGE_FRAGMENT_BIT: {
const auto lib_state = Pipeline::GetLibraryState<VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT>(*this);
return (lib_state) ? lib_state->fragment_shader : nullptr;
break;
};
default:
return {};
}
}
Pipeline::Pipeline(const DeviceState& state_data, const VkGraphicsPipelineCreateInfo* pCreateInfo,
std::shared_ptr<const vvl::PipelineCache> pipe_cache, std::shared_ptr<const vvl::RenderPass>&& rpstate,
std::shared_ptr<const vvl::PipelineLayout>&& layout,
spirv::StatelessData stateless_data[kCommonMaxGraphicsShaderStages])
: StateObject(static_cast<VkPipeline>(VK_NULL_HANDLE), kVulkanObjectTypePipeline),
rp_state(rpstate),
create_info(MakeGraphicsCreateInfo(*pCreateInfo, rpstate, state_data)),
pipeline_cache(pipe_cache),
rendering_create_info(vku::FindStructInPNextChain<VkPipelineRenderingCreateInfo>(GraphicsCreateInfo().pNext)),
library_create_info(vku::FindStructInPNextChain<VkPipelineLibraryCreateInfoKHR>(GraphicsCreateInfo().pNext)),
graphics_lib_type(GetGraphicsLibType(GraphicsCreateInfo())),
pipeline_type(VK_PIPELINE_BIND_POINT_GRAPHICS),
create_flags(GetPipelineCreateFlags(GraphicsCreateInfo().pNext, GraphicsCreateInfo().flags)),
descriptor_buffer_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT) != 0),
descriptor_heap_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT) != 0),
shader_stages_ci(GraphicsCreateInfo().pStages, GraphicsCreateInfo().stageCount),
uses_shader_module_id(UsesShaderModuleId(*this)),
vertex_input_state(CreateVertexInputState(*this, state_data, GraphicsCreateInfo())),
pre_raster_state(CreatePreRasterState(*this, state_data, GraphicsCreateInfo(), rpstate, stateless_data)),
fragment_shader_state(
CreateFragmentShaderState(*this, state_data, *pCreateInfo, GraphicsCreateInfo(), rpstate, stateless_data)),
fragment_output_state(CreateFragmentOutputState(*this, state_data, *pCreateInfo, GraphicsCreateInfo(), rpstate)),
stage_states(GetStageStates(state_data, *this, GraphicsCreateInfo().layout, stateless_data)),
create_info_shaders(GetCreateInfoShaders(*this)),
linking_shaders(GetLinkingShaders(library_create_info, state_data)),
active_shaders(create_info_shaders | linking_shaders),
fs_writable_output_location_list(GetFSOutputLocations(stage_states)),
active_slots(GetActiveSlots(stage_states)),
max_active_slot(GetMaxActiveSlot(active_slots)),
dynamic_state(GetGraphicsDynamicState(*this)),
ignored_dynamic_state(GetIgnoredDynamicState(*this)),
topology_at_rasterizer(GetRasterizationInputTopology(*this, state_data)),
uses_pipeline_robustness(UsesPipelineRobustness(GraphicsCreateInfo().pNext, *this)),
uses_pipeline_vertex_robustness(UsesPipelineVertexRobustness(GraphicsCreateInfo().pNext, *this)),
ignore_color_attachments(IgnoreColorAttachments(state_data, *this)),
descriptor_heap_embedded_samplers_count(CountDescriptorHeapEmbeddedSamplers(*this)) {
if (library_create_info) {
const auto &exe_layout_state = state_data.Get<vvl::PipelineLayout>(GraphicsCreateInfo().layout);
const auto *exe_layout = exe_layout_state.get();
const auto *pre_raster_layout =
(pre_raster_state && pre_raster_state->pipeline_layout) ? pre_raster_state->pipeline_layout.get() : nullptr;
const auto *fragment_shader_layout = (fragment_shader_state && fragment_shader_state->pipeline_layout)
? fragment_shader_state->pipeline_layout.get()
: nullptr;
std::array<decltype(exe_layout), 3> layouts;
// We assume in GetCreateFlags() that the executable layout is first in this array
layouts[0] = exe_layout;
layouts[1] = fragment_shader_layout;
layouts[2] = pre_raster_layout;
merged_graphics_layout = std::make_shared<vvl::PipelineLayout>(layouts);
// TODO Could store the graphics_lib_type in the library state rather than searching for it again here.
// Or, could store a pointer back to the owning Pipeline.
for (uint32_t i = 0; i < library_create_info->libraryCount; ++i) {
const auto &state = state_data.Get<vvl::Pipeline>(library_create_info->pLibraries[i]);
if (state) {
graphics_lib_type |= state->graphics_lib_type;
}
}
}
}
Pipeline::Pipeline(const DeviceState& state_data, const VkComputePipelineCreateInfo* pCreateInfo,
std::shared_ptr<const vvl::PipelineCache>&& pipe_cache, std::shared_ptr<const vvl::PipelineLayout>&& layout,
spirv::StatelessData* stateless_data)
: StateObject(static_cast<VkPipeline>(VK_NULL_HANDLE), kVulkanObjectTypePipeline),
create_info(pCreateInfo),
pipeline_cache(std::move(pipe_cache)),
pipeline_type(VK_PIPELINE_BIND_POINT_COMPUTE),
create_flags(GetPipelineCreateFlags(ComputeCreateInfo().pNext, ComputeCreateInfo().flags)),
descriptor_buffer_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT) != 0),
descriptor_heap_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT) != 0),
shader_stages_ci(&ComputeCreateInfo().stage, 1),
uses_shader_module_id(UsesShaderModuleId(*this)),
stage_states(GetStageStates(state_data, *this, ComputeCreateInfo().layout, stateless_data)),
create_info_shaders(GetCreateInfoShaders(*this)),
active_shaders(create_info_shaders), // compute has no linking shaders
active_slots(GetActiveSlots(stage_states)),
max_active_slot(GetMaxActiveSlot(active_slots)),
dynamic_state(0), // compute has no dynamic state
ignored_dynamic_state(0), // compute has no dynamic state
uses_pipeline_robustness(UsesPipelineRobustness(ComputeCreateInfo().pNext, *this)),
uses_pipeline_vertex_robustness(false),
ignore_color_attachments(IgnoreColorAttachments(state_data, *this)),
descriptor_heap_embedded_samplers_count(CountDescriptorHeapEmbeddedSamplers(*this)),
merged_graphics_layout(layout) {
assert(active_shaders == VK_SHADER_STAGE_COMPUTE_BIT);
}
Pipeline::Pipeline(const DeviceState& state_data, const VkRayTracingPipelineCreateInfoKHR* pCreateInfo,
std::shared_ptr<const vvl::PipelineCache>&& pipe_cache, std::shared_ptr<const vvl::PipelineLayout>&& layout,
std::vector<spirv::StatelessData>* stateless_data)
: StateObject(static_cast<VkPipeline>(VK_NULL_HANDLE), kVulkanObjectTypePipeline),
create_info(pCreateInfo),
pipeline_cache(std::move(pipe_cache)),
pipeline_type(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR),
create_flags(GetPipelineCreateFlags(RayTracingCreateInfo().pNext, RayTracingCreateInfo().flags)),
descriptor_buffer_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT) != 0),
descriptor_heap_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT) != 0),
shader_stages_ci(RayTracingCreateInfo().pStages, RayTracingCreateInfo().stageCount),
ray_tracing_library_ci(RayTracingCreateInfo().pLibraryInfo),
uses_shader_module_id(UsesShaderModuleId(*this)),
stage_states(GetRayTracingStageStates(state_data, *this, RayTracingCreateInfo().layout, stateless_data)),
create_info_shaders(GetCreateInfoShaders(*this)),
active_shaders(create_info_shaders), // RTX has no linking shaders
active_slots(GetActiveSlots(stage_states)),
max_active_slot(GetMaxActiveSlot(active_slots)),
dynamic_state(GetRayTracingDynamicState(*this)),
ignored_dynamic_state(0), // RTX has no ignored dynamic state
uses_pipeline_robustness(UsesPipelineRobustness(RayTracingCreateInfo().pNext, *this)),
uses_pipeline_vertex_robustness(false),
ignore_color_attachments(IgnoreColorAttachments(state_data, *this)),
descriptor_heap_embedded_samplers_count(CountDescriptorHeapEmbeddedSamplers(*this)),
merged_graphics_layout(std::move(layout)) {
assert(0 == (active_shaders & ~(kShaderStageAllRayTracing)));
}
Pipeline::Pipeline(const DeviceState& state_data, const VkRayTracingPipelineCreateInfoNV* pCreateInfo,
std::shared_ptr<const vvl::PipelineCache>&& pipe_cache, std::shared_ptr<const vvl::PipelineLayout>&& layout)
: StateObject(static_cast<VkPipeline>(VK_NULL_HANDLE), kVulkanObjectTypePipeline),
create_info(pCreateInfo),
pipeline_cache(std::move(pipe_cache)),
pipeline_type(VK_PIPELINE_BIND_POINT_RAY_TRACING_NV),
create_flags(GetPipelineCreateFlags(RayTracingCreateInfo().pNext, RayTracingCreateInfo().flags)),
descriptor_buffer_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT) != 0),
descriptor_heap_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT) != 0),
shader_stages_ci(RayTracingCreateInfo().pStages, RayTracingCreateInfo().stageCount),
ray_tracing_library_ci(RayTracingCreateInfo().pLibraryInfo),
uses_shader_module_id(UsesShaderModuleId(*this)),
stage_states(GetStageStates(state_data, *this, RayTracingCreateInfo().layout, nullptr)),
create_info_shaders(GetCreateInfoShaders(*this)),
active_shaders(create_info_shaders), // RTX has no linking shaders
active_slots(GetActiveSlots(stage_states)),
max_active_slot(GetMaxActiveSlot(active_slots)),
dynamic_state(GetRayTracingDynamicState(*this)),
ignored_dynamic_state(0), // RTX has no ignored dynamic state
uses_pipeline_robustness(UsesPipelineRobustness(RayTracingCreateInfo().pNext, *this)),
uses_pipeline_vertex_robustness(false),
ignore_color_attachments(IgnoreColorAttachments(state_data, *this)),
descriptor_heap_embedded_samplers_count(CountDescriptorHeapEmbeddedSamplers(*this)),
merged_graphics_layout(std::move(layout)) {
assert(0 == (active_shaders & ~(kShaderStageAllRayTracing)));
}
Pipeline::Pipeline(const DeviceState& state_data, const VkDataGraphPipelineCreateInfoARM* pCreateInfo,
std::shared_ptr<const vvl::PipelineCache>&& pipe_cache, std::shared_ptr<const vvl::PipelineLayout>&& layout,
spirv::StatelessData* stateless_data)
: StateObject(static_cast<VkPipeline>(VK_NULL_HANDLE), kVulkanObjectTypePipeline),
create_info(pCreateInfo),
pipeline_cache(std::move(pipe_cache)),
pipeline_type(VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM),
create_flags(GetPipelineCreateFlags(DataGraphCreateInfo().pNext, DataGraphCreateInfo().flags)),
descriptor_buffer_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT) != 0),
descriptor_heap_mode((create_flags & VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT) != 0),
uses_shader_module_id(UsesShaderModuleId(*this)),
stage_states(GetDataGraphStageStates(state_data, *this, DataGraphCreateInfo().layout, stateless_data)),
create_info_shaders(VK_SHADER_STAGE_COMPUTE_BIT),
active_shaders(create_info_shaders), // TODO: graph may have linking shaders
active_slots(GetActiveSlots(stage_states)),
max_active_slot(GetMaxActiveSlot(active_slots)),
dynamic_state(0), // graph has no dynamic state
ignored_dynamic_state(0), // graph has no dynamic state
uses_pipeline_robustness(UsesPipelineRobustness(DataGraphCreateInfo().pNext, *this)),
uses_pipeline_vertex_robustness(false),
ignore_color_attachments(IgnoreColorAttachments(state_data, *this)),
descriptor_heap_embedded_samplers_count(CountDescriptorHeapEmbeddedSamplers(*this)),
merged_graphics_layout(layout) {
assert(active_shaders == VK_SHADER_STAGE_COMPUTE_BIT);
}
void Pipeline::Destroy() {
for (auto &item : sub_states_) {
item.second->Destroy();
}
sub_states_.clear();
StateObject::Destroy();
}
} // namespace vvl
bool IsPipelineLayoutSetCompatible(uint32_t set, const vvl::PipelineLayout *a, const vvl::PipelineLayout *b) {
if (!a || !b) {
return false;
}
if ((set >= a->set_compat_ids.size()) || (set >= b->set_compat_ids.size())) {
return false;
}
return *(a->set_compat_ids[set]) == *(b->set_compat_ids[set]);
}
std::string DescribePipelineLayoutSetNonCompatible(uint32_t set, const vvl::PipelineLayout *a, const vvl::PipelineLayout *b) {
std::ostringstream ss;
if (!a || !b) {
ss << "The set (" << set << ") has a null VkPipelineLayout object\n";
} else if (set >= a->set_compat_ids.size()) {
ss << "The set (" << set << ") is out of bounds for the number of sets in the non-compatible VkDescriptorSetLayout ("
<< a->set_compat_ids.size() << ")\n";
} else if (set >= b->set_compat_ids.size()) {
ss << "The set (" << set << ") is out of bounds for the number of sets in the non-compatible VkDescriptorSetLayout ("
<< b->set_compat_ids.size() << ")\n";
} else {
return a->set_compat_ids[set]->DescribeDifference(*(b->set_compat_ids[set]));
}
return ss.str();
}