blob: 6c810fd5c84e14b0029a8829ffae1e766fac9c5e [file] [log] [blame]
/* Copyright (c) 2015-2021 The Khronos Group Inc.
* Copyright (c) 2015-2021 Valve Corporation
* Copyright (c) 2015-2021 LunarG, Inc.
* Copyright (C) 2015-2021 Google Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Courtney Goeltzenleuchter <courtneygo@google.com>
* Author: Tobin Ehlis <tobine@google.com>
* Author: Chris Forbes <chrisf@ijw.co.nz>
* Author: Mark Lobodzinski <mark@lunarg.com>
* Author: Dave Houlton <daveh@lunarg.com>
* Author: John Zulauf <jzulauf@lunarg.com>
* Author: Tobias Hector <tobias.hector@amd.com>
*/
#pragma once
#include "hash_vk_types.h"
#include "base_node.h"
#include "sampler_state.h"
#include "ray_tracing_state.h"
// Fwd declarations -- including descriptor_set.h creates an ugly include loop
namespace cvdescriptorset {
class DescriptorSetLayoutDef;
class DescriptorSetLayout;
class DescriptorSet;
class Descriptor;
} // namespace cvdescriptorset
class ValidationStateTracker;
class CMD_BUFFER_STATE;
class RENDER_PASS_STATE;
struct SHADER_MODULE_STATE;
// Flags describing requirements imposed by the pipeline on a descriptor. These
// can't be checked at pipeline creation time as they depend on the Image or
// ImageView bound.
enum descriptor_req {
DESCRIPTOR_REQ_VIEW_TYPE_1D = 1 << VK_IMAGE_VIEW_TYPE_1D,
DESCRIPTOR_REQ_VIEW_TYPE_1D_ARRAY = 1 << VK_IMAGE_VIEW_TYPE_1D_ARRAY,
DESCRIPTOR_REQ_VIEW_TYPE_2D = 1 << VK_IMAGE_VIEW_TYPE_2D,
DESCRIPTOR_REQ_VIEW_TYPE_2D_ARRAY = 1 << VK_IMAGE_VIEW_TYPE_2D_ARRAY,
DESCRIPTOR_REQ_VIEW_TYPE_3D = 1 << VK_IMAGE_VIEW_TYPE_3D,
DESCRIPTOR_REQ_VIEW_TYPE_CUBE = 1 << VK_IMAGE_VIEW_TYPE_CUBE,
DESCRIPTOR_REQ_VIEW_TYPE_CUBE_ARRAY = 1 << VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,
DESCRIPTOR_REQ_ALL_VIEW_TYPE_BITS = (1 << (VK_IMAGE_VIEW_TYPE_CUBE_ARRAY + 1)) - 1,
DESCRIPTOR_REQ_SINGLE_SAMPLE = 2 << VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,
DESCRIPTOR_REQ_MULTI_SAMPLE = DESCRIPTOR_REQ_SINGLE_SAMPLE << 1,
DESCRIPTOR_REQ_COMPONENT_TYPE_FLOAT = DESCRIPTOR_REQ_MULTI_SAMPLE << 1,
DESCRIPTOR_REQ_COMPONENT_TYPE_SINT = DESCRIPTOR_REQ_COMPONENT_TYPE_FLOAT << 1,
DESCRIPTOR_REQ_COMPONENT_TYPE_UINT = DESCRIPTOR_REQ_COMPONENT_TYPE_SINT << 1,
DESCRIPTOR_REQ_VIEW_ATOMIC_OPERATION = DESCRIPTOR_REQ_COMPONENT_TYPE_UINT << 1,
DESCRIPTOR_REQ_SAMPLER_IMPLICITLOD_DREF_PROJ = DESCRIPTOR_REQ_VIEW_ATOMIC_OPERATION << 1,
DESCRIPTOR_REQ_SAMPLER_BIAS_OFFSET = DESCRIPTOR_REQ_SAMPLER_IMPLICITLOD_DREF_PROJ << 1,
};
extern unsigned DescriptorRequirementsBitsFromFormat(VkFormat fmt);
struct DescriptorRequirement {
descriptor_req reqs;
bool is_writable;
std::vector<std::map<SamplerUsedByImage, const cvdescriptorset::Descriptor *>>
samplers_used_by_image; // Copy from StageState.interface_var. It combines from plural shader stages.
// The index of array is index of image.
DescriptorRequirement() : reqs(descriptor_req(0)), is_writable(false) {}
};
inline bool operator==(const DescriptorRequirement &a, const DescriptorRequirement &b) NOEXCEPT { return a.reqs == b.reqs; }
inline bool operator<(const DescriptorRequirement &a, const DescriptorRequirement &b) NOEXCEPT { return a.reqs < b.reqs; }
typedef std::map<uint32_t, DescriptorRequirement> BindingReqMap;
// Canonical dictionary for the pipeline layout's layout of descriptorsetlayouts
using DescriptorSetLayoutDef = cvdescriptorset::DescriptorSetLayoutDef;
using DescriptorSetLayoutId = std::shared_ptr<const DescriptorSetLayoutDef>;
using PipelineLayoutSetLayoutsDef = std::vector<DescriptorSetLayoutId>;
using PipelineLayoutSetLayoutsDict =
hash_util::Dictionary<PipelineLayoutSetLayoutsDef, hash_util::IsOrderedContainer<PipelineLayoutSetLayoutsDef>>;
using PipelineLayoutSetLayoutsId = PipelineLayoutSetLayoutsDict::Id;
// Canonical dictionary for PushConstantRanges
using PushConstantRangesDict = hash_util::Dictionary<PushConstantRanges>;
using PushConstantRangesId = PushConstantRangesDict::Id;
// Defines/stores a compatibility defintion for set N
// The "layout layout" must store at least set+1 entries, but only the first set+1 are considered for hash and equality testing
// Note: the "cannonical" data are referenced by Id, not including handle or device specific state
// Note: hash and equality only consider layout_id entries [0, set] for determining uniqueness
struct PipelineLayoutCompatDef {
uint32_t set;
PushConstantRangesId push_constant_ranges;
PipelineLayoutSetLayoutsId set_layouts_id;
PipelineLayoutCompatDef(const uint32_t set_index, const PushConstantRangesId pcr_id, const PipelineLayoutSetLayoutsId sl_id)
: set(set_index), push_constant_ranges(pcr_id), set_layouts_id(sl_id) {}
size_t hash() const;
bool operator==(const PipelineLayoutCompatDef &other) const;
};
// Canonical dictionary for PipelineLayoutCompat records
using PipelineLayoutCompatDict = hash_util::Dictionary<PipelineLayoutCompatDef, hash_util::HasHashMember<PipelineLayoutCompatDef>>;
using PipelineLayoutCompatId = PipelineLayoutCompatDict::Id;
// Store layouts and pushconstants for PipelineLayout
class PIPELINE_LAYOUT_STATE : public BASE_NODE {
public:
std::vector<std::shared_ptr<cvdescriptorset::DescriptorSetLayout const>> set_layouts;
PushConstantRangesId push_constant_ranges;
std::vector<PipelineLayoutCompatId> compat_for_set;
PIPELINE_LAYOUT_STATE(VkPipelineLayout l)
: BASE_NODE(l, kVulkanObjectTypePipelineLayout), set_layouts{}, push_constant_ranges{}, compat_for_set{} {}
VkPipelineLayout layout() const { return handle_.Cast<VkPipelineLayout>(); }
void reset() {
handle_.handle = 0;
set_layouts.clear();
push_constant_ranges.reset();
compat_for_set.clear();
}
std::shared_ptr<cvdescriptorset::DescriptorSetLayout const> GetDsl(uint32_t set) const {
std::shared_ptr<cvdescriptorset::DescriptorSetLayout const> dsl = nullptr;
if (set < set_layouts.size()) {
dsl = set_layouts[set];
}
return dsl;
}
};
// Shader typedefs needed to store StageStage below
struct interface_var {
uint32_t id;
uint32_t type_id;
uint32_t offset;
std::vector<std::set<SamplerUsedByImage>> samplers_used_by_image; // List of samplers that sample a given image.
// The index of array is index of image.
bool is_patch;
bool is_block_member;
bool is_relaxed_precision;
bool is_writable;
bool is_atomic_operation;
bool is_sampler_implicitLod_dref_proj;
bool is_sampler_bias_offset;
// TODO: collect the name, too? Isn't required to be present.
interface_var()
: id(0),
type_id(0),
offset(0),
is_patch(false),
is_block_member(false),
is_relaxed_precision(false),
is_writable(false),
is_atomic_operation(false),
is_sampler_implicitLod_dref_proj(false),
is_sampler_bias_offset(false) {}
};
struct PipelineStageState {
layer_data::unordered_set<uint32_t> accessible_ids;
std::vector<std::pair<descriptor_slot_t, interface_var>> descriptor_uses;
bool has_writable_descriptor;
bool has_atomic_descriptor;
VkShaderStageFlagBits stage_flag;
std::string entry_point_name;
std::shared_ptr<const SHADER_MODULE_STATE> shader_state;
};
class PIPELINE_STATE : public BASE_NODE {
public:
safe_VkGraphicsPipelineCreateInfo graphicsPipelineCI;
safe_VkComputePipelineCreateInfo computePipelineCI;
safe_VkRayTracingPipelineCreateInfoCommon raytracingPipelineCI;
// Hold shared ptr to RP in case RP itself is destroyed
std::shared_ptr<const RENDER_PASS_STATE> rp_state;
// Flag of which shader stages are active for this pipeline
uint32_t active_shaders;
uint32_t duplicate_shaders;
// Capture which slots (set#->bindings) are actually used by the shaders of this pipeline
layer_data::unordered_map<uint32_t, BindingReqMap> active_slots;
uint32_t max_active_slot; // the highest set number in active_slots for pipeline layout compatibility checks
// Additional metadata needed by pipeline_state initialization and validation
std::vector<PipelineStageState> stage_state;
layer_data::unordered_set<uint32_t> fragmentShader_writable_output_location_list;
// Vtx input info (if any)
std::vector<VkVertexInputBindingDescription> vertex_binding_descriptions_;
std::vector<VkVertexInputAttributeDescription> vertex_attribute_descriptions_;
std::vector<VkDeviceSize> vertex_attribute_alignments_;
layer_data::unordered_map<uint32_t, uint32_t> vertex_binding_to_index_map_;
std::vector<VkPipelineColorBlendAttachmentState> attachments;
layer_data::unordered_set<VkShaderStageFlagBits, hash_util::HashCombiner::WrappedHash<VkShaderStageFlagBits>>
wrote_primitive_shading_rate;
bool blendConstantsEnabled; // Blend constants enabled for any attachments
std::shared_ptr<const PIPELINE_LAYOUT_STATE> pipeline_layout;
VkPrimitiveTopology topology_at_rasterizer;
VkBool32 sample_location_enabled;
// Default constructor
PIPELINE_STATE()
: BASE_NODE(static_cast<VkPipeline>(VK_NULL_HANDLE), kVulkanObjectTypePipeline),
graphicsPipelineCI{},
computePipelineCI{},
raytracingPipelineCI{},
rp_state(nullptr),
active_shaders(0),
duplicate_shaders(0),
active_slots(),
max_active_slot(0),
vertex_binding_descriptions_(),
vertex_attribute_descriptions_(),
vertex_binding_to_index_map_(),
attachments(),
blendConstantsEnabled(false),
pipeline_layout(),
topology_at_rasterizer{},
sample_location_enabled(VK_FALSE) {}
VkPipeline pipeline() const { return handle_.Cast<VkPipeline>(); }
void SetHandle(VkPipeline p) { handle_.handle = CastToUint64(p); }
void reset() {
VkGraphicsPipelineCreateInfo emptyGraphicsCI = {};
graphicsPipelineCI.initialize(&emptyGraphicsCI, false, false);
VkComputePipelineCreateInfo emptyComputeCI = {};
computePipelineCI.initialize(&emptyComputeCI);
VkRayTracingPipelineCreateInfoKHR emptyRayTracingCI = {};
raytracingPipelineCI.initialize(&emptyRayTracingCI);
stage_state.clear();
fragmentShader_writable_output_location_list.clear();
}
void initGraphicsPipeline(const ValidationStateTracker *state_data, const VkGraphicsPipelineCreateInfo *pCreateInfo,
std::shared_ptr<const RENDER_PASS_STATE> &&rpstate);
void initComputePipeline(const ValidationStateTracker *state_data, const VkComputePipelineCreateInfo *pCreateInfo);
template <typename CreateInfo>
void initRayTracingPipeline(const ValidationStateTracker *state_data, const CreateInfo *pCreateInfo);
inline VkPipelineBindPoint getPipelineType() const {
if (graphicsPipelineCI.sType == VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO)
return VK_PIPELINE_BIND_POINT_GRAPHICS;
else if (computePipelineCI.sType == VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO)
return VK_PIPELINE_BIND_POINT_COMPUTE;
else if (raytracingPipelineCI.sType == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV)
return VK_PIPELINE_BIND_POINT_RAY_TRACING_NV;
else if (raytracingPipelineCI.sType == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR)
return VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR;
else
return VK_PIPELINE_BIND_POINT_MAX_ENUM;
}
inline VkPipelineCreateFlags getPipelineCreateFlags() const {
if (graphicsPipelineCI.sType == VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO)
return graphicsPipelineCI.flags;
else if (computePipelineCI.sType == VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO)
return computePipelineCI.flags;
else if ((raytracingPipelineCI.sType == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV) ||
(raytracingPipelineCI.sType == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR))
return raytracingPipelineCI.flags;
else
return 0;
}
};
// Track last states that are bound per pipeline bind point (Gfx & Compute)
struct LAST_BOUND_STATE {
LAST_BOUND_STATE() { Reset(); } // must define default constructor for portability reasons
PIPELINE_STATE *pipeline_state;
VkPipelineLayout pipeline_layout;
std::unique_ptr<cvdescriptorset::DescriptorSet> push_descriptor_set;
// Ordered bound set tracking where index is set# that given set is bound to
struct PER_SET {
PER_SET()
: bound_descriptor_set(nullptr),
compat_id_for_set(0),
validated_set(nullptr),
validated_set_change_count(~0ULL),
validated_set_image_layout_change_count(~0ULL),
validated_set_binding_req_map() {}
cvdescriptorset::DescriptorSet *bound_descriptor_set;
// one dynamic offset per dynamic descriptor bound to this CB
std::vector<uint32_t> dynamicOffsets;
PipelineLayoutCompatId compat_id_for_set;
// Cache most recently validated descriptor state for ValidateCmdBufDrawState/UpdateDrawState
const cvdescriptorset::DescriptorSet *validated_set;
uint64_t validated_set_change_count;
uint64_t validated_set_image_layout_change_count;
BindingReqMap validated_set_binding_req_map;
};
std::vector<PER_SET> per_set;
void Reset();
void UnbindAndResetPushDescriptorSet(CMD_BUFFER_STATE *cb_state, cvdescriptorset::DescriptorSet *ds);
void UpdateSamplerDescriptorsUsedByImage();
inline bool IsUsing() const { return pipeline_state ? true : false; }
};
static inline bool CompatForSet(uint32_t set, const LAST_BOUND_STATE &a, const std::vector<PipelineLayoutCompatId> &b) {
bool result = (set < a.per_set.size()) && (set < b.size()) && (a.per_set[set].compat_id_for_set == b[set]);
return result;
}
static inline bool CompatForSet(uint32_t set, const PIPELINE_LAYOUT_STATE *a, const PIPELINE_LAYOUT_STATE *b) {
// Intentionally have a result variable to simplify debugging
bool result = a && b && (set < a->compat_for_set.size()) && (set < b->compat_for_set.size()) &&
(a->compat_for_set[set] == b->compat_for_set[set]);
return result;
}
enum LvlBindPoint {
BindPoint_Graphics = VK_PIPELINE_BIND_POINT_GRAPHICS,
BindPoint_Compute = VK_PIPELINE_BIND_POINT_COMPUTE,
BindPoint_Ray_Tracing = 2,
BindPoint_Count = 3,
};
static VkPipelineBindPoint inline ConvertToPipelineBindPoint(LvlBindPoint bind_point) {
switch (bind_point) {
case BindPoint_Ray_Tracing:
return VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR;
default:
return static_cast<VkPipelineBindPoint>(bind_point);
}
return VK_PIPELINE_BIND_POINT_MAX_ENUM;
}
static LvlBindPoint inline ConvertToLvlBindPoint(VkPipelineBindPoint bind_point) {
switch (bind_point) {
case VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR:
return BindPoint_Ray_Tracing;
default:
return static_cast<LvlBindPoint>(bind_point);
}
return BindPoint_Count;
}