| /* Copyright (c) 2019-2026 The Khronos Group Inc. |
| * Copyright (c) 2019-2026 Valve Corporation |
| * Copyright (c) 2019-2026 LunarG, Inc. |
| * Modifications Copyright (C) 2022 RasterGrid Kft. |
| * Modifications Copyright (C) 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. |
| */ |
| |
| #pragma once |
| |
| // This file should not need to include anything else, the goal of this file is utils that "could" be in the Vulkan-Headers |
| #include <vulkan/vulkan_core.h> |
| |
| // It is very rare to have more than 3 stages (really only geo/tess) and better to save memory/time for the 99% use cases |
| static const uint32_t kCommonMaxGraphicsShaderStages = 3; |
| |
| static const VkShaderStageFlags kShaderStageAllGraphics = |
| 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 | VK_SHADER_STAGE_TASK_BIT_EXT | VK_SHADER_STAGE_MESH_BIT_EXT; |
| |
| static const VkShaderStageFlags kShaderStageAllRayTracing = |
| VK_SHADER_STAGE_ANY_HIT_BIT_KHR | VK_SHADER_STAGE_CALLABLE_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | |
| VK_SHADER_STAGE_INTERSECTION_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR | VK_SHADER_STAGE_RAYGEN_BIT_KHR; |
| |
| static inline uint32_t IndexTypeByteSize(VkIndexType index_type) { |
| switch (index_type) { |
| case VK_INDEX_TYPE_UINT16: |
| return 2; |
| case VK_INDEX_TYPE_UINT32: |
| return 4; |
| case VK_INDEX_TYPE_UINT8: |
| return 1; |
| case VK_INDEX_TYPE_NONE_KHR: // alias VK_INDEX_TYPE_NONE_NV |
| return 0; |
| case VK_INDEX_TYPE_MAX_ENUM: |
| break; |
| // Not a real index type. Express no alignment requirement here |
| // Assume caller is handling this already |
| } |
| return 1; // so compilers don't complain nothing is returned in all cases |
| } |
| |
| static bool inline IsStageInPipelineBindPoint(VkShaderStageFlags stages, VkPipelineBindPoint bind_point) { |
| switch (bind_point) { |
| case VK_PIPELINE_BIND_POINT_GRAPHICS: |
| return (stages & kShaderStageAllGraphics) != 0; |
| case VK_PIPELINE_BIND_POINT_COMPUTE: |
| return (stages & VK_SHADER_STAGE_COMPUTE_BIT) != 0; |
| case VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR: |
| return (stages & kShaderStageAllRayTracing) != 0; |
| default: |
| return false; |
| } |
| } |
| |
| // all "advanced blend operation" found in spec |
| static inline bool IsAdvanceBlendOperation(const VkBlendOp blend_op) { |
| return (static_cast<int>(blend_op) >= VK_BLEND_OP_ZERO_EXT) && (static_cast<int>(blend_op) <= VK_BLEND_OP_BLUE_EXT); |
| } |
| |
| // Helper for Dual-Source Blending |
| static inline bool IsSecondaryColorInputBlendFactor(VkBlendFactor blend_factor) { |
| return (blend_factor == VK_BLEND_FACTOR_SRC1_COLOR || blend_factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR || |
| blend_factor == VK_BLEND_FACTOR_SRC1_ALPHA || blend_factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA); |
| } |
| |
| static inline bool IsPointTopology(VkPrimitiveTopology topology) { return topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST; }; |
| |
| static inline bool IsLineTopology(VkPrimitiveTopology topology) { |
| return (topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP || |
| topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY || |
| topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY); |
| }; |
| |
| static inline bool IsTriangleTopology(VkPrimitiveTopology topology) { |
| return (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST || topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP || |
| topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN || topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY || |
| topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY); |
| }; |
| |
| // from vkspec.html#drawing-primitive-topology-class |
| static inline bool IsSameTopologyClass(VkPrimitiveTopology a, VkPrimitiveTopology b) { |
| if (IsPointTopology(a)) { |
| return IsPointTopology(b); |
| } else if (IsLineTopology(a)) { |
| return IsLineTopology(b); |
| } else if (IsTriangleTopology(a)) { |
| return IsTriangleTopology(b); |
| } else { |
| return a == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && b == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; |
| } |
| }; |
| |
| static inline VkPrimitiveTopology TriangleToLineTopology(VkPrimitiveTopology topology) { |
| if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) { |
| return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; |
| } else if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY) { |
| return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY; |
| } else if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) { |
| return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; |
| } else if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY) { |
| return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY; |
| } else if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) { |
| return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; |
| } |
| return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; |
| } |
| |
| static inline VkExtent3D CastTo3D(const VkExtent2D &d2) { |
| VkExtent3D d3 = {d2.width, d2.height, 1}; |
| return d3; |
| } |
| |
| static inline VkOffset3D CastTo3D(const VkOffset2D &d2) { |
| VkOffset3D d3 = {d2.x, d2.y, 0}; |
| return d3; |
| } |
| |
| // Returns true if sub_rect is entirely contained within rect |
| static inline bool ContainsRect(VkRect2D rect, VkRect2D sub_rect) { |
| if ((sub_rect.offset.x < rect.offset.x) || (sub_rect.offset.x + sub_rect.extent.width > rect.offset.x + rect.extent.width) || |
| (sub_rect.offset.y < rect.offset.y) || (sub_rect.offset.y + sub_rect.extent.height > rect.offset.y + rect.extent.height)) { |
| return false; |
| } |
| return true; |
| } |
| |
| static constexpr VkPipelineStageFlags2 kFramebufferStagePipelineStageFlags = |
| (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | |
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); |
| |
| static constexpr VkAccessFlags2 kShaderTileImageAllowedAccessFlags = |
| VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | |
| VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
| |
| static constexpr bool HasNonFramebufferStagePipelineStageFlags(VkPipelineStageFlags2 inflags) { |
| return (inflags & ~kFramebufferStagePipelineStageFlags) != 0; |
| } |
| |
| static constexpr bool HasFramebufferStagePipelineStageFlags(VkPipelineStageFlags2 inflags) { |
| return (inflags & kFramebufferStagePipelineStageFlags) != 0; |
| } |
| |
| static constexpr bool HasNonShaderTileImageAccessFlags(VkAccessFlags2 in_flags) { |
| return ((in_flags & ~kShaderTileImageAllowedAccessFlags) != 0); |
| } |
| |
| static inline const VkSamplerCreateInfo* GetEmbeddedSampler(const VkDescriptorSetAndBindingMappingEXT& mapping) { |
| switch (mapping.source) { |
| case VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT: |
| return mapping.sourceData.constantOffset.pEmbeddedSampler; |
| case VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_PUSH_INDEX_EXT: |
| return mapping.sourceData.pushIndex.pEmbeddedSampler; |
| case VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_INDIRECT_INDEX_EXT: |
| return mapping.sourceData.indirectIndex.pEmbeddedSampler; |
| case VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_INDIRECT_INDEX_ARRAY_EXT: |
| return mapping.sourceData.indirectIndexArray.pEmbeddedSampler; |
| case VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_SHADER_RECORD_INDEX_EXT: |
| return mapping.sourceData.shaderRecordIndex.pEmbeddedSampler; |
| default: |
| return nullptr; |
| } |
| } |
| |
| static inline uint32_t CountDescriptorHeapEmbeddedSamplers(const void* pNext) { |
| const VkShaderDescriptorSetAndBindingMappingInfoEXT* mapping_info = |
| vku::FindStructInPNextChain<VkShaderDescriptorSetAndBindingMappingInfoEXT>(pNext); |
| uint32_t count = 0; |
| |
| if (mapping_info) { |
| for (uint32_t i = 0; i < mapping_info->mappingCount; ++i) { |
| const VkDescriptorSetAndBindingMappingEXT& mapping = mapping_info->pMappings[i]; |
| if (GetEmbeddedSampler(mapping) != nullptr) { |
| count++; |
| } |
| } |
| } |
| |
| return count; |
| } |
| |
| static constexpr bool IsDescriptorHeapAddr(const VkDescriptorType type) { |
| return (type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) || (type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV) || |
| (type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) || (type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) || |
| (type == VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV); |
| } |
| |
| static constexpr bool IsDescriptorHeapTexelBuffer(const VkDescriptorType type) { |
| return (type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) || (type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER); |
| } |
| |
| static constexpr bool IsDescriptorHeapImage(const VkDescriptorType type) { |
| return (type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) || (type == VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM) || |
| (type == VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM) || (type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) || |
| (type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT); |
| } |
| |
| static constexpr bool IsDescriptorHeapTensor(const VkDescriptorType type) { |
| return (type == VK_DESCRIPTOR_TYPE_TENSOR_ARM); |
| } |