vOOlkan
An object oriented approach to Vulkan
VertexInput.h
Go to the documentation of this file.
1#ifndef VULKAN_VERTEXINPUT
2#define VULKAN_VERTEXINPUT
3
4#include <glm/glm.hpp>
5#include <concepts>
6#include <type_traits>
7#include <tuple>
8#include <map>
9#include <array>
10#include <functional>
11
12#include "VulkanException.h"
13
14
16
21 template<template<int, typename, glm::qualifier> class Vec, int n, typename Type, glm::qualifier packing>
22 constexpr bool isGlmVec(Vec<n, Type, packing>) {
23 return n > 0 && n <= 4 &&
24 std::is_fundamental<Type>::value &&
25 std::same_as <Vec<n, Type, packing>, glm::vec<n, Type, packing>>;
26 }
27
28 template<typename T>
29 concept IsVertex = requires(T, unsigned int i) {
30 {T::getDescriptors(i)} -> std::same_as<std::pair<VkVertexInputBindingDescription, std::vector<VkVertexInputAttributeDescription>>>;
31 };
32
33
34
35
36
37//===========================================================================================================================================================================
38//===========================================================================================================================================================================
39//===========================================================================================================================================================================
40
41
42
53 template<int componentsAmount>
54 class VertexProperties {
55 private:
56
60 struct ComponentProperties {
61 unsigned int scalarAmount;
62 size_t scalarSize;
63 size_t offset;
64 VkFormat format;
65 };
66
67 std::array<ComponentProperties, componentsAmount> componentsProperties;
68 size_t stride;
69 int arrayDimension = 0; //how many "complete" elements are in the array
70
71
84 template<template<int, typename, glm::qualifier> class Vec, int n, typename Type, glm::qualifier packing> requires(isGlmVec(Vec<n, Type, packing>{}))
85 void addComponent(Vec<n, Type, packing>, std::array<size_t, componentsAmount> tupleOffsets) {
86 componentsProperties.at(arrayDimension) = ComponentProperties{ n, sizeof(Type), tupleOffsets[arrayDimension]};
87 componentsProperties.at(arrayDimension).format = findRightFormat<Type, n>(); //calculate the VkFormat (no arguments are passed because it is only important the Type and coordinates amount to calculate the format)
88 stride += sizeof(Vec<n, Type, packing>); //increase the stride, namely the dimension of this Vertex
89 arrayDimension++;
90 }
91
92
98 template<typename Type, int scalarAmount>
99 static VkFormat findRightFormat() {
100 //build the string which match the enum value name, then lookup a map containing <string, enum>
101 std::string format = "VK_FORMAT_";
102 std::string length = std::to_string(sizeof(Type) * 8); //length of the underying type, in bits
103
104 //add the RxxGxxBxxAxx part
105 for (int i = 1; i <= scalarAmount; ++i) {
106 format += RGBA(i) + length;
107 }
108 format += "_";
109
110 std::string type = std::is_floating_point_v<Type> ? "SFLOAT" :
111 std::is_unsigned_v<Type> ? "UINT" : "SINT";
112 format += type;
113
114 //from the string now get the enum value
115 std::map<std::string, VkFormat> formats = {
116 {"VK_FORMAT_R16_UINTx", VK_FORMAT_R16_UINT},
117 {"VK_FORMAT_R16_SINT", VK_FORMAT_R16_SINT},
118 {"VK_FORMAT_R16_SFLOAT", VK_FORMAT_R16_SFLOAT},
119 {"VK_FORMAT_R16G16_UNORM", VK_FORMAT_R16G16_UNORM},
120 {"VK_FORMAT_R16G16_SNORM", VK_FORMAT_R16G16_SNORM},
121 {"VK_FORMAT_R16G16_USCALED", VK_FORMAT_R16G16_USCALED},
122 {"VK_FORMAT_R16G16_SSCALED", VK_FORMAT_R16G16_SSCALED},
123 {"VK_FORMAT_R16G16_UINT", VK_FORMAT_R16G16_UINT},
124 {"VK_FORMAT_R16G16_SINT", VK_FORMAT_R16G16_SINT},
125 {"VK_FORMAT_R16G16_SFLOAT", VK_FORMAT_R16G16_SFLOAT},
126 {"VK_FORMAT_R16G16B16_UNORM", VK_FORMAT_R16G16B16_UNORM},
127 {"VK_FORMAT_R16G16B16_SNORM", VK_FORMAT_R16G16B16_SNORM},
128 {"VK_FORMAT_R16G16B16_USCALED", VK_FORMAT_R16G16B16_USCALED},
129 {"VK_FORMAT_R16G16B16_SSCALED", VK_FORMAT_R16G16B16_SSCALED},
130 {"VK_FORMAT_R16G16B16_UINT", VK_FORMAT_R16G16B16_UINT},
131 {"VK_FORMAT_R16G16B16_SINT", VK_FORMAT_R16G16B16_SINT},
132 {"VK_FORMAT_R16G16B16_SFLOAT", VK_FORMAT_R16G16B16_SFLOAT},
133 {"VK_FORMAT_R16G16B16A16_UNORM", VK_FORMAT_R16G16B16A16_UNORM},
134 {"VK_FORMAT_R16G16B16A16_SNORM", VK_FORMAT_R16G16B16A16_SNORM},
135 {"VK_FORMAT_R16G16B16A16_USCALED", VK_FORMAT_R16G16B16A16_USCALED},
136 {"VK_FORMAT_R16G16B16A16_SSCALED", VK_FORMAT_R16G16B16A16_SSCALED},
137 {"VK_FORMAT_R16G16B16A16_UINT", VK_FORMAT_R16G16B16A16_UINT},
138 {"VK_FORMAT_R16G16B16A16_SINT", VK_FORMAT_R16G16B16A16_SINT},
139 {"VK_FORMAT_R16G16B16A16_SFLOAT", VK_FORMAT_R16G16B16A16_SFLOAT},
140 {"VK_FORMAT_R32_UINT", VK_FORMAT_R32_UINT},
141 {"VK_FORMAT_R32_SINT", VK_FORMAT_R32_SINT},
142 {"VK_FORMAT_R32_SFLOAT", VK_FORMAT_R32_SFLOAT},
143 {"VK_FORMAT_R32G32_UINT", VK_FORMAT_R32G32_UINT},
144 {"VK_FORMAT_R32G32_SINT", VK_FORMAT_R32G32_SINT},
145 {"VK_FORMAT_R32G32_SFLOAT", VK_FORMAT_R32G32_SFLOAT},
146 {"VK_FORMAT_R32G32B32_UINT", VK_FORMAT_R32G32B32_UINT},
147 {"VK_FORMAT_R32G32B32_SINT", VK_FORMAT_R32G32B32_SINT},
148 {"VK_FORMAT_R32G32B32_SFLOAT", VK_FORMAT_R32G32B32_SFLOAT},
149 {"VK_FORMAT_R32G32B32A32_UINT", VK_FORMAT_R32G32B32A32_UINT},
150 {"VK_FORMAT_R32G32B32A32_SINT", VK_FORMAT_R32G32B32A32_SINT},
151 {"VK_FORMAT_R32G32B32A32_SFLOAT", VK_FORMAT_R32G32B32A32_SFLOAT},
152 {"VK_FORMAT_R64_UINT", VK_FORMAT_R64_UINT},
153 {"VK_FORMAT_R64_SINT", VK_FORMAT_R64_SINT},
154 {"VK_FORMAT_R64_SFLOAT", VK_FORMAT_R64_SFLOAT},
155 {"VK_FORMAT_R64G64_UINT", VK_FORMAT_R64G64_UINT},
156 {"VK_FORMAT_R64G64_SINT", VK_FORMAT_R64G64_SINT},
157 {"VK_FORMAT_R64G64_SFLOAT", VK_FORMAT_R64G64_SFLOAT},
158 {"VK_FORMAT_R64G64B64_UINT", VK_FORMAT_R64G64B64_UINT},
159 {"VK_FORMAT_R64G64B64_SINT", VK_FORMAT_R64G64B64_SINT},
160 {"VK_FORMAT_R64G64B64_SFLOAT", VK_FORMAT_R64G64B64_SFLOAT},
161 {"VK_FORMAT_R64G64B64A64_UINT", VK_FORMAT_R64G64B64A64_UINT},
162 {"VK_FORMAT_R64G64B64A64_SINT", VK_FORMAT_R64G64B64A64_SINT},
163 {"VK_FORMAT_R64G64B64A64_SFLOAT", VK_FORMAT_R64G64B64A64_SFLOAT}
164 };
165
166 return formats[format];
167 }
168
169
173 static std::string RGBA(int i) {
174 switch (i) {
175 case 1:
176 return "R";
177 break;
178 case 2:
179 return "G";
180 break;
181 case 3:
182 return "B";
183 break;
184 case 4:
185 return "A";
186 break;
187 default:
188 throw VulkanException("Error in the amount of scala amount in the component of a Vertex: 1 <= n <= 4, but n == " + i);
189 break;
190 }
191 }
192
193
202 template<int I = 0, typename... E>
203 static constexpr std::array<size_t, sizeof...(E)> tupleElementsOffset(const std::tuple<E...>& tuple) {
204 constexpr size_t elementsInTuple = sizeof...(E);
205 std::array<size_t, elementsInTuple> result{};
206 result[I] = (char*)&std::get<I>(tuple) - (char*)&tuple; //place the offset of the I-th element of the tuple (from the beginning of the tuple itself) into the I-th element of the array
207
208 //if we reached the last element of the tuple you don't have to try to calculate the offsets of the next elements (because there are none)
209 if constexpr (I < elementsInTuple - 1) {
210 auto tmp = tupleElementsOffset<I + 1>(tuple); //calculate offsets of the elements past I
211
212 //copy the calculated offsets into the return array (only elements past I)
213 for (int i = I + 1; i < elementsInTuple; ++i) {
214 result[i] = tmp[i];
215 }
216 }
217
218 return result;
219 }
220
221
222 public:
230 template<typename... Vec> requires (isGlmVec(Vec{}) && ...)
231 VertexProperties(Vec... components) : stride{ 0 }, componentsProperties{} {
232 auto tupleOffsets = tupleElementsOffset(std::tuple{ components... }); //calculate the offsets of each element of the tuple
233
234 (addComponent(components, tupleOffsets), ...); //for each component, extract its properties
235 }
236
237
244 const ComponentProperties& operator[](unsigned int i) {
245 return componentsProperties.at(i);
246 }
247 };
248
249
250
251
252
253//===========================================================================================================================================================================
254//===========================================================================================================================================================================
255//===========================================================================================================================================================================
256
257
258
259
266 template<typename... Vec> requires (isGlmVec(Vec{}) && ...)
267 class Vertex {
268 public:
274 Vertex(Vec... vertexComponents) : vertexComponents{ vertexComponents... } {}
275
276
277 Vertex() : vertexComponents{} {}
278
279
280 friend bool operator==(const Vertex& v1, const Vertex& v2) {
281 return v1.vertexComponents == v2.vertexComponents;
282 }
283
284
291 static std::pair<VkVertexInputBindingDescription, std::vector<VkVertexInputAttributeDescription>> getDescriptors(unsigned int binding) {
292 VkVertexInputBindingDescription bindingDescription{};
293 bindingDescription.binding = binding;
294 bindingDescription.stride = sizeof(Vertex);
295 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
296
297 std::vector <VkVertexInputAttributeDescription> attributeDescriptions(sizeof...(Vec)); //the array must have as many elements as the number of components
298 for (int i = 0; i < sizeof...(Vec); ++i) {
299 attributeDescriptions[i].binding = binding;
300 attributeDescriptions[i].location = i;
301 attributeDescriptions[i].format = vertexProperties[i].format;
302 attributeDescriptions[i].offset = vertexProperties[i].offset;
303 }
304
305 return { bindingDescription, attributeDescriptions };
306 }
307
308
309 private:
310 std::tuple<Vec...> vertexComponents; //this MUST be the only (non-static) member of the class, if not we may have problems with the stride.
311 inline static VertexProperties<sizeof...(Vec)> vertexProperties = VertexProperties<sizeof...(Vec)>{ Vec{}... };
312 };
313
314
315
316
317//===========================================================================================================================================================================
318//===========================================================================================================================================================================
319//===========================================================================================================================================================================
320
321
322
323
328 class PipelineVertexArrays {
329 public:
330
331 template<IsVertex... V>
332 PipelineVertexArrays(V...) : vertexArraysDescriptor{} {
333 {
334 int i = 0; //counter of how many vertex types has already been added
335 auto extract = [this, &i]<IsVertex T>()->void {
336 auto [bindingDescr, attributeDescrs] = T::getDescriptors(i); //get the descriptors for this vertex type
337
338 //add them to the arrays
339 this->bindingDescriptors.push_back(bindingDescr);
340 this->attributeDescriptors.insert(this->attributeDescriptors.end(), attributeDescrs.begin(), attributeDescrs.end());
341 i++;
342 };
343 ((extract.operator() < V > ()), ...); //calls the lambda for each Vertex type that has been passed as argument
344 }
345
346 //fill the descriptor
347 vertexArraysDescriptor.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
348 vertexArraysDescriptor.vertexBindingDescriptionCount = bindingDescriptors.size();
349 vertexArraysDescriptor.pVertexBindingDescriptions = bindingDescriptors.data();
350 vertexArraysDescriptor.vertexAttributeDescriptionCount = attributeDescriptors.size();
351 vertexArraysDescriptor.pVertexAttributeDescriptions = attributeDescriptors.data();
352 }
353
354
360 const VkPipelineVertexInputStateCreateInfo& operator+() const {
361 return vertexArraysDescriptor;
362 }
363
364 private:
365 VkPipelineVertexInputStateCreateInfo vertexArraysDescriptor;
366 std::vector<VkVertexInputBindingDescription> bindingDescriptors;
367 std::vector< VkVertexInputAttributeDescription> attributeDescriptors;
368 };
369
370}
371
372#endif
Position operator+(Position origin, DeltaSpace spaceCovered)
Definition: Foundations.h:307
Definition: Attachment.h:11