/* * 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. * * Copyright: * 2016-2017, Danny Robson */ #ifndef CRUFT_VK_TRAITS_HPP #define CRUFT_VK_TRAITS_HPP #include "./fwd.hpp" #include "./vk.hpp" #include #include namespace cruft::vk { /////////////////////////////////////////////////////////////////////////// /// describes the native type that corresponds to a given vk-cruft type. template struct native_traits { }; //------------------------------------------------------------------------- template <> struct native_traits { using type = VkBuffer; }; template <> struct native_traits { using type = VkBufferView; }; template <> struct native_traits { using type = VkCommandBuffer; }; template <> struct native_traits { using type = VkCommandPool; }; template <> struct native_traits { using type = VkDeviceMemory; }; template <> struct native_traits { using type = VkDevice; }; template <> struct native_traits { using type = VkEvent; }; template <> struct native_traits { using type = VkFence; }; template <> struct native_traits { using type = VkFramebuffer; }; template <> struct native_traits { using type = VkImageView; }; template <> struct native_traits { using type = VkInstance; }; template <> struct native_traits { using type = VkPhysicalDevice; }; template <> struct native_traits { using type = VkPipelineCache; }; template <> struct native_traits { using type = VkPipelineLayout; }; template <> struct native_traits { using type = VkQueue; }; template <> struct native_traits { using type = VkRenderPass; }; template <> struct native_traits { using type = VkSemaphore; }; template <> struct native_traits { using type = VkShaderModule; }; template <> struct native_traits { using type = VkSurfaceKHR; }; template <> struct native_traits { using type = VkSwapchainKHR; }; template <> struct native_traits { using type = VkDebugReportCallbackEXT; }; //------------------------------------------------------------------------- template <> struct native_traits> { using type = VkPipeline; }; template <> struct native_traits> { using type = VkPipeline; }; //------------------------------------------------------------------------- template using native_t = typename native_traits::type; /////////////////////////////////////////////////////////////////////////// /// defines whether a type is a native vulkan object. /// /// note that this only returns true for handle types, not parameter types. /// eg, VkDevice will return true, but VkDeviceCreateInfo will not. template struct is_native : public std::false_type { }; //------------------------------------------------------------------------- #define DEFINE_IS_HANDLE(KLASS) \ template <> \ struct is_native : \ public std::true_type \ { }; VK_NATIVE_TYPE_MAP (DEFINE_IS_HANDLE) #undef DEFINE_IS_HANDLE //------------------------------------------------------------------------- template static constexpr auto is_native_v = is_native::value; /////////////////////////////////////////////////////////////////////////// template struct is_wrapper : public std::false_type { }; //------------------------------------------------------------------------- #define IS_WRAPPER(KLASS) \ template <> \ struct is_wrapper : \ public std::true_type \ { }; VK_TYPE_MAP(IS_WRAPPER) #undef IS_WRAPPER //------------------------------------------------------------------------- template struct is_wrapper> : public is_wrapper { }; //------------------------------------------------------------------------- template static constexpr auto is_wrapper_v = is_wrapper::value; /////////////////////////////////////////////////////////////////////////// /// determines whether a function conceptually returns a singular Vulkan /// data structure. /// /// that is: is the last parameter a pointer to a Vulkan data type? /// /// a result of true probably implies the function type can be passed to /// error::try_query template struct is_query_function : public std::false_type {}; //------------------------------------------------------------------------- namespace detail { template struct is_query_args : public std::conditional_t< std::is_pointer_v< std::tuple_element_t< sizeof...(Args) - 1, std::tuple > > && is_native_v< std::remove_pointer_t< std::tuple_element_t< sizeof...(Args) - 1, std::tuple > > >, std::true_type, std::false_type > { }; } //------------------------------------------------------------------------- template struct is_query_function< ReturnT(Args...)noexcept > : detail::is_query_args { }; template struct is_query_function< ReturnT(*)(Args...)noexcept > : detail::is_query_args { }; template struct is_query_function< ReturnT(&)(Args...)noexcept > : detail::is_query_args { }; //------------------------------------------------------------------------- template constexpr auto is_query_function_v = is_query_function::value; /////////////////////////////////////////////////////////////////////////// /// determines whether a function conceptually returns an array of values. /// /// that is: does it take/return uint32_t and Vulkan types as the last two /// pointer parameters. /// /// a result of true likely implies such a function pointer can be passed /// to error::try_values template struct is_values_function : public std::false_type {}; //------------------------------------------------------------------------- namespace detail { template struct is_values_args : public std::conditional_t< std::is_same_v< uint32_t*, std::tuple_element_t< sizeof...(Args) - 2, std::tuple > > && std::is_pointer_v< std::tuple_element_t< sizeof...(Args) - 1, std::tuple > > && is_native_v< std::remove_pointer_t< std::tuple_element_t< sizeof...(Args) - 1, std::tuple > > >, std::true_type, std::false_type > { }; }; //------------------------------------------------------------------------- template struct is_values_function< ReturnT(Args...) noexcept > : detail::is_values_args { }; //------------------------------------------------------------------------- template struct is_values_function< ReturnT(&)(Args...) noexcept > : detail::is_values_args { }; //------------------------------------------------------------------------- template struct is_values_function< ReturnT(*)(Args...) noexcept > : detail::is_values_args { }; //------------------------------------------------------------------------- template constexpr auto is_values_function_v = is_values_function::value; /////////////////////////////////////////////////////////////////////////// /// return the native value for the wrapper object template constexpr auto native (const object &obj) { return obj.native (); } ///------------------------------------------------------------------------ /// return the native handle as is; supplied for implementation convenience /// in other higher order methods. template < typename NativeT, typename = std::enable_if_t, void> > constexpr auto native (NativeT val) { return val; } ///------------------------------------------------------------------------ /// return the underlying native value by querying the data object (not the /// owner object). template constexpr auto native (const owned_ptr &ptr) { return native (ptr.get ()); } ///------------------------------------------------------------------------ /// try to return the vulkan native type if there is one, else return the /// value we were given. template > constexpr auto maybe_native (T &&t) { if constexpr (is_wrapper_v) return native (t); else if constexpr (is_native_v) return native (t); else return t; } /////////////////////////////////////////////////////////////////////////// /// describes the corresponding value for sType in native structures template struct structure_type {}; //------------------------------------------------------------------------- #define DEFINE_STRUCTURE_TYPE(KLASS,VALUE) \ template <> \ struct structure_type : \ public std::integral_constant< \ VkStructureType, \ PASTE(VK_STRUCTURE_TYPE_,VALUE) \ > \ { } DEFINE_STRUCTURE_TYPE (VkInstanceCreateInfo, INSTANCE_CREATE_INFO); DEFINE_STRUCTURE_TYPE (VkApplicationInfo, APPLICATION_INFO); DEFINE_STRUCTURE_TYPE (VkDeviceQueueCreateInfo, DEVICE_QUEUE_CREATE_INFO); DEFINE_STRUCTURE_TYPE (VkDeviceCreateInfo, DEVICE_CREATE_INFO); DEFINE_STRUCTURE_TYPE (VkSwapchainCreateInfoKHR, SWAPCHAIN_CREATE_INFO_KHR); DEFINE_STRUCTURE_TYPE (VkImageViewCreateInfo, IMAGE_VIEW_CREATE_INFO); DEFINE_STRUCTURE_TYPE (VkShaderModuleCreateInfo, SHADER_MODULE_CREATE_INFO); DEFINE_STRUCTURE_TYPE (VkPipelineShaderStageCreateInfo, PIPELINE_SHADER_STAGE_CREATE_INFO); DEFINE_STRUCTURE_TYPE (VkDebugReportCallbackCreateInfoEXT, DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT); #undef DEFINE_STRUCTURE_TYPE //------------------------------------------------------------------------- template constexpr auto structure_type_v = structure_type::value; /////////////////////////////////////////////////////////////////////////// /// describes the native type that owns a given native type, and hence /// forms part of the create/destroy process. /// /// undefined for types that aren't `owned' types. template struct owner_traits {}; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits { using type = instance; }; template <> struct owner_traits { using type = device; }; template <> struct owner_traits> { using type = device; }; template <> struct owner_traits> { using type = device; }; template <> struct owner_traits { using type = instance; }; template using owner_t = typename owner_traits::type; /////////////////////////////////////////////////////////////////////////// /// describes the parameter struct used to create a given vulkan type. /// /// explicitly does not operate on vk-cruft types, only native types. template struct create_info { }; //------------------------------------------------------------------------- #define DEFINE_CREATE_INFO(TARGET,INFO) \ template <> \ struct create_info \ { using type = INFO; } DEFINE_CREATE_INFO (VkImageViewCreateInfo, VkImageViewCreateInfo); DEFINE_CREATE_INFO (VkShaderModule, VkShaderModuleCreateInfo); DEFINE_CREATE_INFO (VkPipelineLayout, VkPipelineLayoutCreateInfo); #undef DEFINE_CREATE_INFO //------------------------------------------------------------------------- template using create_info_t = typename create_info::type; /////////////////////////////////////////////////////////////////////////// /// lists the `create' and `destroy' methods for a given native type. /// /// XXX: if such a function does not exist anywhere then we currently use /// tuple::ignore to simplify implementation elsewhere. we should probably /// investigate std::is_detected for these cases though. /// /// clang#18781: we make use of references types extensively here to /// workaround a potential ODR bug in clang which results in undefined /// references. /// or potentially i'm being too clever for my own good, but it works fine /// in gcc... template struct life_traits { }; //------------------------------------------------------------------------- template <> struct life_traits { static constexpr auto& create = vkCreateInstance; static constexpr auto& destroy = vkDestroyInstance; }; template <> struct life_traits { static constexpr auto& create = vkCreateDevice; static constexpr auto& destroy = vkDestroyDevice; }; template <> struct life_traits { static constexpr auto& create = vkCreateImageView; static constexpr auto& destroy = vkDestroyImageView; }; template <> struct life_traits { static constexpr auto& create = vkCreateCommandPool; static constexpr auto& destroy = vkDestroyCommandPool; }; template <> struct life_traits { static constexpr auto& create = vkCreateFence; static constexpr auto& destroy = vkDestroyFence; }; template <> struct life_traits { static constexpr auto& create = vkCreateSemaphore; static constexpr auto& destroy = vkDestroySemaphore; }; template <> struct life_traits { static constexpr auto& create = vkCreateEvent; static constexpr auto& destroy = vkDestroyEvent; }; template <> struct life_traits { static constexpr auto& create = vkCreateRenderPass; static constexpr auto& destroy = vkDestroyRenderPass; }; template <> struct life_traits { static constexpr auto& create = vkCreateFramebuffer; static constexpr auto& destroy = vkDestroyFramebuffer; }; template <> struct life_traits { static constexpr auto& create = vkCreateShaderModule; static constexpr auto& destroy = vkDestroyShaderModule; }; template <> struct life_traits { static constexpr auto& destroy = vkDestroySurfaceKHR; }; template <> struct life_traits { static constexpr auto& create = vkGetDeviceQueue; static constexpr auto destroy = ::util::tuple::ignore< native_t>, native_t, const VkAllocationCallbacks* >; }; template <> struct life_traits { static constexpr auto& create = vkCreatePipelineLayout; static constexpr auto& destroy = vkDestroyPipelineLayout; }; template <> struct life_traits { static constexpr auto& create = vkCreatePipelineCache; static constexpr auto& destroy = vkDestroyPipelineCache; }; template <> struct life_traits { static constexpr auto& create = vkAllocateMemory; static constexpr auto& destroy = vkFreeMemory; }; template <> struct life_traits { static constexpr auto& create = vkCreateBuffer; static constexpr auto& destroy = vkDestroyBuffer; }; template <> struct life_traits { static constexpr auto& create = vkCreateBufferView; }; template <> struct life_traits { static constexpr auto& create = vkCreateSwapchainKHR; static constexpr auto& destroy = vkDestroySwapchainKHR; }; template <> struct life_traits { static decltype(vkCreateDebugReportCallbackEXT) *create; static decltype(vkDestroyDebugReportCallbackEXT) *destroy; }; // loads all extension function pointers from an instance into the // appropriate life_traits pointers where available. // // this is unambigiously the wrong way of doing things: each instance may // have their own function pointers to each of the extension methods. // // but... we only support one instance for the time being, and this easier. void load_traits (instance&); /////////////////////////////////////////////////////////////////////////// template struct wrapper_traits : public life_traits> { }; //------------------------------------------------------------------------- template <> struct wrapper_traits> { static constexpr auto &create = vkCreateGraphicsPipelines; static constexpr auto &destroy = vkDestroyPipeline; }; //------------------------------------------------------------------------- template <> struct wrapper_traits> { static constexpr auto &create = vkCreateComputePipelines; static constexpr auto &destroy = vkDestroyPipeline; }; /////////////////////////////////////////////////////////////////////////// /// describes the functions required to enumerate native types template struct enum_traits { }; template <> struct enum_traits { static constexpr auto &enumerate = vkEnumeratePhysicalDevices; }; } #endif