Skip to content

Commit

Permalink
util: Add vk_typemap_helper.hpp
Browse files Browse the repository at this point in the history
Move the vk_typemap_helper.h file in Vulkan-ValidationLayers over to this
repo.

Changes from that include:
 * Renaming functions
 * Placing them in the `vku` namespace
 * Adding the `InitStructHelper` class which deduces the type based on the
   variable that is being initialized
 * Added compatibility versions for renamed functions - allows using this
   library with minimal changes to source code.
 * If a type isn't defined then compiler error is emitted
  • Loading branch information
charles-lunarg committed Sep 13, 2023
1 parent b78ed1a commit d8062e9
Show file tree
Hide file tree
Showing 7 changed files with 2,208 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.19")
vulkan/utility/vk_dispatch_table.h
vulkan/vk_enum_string_helper.h
vulkan/utility/vk_format_utils.h
vulkan/utility/vk_typemap_helper.hpp
)
endif()

Expand Down
1,890 changes: 1,890 additions & 0 deletions include/vulkan/utility/vk_typemap_helper.hpp

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions scripts/generate_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def RunGenerators(api: str, registry: str, targetFilter: str) -> None:
from generators.dispatch_table_generator import DispatchTableOutputGenerator
from generators.enum_string_helper_generator import EnumStringHelperOutputGenerator
from generators.format_utils_generator import FormatUtilsOutputGenerator
from generators.typemap_helper_generator import TypemapHelperOutputGenerator

# Build up a list of all generators and custom options
generators = {
Expand All @@ -40,6 +41,10 @@ def RunGenerators(api: str, registry: str, targetFilter: str) -> None:
'generator' : FormatUtilsOutputGenerator,
'directory' : 'include/vulkan/utility',
},
'vk_typemap_helper.hpp' : {
'generator' : TypemapHelperOutputGenerator,
'directory' : 'include/vulkan/utility',
},
}

if (targetFilter and targetFilter not in generators.keys()):
Expand Down
154 changes: 154 additions & 0 deletions scripts/generators/typemap_helper_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/python3 -i
#
# 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.
# Copyright (c) 2023-2023 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.

import os
from generators.base_generator import BaseGenerator

class TypemapHelperOutputGenerator(BaseGenerator):
def __init__(self):
BaseGenerator.__init__(self)

def generate(self):
out = []
out.append(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
// See {os.path.basename(__file__)} for modifications
// Copyright 2023 The Khronos Group Inc.
// Copyright 2023 Valve Corporation
// Copyright 2023 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0
''')
out.append('// NOLINTBEGIN') # Wrap for clang-tidy to ignore
out.append('''
#pragma once
#include <vulkan/vulkan.h>
namespace vku {
template <typename T>
VkStructureType GetSType() {
static_assert(sizeof(T) == 0, "GetSType() is being used with an unsupported Type! Is the code-gen up to date?");
return VK_STRUCTURE_TYPE_APPLICATION_INFO;
}\n''')

for struct in [x for x in self.vk.structs.values() if x.sType]:
out.extend([f'#ifdef {struct.protect}'] if struct.protect else [])
out.append(f'''
template <> VkStructureType GetSType<{struct.name}>() {{ return {struct.sType}; }}\n''')
out.extend([f'#endif // {struct.protect}\n'] if struct.protect else [])

out.append('''
// Find an entry of the given type in the const pNext chain
// returns nullptr if the entry is not found
template <typename T> const T *FindStructInPNextChain(const void *next) {
const VkBaseOutStructure *current = reinterpret_cast<const VkBaseOutStructure *>(next);
VkStructureType desired_sType = GetSType<T>();
while (current) {
if (desired_sType == current->sType) {
return reinterpret_cast<const T*>(current);
}
current = current->pNext;
}
return nullptr;
}
// Find an entry of the given type in the non-const pNext chain
// returns nullptr if the entry is not found
template <typename T> T *FindStructInPNextChain(void *next) {
VkBaseOutStructure *current = reinterpret_cast<VkBaseOutStructure *>(next);
VkStructureType desired_sType = GetSType<T>();
while (current) {
if (desired_sType == current->sType) {
return reinterpret_cast<T*>(current);
}
current = current->pNext;
}
return nullptr;
}
// Find last element of pNext chain
inline VkBaseOutStructure *FindLastStructInPNextChain(void *next) {
auto *current = reinterpret_cast<VkBaseOutStructure *>(next);
auto *prev = current;
while (current) {
prev = current;
current = reinterpret_cast<VkBaseOutStructure *>(current->pNext);
}
return prev;
}
// Init the header of an sType struct
template <typename T>
T InitStruct(void *p_next = nullptr) {
T out = {};
out.sType = GetSType<T>();
out.pNext = p_next;
return out;
}
// Init the header of an sType struct with pNext and optional fields
template <typename T, typename... StructFields>
T InitStruct(void *p_next, StructFields... fields) {
T out = {GetSType<T>(), p_next, fields...};
return out;
}
class InitStructHelper {
void* p_next = nullptr;
public:
InitStructHelper() = default;
InitStructHelper(void *p_next) : p_next(p_next) {};
template <typename T>
operator T() {
return InitStruct<T>(p_next);
}
};
} // namespace vku
// Init the header of an sType struct with pNext and optional fields
template <typename T, typename... StructFields>
T LvlInitStruct(void *p_next, StructFields... fields) {
T out = {vku::GetSType<T>(), p_next, fields...};
return out;
}
// Init the header of an sType struct
template <typename T>
T LvlInitStruct(void *p_next = nullptr) {
T out = {};
out.sType = vku::GetSType<T>();
out.pNext = p_next;
return out;
}
// Find an entry of the given type in the const pNext chain
template <typename T> const T *LvlFindInChain(const void *next) { return vku::FindStructInPNextChain<T>(next); }
// Find an entry of the given type in the pNext chain
template <typename T> T *LvlFindModInChain(void *next) { return vku::FindStructInPNextChain<T>(next); }
// Find last element of pNext chain
inline VkBaseOutStructure *LvlFindLastInChain(void *next) { return vku::FindLastStructInPNextChain(next); }
\n''')

out.append('// NOLINTEND') # Wrap for clang-tidy to ignore
self.write("".join(out))
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ add_subdirectory(layer)
add_subdirectory(generated)
add_subdirectory(vk_dispatch_table)
add_subdirectory(format_utils)
add_subdirectory(typemap_helper)
23 changes: 23 additions & 0 deletions tests/typemap_helper/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2023 The Khronos Group Inc.
# Copyright 2023 Valve Corporation
# Copyright 2023 LunarG, Inc.
#
# SPDX-License-Identifier: Apache-2.0

find_package(GTest REQUIRED CONFIG)

include(GoogleTest)

add_executable(test_typemap_helper typemap_helper.cpp)

target_link_libraries(test_typemap_helper PRIVATE
GTest::gtest
GTest::gtest_main
Vulkan::UtilityHeaders
)

if(${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)")
add_compile_options(-Wpedantic -Wall -Wextra -Werror)
endif()

gtest_discover_tests(test_typemap_helper)
134 changes: 134 additions & 0 deletions tests/typemap_helper/typemap_helper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2023 The Khronos Group Inc.
// Copyright 2023 Valve Corporation
// Copyright 2023 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//

#include <gtest/gtest.h>

#include <vulkan/utility/vk_typemap_helper.hpp>

#include <limits>

TEST(typemap_helper, structure_type_matches) {
ASSERT_EQ(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, vku::GetSType<VkInstanceCreateInfo>());

VkDeviceCreateInfo device_create_info = vku::InitStructHelper();
ASSERT_EQ(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, device_create_info.sType);

VkImageDrmFormatModifierExplicitCreateInfoEXT image_drm_format_modifier_explicit_create_info = vku::InitStructHelper{};

VkImageCreateInfo image_create_info = vku::InitStructHelper(&image_drm_format_modifier_explicit_create_info);
ASSERT_EQ(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, image_create_info.sType);
ASSERT_EQ(&image_drm_format_modifier_explicit_create_info, image_create_info.pNext);

auto buffer_create_info = vku::InitStruct<VkBufferCreateInfo>(
nullptr, static_cast<VkBufferCreateFlags>(VK_BUFFER_CREATE_SPARSE_BINDING_BIT), std::numeric_limits<uint64_t>::max(),
static_cast<VkBufferUsageFlags>(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT), VK_SHARING_MODE_EXCLUSIVE, 0U, nullptr);
ASSERT_EQ(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, buffer_create_info.sType);
ASSERT_EQ(VK_BUFFER_CREATE_SPARSE_BINDING_BIT, buffer_create_info.flags);
ASSERT_EQ(std::numeric_limits<uint64_t>::max(), buffer_create_info.size);
ASSERT_EQ(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, buffer_create_info.usage);
ASSERT_EQ(VK_SHARING_MODE_EXCLUSIVE, buffer_create_info.sharingMode);
ASSERT_EQ(0, buffer_create_info.queueFamilyIndexCount);
ASSERT_EQ(nullptr, buffer_create_info.pQueueFamilyIndices);
}

TEST(typemap_helper, find_struct_in_pnext_chain) {
VkPhysicalDeviceIDProperties id_props = vku::InitStructHelper();
VkPhysicalDeviceDriverProperties driver_props = vku::InitStructHelper(&id_props);
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color_props = vku::InitStructHelper(&driver_props);
VkPhysicalDeviceMultiDrawPropertiesEXT multi_draw_props = vku::InitStructHelper(&custom_border_color_props);
VkPhysicalDeviceProperties2 props2 = vku::InitStructHelper(&multi_draw_props);

ASSERT_EQ(&id_props, vku::FindStructInPNextChain<VkPhysicalDeviceIDProperties>(props2.pNext));
ASSERT_EQ(&driver_props, vku::FindStructInPNextChain<VkPhysicalDeviceDriverProperties>(props2.pNext));
ASSERT_EQ(&custom_border_color_props,
vku::FindStructInPNextChain<VkPhysicalDeviceCustomBorderColorPropertiesEXT>(props2.pNext));
ASSERT_EQ(&multi_draw_props, vku::FindStructInPNextChain<VkPhysicalDeviceMultiDrawPropertiesEXT>(props2.pNext));

ASSERT_EQ(reinterpret_cast<VkBaseOutStructure*>(&id_props), vku::FindLastStructInPNextChain(props2.pNext));

// Make sure the LvlFindModInChain function works
ASSERT_EQ(&id_props, LvlFindModInChain<VkPhysicalDeviceIDProperties>(props2.pNext));
ASSERT_EQ(&driver_props, LvlFindModInChain<VkPhysicalDeviceDriverProperties>(props2.pNext));
ASSERT_EQ(&custom_border_color_props, LvlFindModInChain<VkPhysicalDeviceCustomBorderColorPropertiesEXT>(props2.pNext));
ASSERT_EQ(&multi_draw_props, LvlFindModInChain<VkPhysicalDeviceMultiDrawPropertiesEXT>(props2.pNext));

ASSERT_EQ(reinterpret_cast<VkBaseOutStructure*>(&id_props), LvlFindLastInChain(props2.pNext));
}

TEST(typemap_helper, find_const_struct_in_pnext_chain) {
VkImageViewUsageCreateInfo image_view_usage_create_info = vku::InitStructHelper();
VkImageViewSlicedCreateInfoEXT image_view_sliced_create_info = vku::InitStructHelper(&image_view_usage_create_info);
VkSamplerYcbcrConversionInfo sampler_ycbcr_conversion_info = vku::InitStructHelper(&image_view_sliced_create_info);
VkOpaqueCaptureDescriptorDataCreateInfoEXT opaque_capture_descriptor_data_create_info =
vku::InitStructHelper(&sampler_ycbcr_conversion_info);
VkImageViewCreateInfo image_view = vku::InitStructHelper(&opaque_capture_descriptor_data_create_info);

ASSERT_EQ(static_cast<const void*>(&image_view_usage_create_info),
vku::FindStructInPNextChain<VkImageViewUsageCreateInfo>(image_view.pNext));
ASSERT_EQ(static_cast<const void*>(&image_view_sliced_create_info),
vku::FindStructInPNextChain<VkImageViewSlicedCreateInfoEXT>(image_view.pNext));
ASSERT_EQ(static_cast<const void*>(&sampler_ycbcr_conversion_info),
vku::FindStructInPNextChain<VkSamplerYcbcrConversionInfo>(image_view.pNext));
ASSERT_EQ(static_cast<const void*>(&opaque_capture_descriptor_data_create_info),
vku::FindStructInPNextChain<VkOpaqueCaptureDescriptorDataCreateInfoEXT>(image_view.pNext));

ASSERT_EQ(reinterpret_cast<VkBaseOutStructure*>(&image_view_usage_create_info),
vku::FindLastStructInPNextChain(const_cast<void*>(image_view.pNext)));

// Make sure the LvlFindInChain function works
ASSERT_EQ(static_cast<const void*>(&image_view_usage_create_info),
LvlFindInChain<VkImageViewUsageCreateInfo>(image_view.pNext));
ASSERT_EQ(static_cast<const void*>(&image_view_sliced_create_info),
LvlFindInChain<VkImageViewSlicedCreateInfoEXT>(image_view.pNext));
ASSERT_EQ(static_cast<const void*>(&sampler_ycbcr_conversion_info),
LvlFindInChain<VkSamplerYcbcrConversionInfo>(image_view.pNext));
ASSERT_EQ(static_cast<const void*>(&opaque_capture_descriptor_data_create_info),
LvlFindInChain<VkOpaqueCaptureDescriptorDataCreateInfoEXT>(image_view.pNext));

ASSERT_EQ(reinterpret_cast<VkBaseOutStructure*>(&image_view_usage_create_info),
LvlFindLastInChain(const_cast<void*>(image_view.pNext)));
}

TEST(typemap_helper, lvl_init_struct_still_works) {
auto device_create_info = LvlInitStruct<VkDeviceCreateInfo>();
ASSERT_EQ(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, device_create_info.sType);

auto image_drm_format_modifier_explicit_create_info = LvlInitStruct<VkImageDrmFormatModifierExplicitCreateInfoEXT>();

auto image_create_info = LvlInitStruct<VkImageCreateInfo>(&image_drm_format_modifier_explicit_create_info);
ASSERT_EQ(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, image_create_info.sType);
ASSERT_EQ(&image_drm_format_modifier_explicit_create_info, image_create_info.pNext);

auto buffer_create_info = LvlInitStruct<VkBufferCreateInfo>(
nullptr, static_cast<VkBufferCreateFlags>(VK_BUFFER_CREATE_SPARSE_BINDING_BIT), std::numeric_limits<uint64_t>::max(),
static_cast<VkBufferUsageFlags>(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT), VK_SHARING_MODE_EXCLUSIVE, 0U, nullptr);
ASSERT_EQ(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, buffer_create_info.sType);
ASSERT_EQ(VK_BUFFER_CREATE_SPARSE_BINDING_BIT, buffer_create_info.flags);
ASSERT_EQ(std::numeric_limits<uint64_t>::max(), buffer_create_info.size);
ASSERT_EQ(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, buffer_create_info.usage);
ASSERT_EQ(VK_SHARING_MODE_EXCLUSIVE, buffer_create_info.sharingMode);
ASSERT_EQ(0, buffer_create_info.queueFamilyIndexCount);
ASSERT_EQ(nullptr, buffer_create_info.pQueueFamilyIndices);
}

struct SomeVkTypes {
VkImageViewUsageCreateInfo t0{};
VkImageDrmFormatModifierExplicitCreateInfoEXT t1 = vku::InitStructHelper();
VkBufferCreateInfo t2 = vku::InitStruct<VkBufferCreateInfo>();
inline static const auto t3 = vku::InitStruct<VkInstanceCreateInfo>();
inline static const VkDeviceCreateInfo t4 = vku::InitStructHelper();
};

TEST(typemap_helper, struct_defaults_correct) {
SomeVkTypes s;

ASSERT_EQ(s.t0.sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); // should be zero because we didn't initialize it
ASSERT_EQ(s.t1.sType, VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT);
ASSERT_EQ(s.t2.sType, VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO);
ASSERT_EQ(s.t3.sType, VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
ASSERT_EQ(s.t4.sType, VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO);
}

0 comments on commit d8062e9

Please sign in to comment.