HoviTron Video Pipeline
VulkanHelperFunction.cpp
1/* ----------------------
2* Copyright 2023 Université Libre de Bruxelles(ULB), Universidad Politécnica de Madrid(UPM), CREAL, Deutsches Zentrum für Luft - und Raumfahrt(DLR)
3
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at < http://www.apache.org/licenses/LICENSE-2.0>
7
8* Unless required by applicable law or agreed to in writing, software
9* distributed under the License is distributed on an "AS IS" BASIS,
10* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11* See the License for the specific language governing permissionsand
12* limitations under the License.
13---------------------- */
14
15
17#include"VulkanContext.h"
18
19#include "commonVulkan.h"
20
21#define _USE_MATH_DEFINES
22#include <math.h>
23
24#include <glm/gtc/quaternion.hpp>
25#ifndef __ANDROID__
26#include <numbers>
27#endif
28
29uint32_t findMemoryType(vk::PhysicalDevice& physicalDevice, uint32_t typeFilter, vk::MemoryPropertyFlags properties)
30{
31 vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties();
32 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
33 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
34 return i;
35 }
36 }
37 throw std::runtime_error("failed to find suitable memory type!");
38}
39uint32_t findFastMemoryType(vk::PhysicalDevice& physicalDevice, uint32_t typeFilter, vk::MemoryPropertyFlags properties)
40{
41 vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties();
42
43 uint32_t memoryType = 0;
44 bool found = false;
45 int score = 0;
46 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
47 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
48 if (!found){
49 memoryType = i;
50 found = true;
51 }
52 else {
53 if ( ((properties & vk::MemoryPropertyFlagBits::eHostVisible) == vk::MemoryPropertyFlagBits::eHostVisible)&& (memProperties.memoryTypes[i].propertyFlags & (vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible))) {
54 //in general memory with device type is faster than one with eHostVisible only
55 //but it is also smaller
56 memoryType = i;
57 }
58 else if ( ((properties & vk::MemoryPropertyFlagBits::eDeviceLocal) == vk::MemoryPropertyFlagBits::eDeviceLocal)&& !(memProperties.memoryTypes[i].propertyFlags & (vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible))) {
59 //in general memory with device local flag is enough and can fit more
60 memoryType = i;
61 }
62 }
63 }
64 }
65 if (found) {
66 return memoryType;
67 }
68 else {
69 throw std::runtime_error("failed to find suitable memory type!");
70 }
71}
72
73void createImage(vk::Device& device, vk::PhysicalDevice& physicalDevice, uint32_t width, uint32_t height, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties, vk::Image& image, vk::DeviceMemory& imageMemory)
74{
75 vk::ImageCreateInfo imageInfo(
76 vk::ImageCreateFlags(), //flags
77 vk::ImageType::e2D, //imageType
78 format, //format
79 { static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1 }, //extend
80 1, //mipLevels
81 1, //array
82 vk::SampleCountFlagBits::e1, //samples
83 tiling, //tiling
84 usage, //usage
85 vk::SharingMode::eExclusive //sharingMode
86 );
87 imageInfo.initialLayout = vk::ImageLayout::eUndefined;
88
89 image = device.createImage(imageInfo);
90
91 vk::MemoryRequirements memRequirement = device.getImageMemoryRequirements(image);
92 vk::MemoryAllocateInfo allocInfo(memRequirement.size, findMemoryType(physicalDevice, memRequirement.memoryTypeBits, properties));
93
94 imageMemory = device.allocateMemory(allocInfo, nullptr);
95 device.bindImageMemory(image, imageMemory, 0);
96}
97
98vk::ImageView createImageView(vk::Device& device, vk::Image image, vk::Format format, vk::ImageAspectFlags aspectFlags)
99{
100 vk::ImageSubresourceRange subRessources(
101 aspectFlags, //aspectMask
102 0, //baseMipLevel
103 1, //levelCount
104 0, //baseArrayLayer
105 1 //layerCount
106 );
107 vk::ImageViewCreateInfo viewInfo(
108 vk::ImageViewCreateFlags(), //flags
109 image, //image
110 vk::ImageViewType::e2D, //imageView
111 format, //format
112 {}, //components
113 subRessources //subRessourceRange
114 );
115 return device.createImageView(viewInfo);
116}
117vk::CommandBuffer beginSingleTimeCommands(VulkanContext* context, vk::CommandPool& commandPool)
118{
119 vk::CommandBufferAllocateInfo allocInfo(commandPool, vk::CommandBufferLevel::ePrimary, 1);
120 vk::CommandBuffer commandBuffer = context->device.allocateCommandBuffers(allocInfo).front();
121
122 vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
123 commandBuffer.begin(beginInfo);
124
125 return commandBuffer;
126}
127
128void endSingleTimeCommands(VulkanContext* context, vk::CommandPool& commandPool, vk::CommandBuffer commandBuffer)
129{
130 commandBuffer.end();
131
132 vk::SubmitInfo submitInfo(0, nullptr, nullptr, 1, &commandBuffer);
133
134 context->graphicsQueue.submit(submitInfo, nullptr);
135 context->graphicsQueue.waitIdle();
136 context->device.freeCommandBuffers(commandPool, 1, &commandBuffer);
137}
138
139void transitionImageLayout(VulkanContext* context, vk::CommandPool& commandPool, vk::Image image, vk::Format format, vk::ImageLayout oldLayout, vk::ImageLayout newLayout)
140{
141 vk::CommandBuffer commandBuffer = beginSingleTimeCommands(context, commandPool);
142
143 vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eColor;
144
145 if (newLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
146 aspectMask = vk::ImageAspectFlagBits::eDepth;
147
148 if (context->hasStencilComponent(format)) {
149 aspectMask |= vk::ImageAspectFlagBits::eStencil;
150 }
151 }
152
153 vk::ImageSubresourceRange subRessources(
154 aspectMask, //aspectMask
155 0, //baseMipLevel
156 1, //levelCount
157 0, //baseArrayLayer
158 1 //layerCount
159 );
160 vk::PipelineStageFlags sourceStage;
161 vk::PipelineStageFlags destinationStage;
162 vk::AccessFlags srcAccess;
163 vk::AccessFlags dstAccess;
164
165 if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) {
166 srcAccess = {};
167 dstAccess = vk::AccessFlagBits::eTransferWrite;
168
169 sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
170 destinationStage = vk::PipelineStageFlagBits::eTransfer;
171 }
172 else if (oldLayout == vk::ImageLayout::eTransferDstOptimal && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
173 srcAccess = vk::AccessFlagBits::eTransferWrite;
174 dstAccess = vk::AccessFlagBits::eShaderRead;
175
176 sourceStage = vk::PipelineStageFlagBits::eTransfer;
177 destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
178 }
179 else if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
180 srcAccess = {};
181 dstAccess = vk::AccessFlagBits::eShaderRead;
182
183 sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
184 destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
185 }
186 else if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
187 srcAccess = {};
188 dstAccess = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
189
190 sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
191 destinationStage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
192 }
193 else if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eColorAttachmentOptimal) {
194 //check this ?
195 srcAccess = {};
196 dstAccess = vk::AccessFlagBits::eColorAttachmentWrite;
197
198 sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
199 destinationStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
200
201 }
202 else {
203 throw std::invalid_argument("unsupported layout transition!");
204 }
205
206
207 vk::ImageMemoryBarrier barrier(
208 srcAccess, // srcAccessMask //TODO
209 dstAccess, //dstAccesMask //TODO
210 oldLayout, //oldLayout
211 newLayout, //newLayout
212 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
213 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
214 image, //image
215 subRessources //subressourcesRange
216 );
217 commandBuffer.pipelineBarrier(sourceStage, destinationStage, {}, 0, nullptr, 0, nullptr, 1, &barrier);
218
219
220 endSingleTimeCommands(context, commandPool, commandBuffer);
221}
222
223
224void createFastBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::Buffer& buffer, vk::DeviceMemory& bufferMemory, vk::Device& device, vk::PhysicalDevice& physicalDevice)
225{
226 // create buffer
227 vk::BufferCreateInfo createInfo(
228 vk::BufferCreateFlags(),
229 size, //size
230 usage, //usage
231 vk::SharingMode::eExclusive //sharing mode
232 );
233 buffer = device.createBuffer(createInfo);
234
235 // allocate device memory for that buffer
236 vk::MemoryRequirements memRequirements = device.getBufferMemoryRequirements(buffer);
237 uint32_t memoryTypeIndex = findFastMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties);
238 vk::MemoryAllocateInfo memAllocInfo(memRequirements.size, memoryTypeIndex);
239 bufferMemory = device.allocateMemory(memAllocInfo);
240
241 device.bindBufferMemory(buffer, bufferMemory, 0);
242}
243void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::Buffer& buffer, vk::DeviceMemory& bufferMemory, vk::Device & device, vk::PhysicalDevice & physicalDevice)
244{
245 // create buffer
246 vk::BufferCreateInfo createInfo(
247 vk::BufferCreateFlags(),
248 size, //size
249 usage, //usage
250 vk::SharingMode::eExclusive //sharing mode
251 );
252 buffer = device.createBuffer(createInfo);
253
254 // allocate device memory for that buffer
255 vk::MemoryRequirements memRequirements = device.getBufferMemoryRequirements(buffer);
256 uint32_t memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties);
257 vk::MemoryAllocateInfo memAllocInfo(memRequirements.size, memoryTypeIndex);
258 bufferMemory = device.allocateMemory(memAllocInfo);
259
260 device.bindBufferMemory(buffer, bufferMemory, 0);
261}
262
263
264
265std::vector<uint32_t> generate_picture_EBO(const glm::vec2 & s)
266{
267 const size_t tileSize = 4;
268 const size_t W = s[0];
269 const size_t H = s[1];
270
271 const size_t nbOfTileW = (W + tileSize - 1) / tileSize;
272 const size_t nbOfTileH = (H + tileSize - 1) / tileSize;
273
274 size_t elements_number = 3 * 2 * (H - 1) * (W - 1);
275 std::vector<uint32_t> indices;
276 indices.resize(elements_number);
277
278 size_t offset = 0;
279 const auto tileOffset = 6 * tileSize * tileSize;
280
281 for (size_t yTile = 0; yTile < nbOfTileH ; ++yTile)
282 {
283 for (size_t xTile = 0; xTile < nbOfTileW ; ++xTile)
284 {
285 auto yBase = yTile * tileSize;
286 auto xBase = xTile * tileSize;
287
288 auto currentTileOffset = offset;
289
290 for (size_t xd = 0 ; xd < tileSize ; xd++) {
291 for (size_t yd = 0 ; yd < tileSize ; yd++) {
292 auto x = xd + xBase;
293 auto y = yd + yBase;
294
295 auto currentTileSizeW = std::min(tileSize, W -1 - xBase);
296 if (x < W - 1 && y < H-1) {
297 indices[currentTileOffset + 6 * INDEX_E(xd, yd, (currentTileSizeW)) + 0] = uint32_t(INDEX_E(x, y, W));
298 indices[currentTileOffset + 6 * INDEX_E(xd, yd, (currentTileSizeW)) + 1] = uint32_t(INDEX_E(x + 1, y, W));
299 indices[currentTileOffset + 6 * INDEX_E(xd, yd, (currentTileSizeW)) + 2] = uint32_t(INDEX_E(x, y + 1, W));
300
301 indices[currentTileOffset + 6 * INDEX_E(xd, yd, (currentTileSizeW)) + 3] = uint32_t(INDEX_E(x, y + 1, W));
302 indices[currentTileOffset + 6 * INDEX_E(xd, yd, (currentTileSizeW)) + 4] = uint32_t(INDEX_E(x + 1, y, W));
303 indices[currentTileOffset + 6 * INDEX_E(xd, yd, (currentTileSizeW)) + 5] = uint32_t(INDEX_E(x + 1, y + 1, W));
304
305 offset += 6;
306 }
307 }
308 }
309 }
310 }
311
312 printf("Real number of elements %i\n", int(elements_number));
313 return indices;
314}
315
316glm::mat3x3 glmRotationMatrixFromRotationAroundX(float rx)
317{
318 /*The following is not correct because glm use column major representation
319 return glm::mat3x3(
320 1.f, 0.f, 0.f,
321 0.f, cos(rx), -sin(rx),
322 0.f, sin(rx), cos(rx));*/
323 //the correct way:
324 return glm::mat3x3(
325 1.f, 0.f, 0.f,
326 0.f, cos(rx), sin(rx),
327 0.f, -sin(rx), cos(rx));
328}
329
330glm::mat3x3 glmRotationMatrixFromRotationAroundY(float ry)
331{
332 /*
333 The following is not correct because glm use column major representation
334 return glm::mat3x3(
335 cos(ry), 0.f, sin(ry),
336 0.f, 1.f, 0.f,
337 -sin(ry), 0.f, cos(ry));*/
338 //the correct way:
339 return glm::mat3x3(
340 cos(ry), 0.f, -sin(ry),
341 0.f, 1.f, 0.f,
342 sin(ry), 0.f, cos(ry));
343}
344
345glm::mat3x3 glmRotationMatrixFromRotationAroundZ(float rz)
346{
347 /*The following is not correct because glm use column major representation
348 return glm::mat3x3(
349 cos(rz), -sin(rz), 0.f,
350 sin(rz), cos(rz), 0.f,
351 0.f, 0.f, 1.f);*/
352 //the correct way:
353 return glm::mat3x3(
354 cos(rz), sin(rz), 0.f,
355 -sin(rz), cos(rz), 0.f,
356 0.f, 0.f, 1.f);
357}
358
359glm::mat3x3 glmEulerAnglesDegreeToRotationMatrix(glm::vec3 rotationDegrees)
360{
361 // /!\ should be in rad !!!!!!!!!!!!!!!
362 //for rotation in OMAF
363 auto const radperdeg = 0.01745329252f;
364 auto rotation = radperdeg * rotationDegrees;
365 return
366 glmRotationMatrixFromRotationAroundZ(rotation[0]) *
367 glmRotationMatrixFromRotationAroundY(rotation[1]) *
368 glmRotationMatrixFromRotationAroundX(rotation[2]);
369}
370
371glm::mat3x3 glmEulerAnglesDegreeToRotationMatrixNotOMAF(glm::vec3 rotationDegrees)
372{
373 // /!\ should be in degree !!!!!!!!!!!!!!!
374 // not for OMAF !!!!
375 auto const radperdeg = 0.01745329252f;
376 auto rotation = radperdeg * rotationDegrees;
377 return glmRotationMatrixFromRotationAroundZ(rotation[2]) *
378 glmRotationMatrixFromRotationAroundY(rotation[1]) *
379 glmRotationMatrixFromRotationAroundX(rotation[0]);
380}
381
382#ifdef USE_OPENXR
383
384glm::quat XrQuaternionToGLMQuat(XrQuaternionf & q) {
385 glm::quat quad;
386 quad.x = q.x;
387 quad.y = q.y;
388 quad.z = q.z;
389 quad.w = q.w;
390 return quad;
391
392}
393XrQuaternionf GLMQuatToXrQuaternion(glm::quat & q) {
394 XrQuaternionf quad;
395 quad.x = q.x;
396 quad.y = q.y;
397 quad.z = q.z;
398 quad.w = q.w;
399 return quad;
400
401}
402
403#endif // USE_OPENXR
404
405//modified from : https://math.stackexchange.com/questions/61146/averaging-quaternions
406glm::quat ApproximateAverage(std::vector<glm::quat> quaternions) {
407 auto qLength = quaternions.size();
408 const float weightCst = 1.0f / qLength;
409 glm::quat qAvg = { 0.0, 0.0, 0.0, 0.0 };
410 for (int i = 0; i < qLength; i++) {
411 glm::quat q = quaternions[i];
412 double weight = weightCst;
413
414 // Correct for double cover, by ensuring that dot product
415 // of quats[i] and quats[0] is positive
416
417 if (i > 0 && glm::dot(quaternions[i], quaternions[0]) < 0.0) {
418 weight = -weight;
419 }
420
421 qAvg += glm::quat(weight * q.x, weight * q.y, weight * q.z, weight * q.w);
422 }
423 return glm::normalize(qAvg);
424}
425
426glm::vec3 eulerAngleXYZFromRotationMatrix(glm::mat3x3& matA) {
427 //source: http://eecs.qmul.ac.uk/~gslabaugh/publications/euler.pdf
428 auto mat = glm::transpose(matA);
429
430 auto R11 = mat[0][0];
431 auto R21 = mat[1][0];
432 auto R31 = mat[2][0];
433
434 auto R12 = mat[0][1];
435 auto R22 = mat[1][1];
436 auto R32 = mat[2][1];
437
438 auto R13 = mat[0][2];
439 auto R23 = mat[1][2];
440 auto R33 = mat[2][2];
441
442 float theta = 0;
443 float phi = 0;
444 float psi = 0;
445
446#ifndef __ANDROID__
447 const double pi = std::numbers::pi;
448#else
449 const double pi = M_PI;
450#endif
451 if (std::abs(R31) != 1) {
452 auto t1 = std::asin(R31);
453 auto t2 = pi - t1;
454 theta = std::abs(t1) <= std::abs(t2) ? t1 : t2;
455
456 auto cost1 = std::cos(t1);
457 auto cost2 = std::cos(t2);
458
459 auto psi1 = std::atan2(R32/cost1,R33/cost1);
460 auto psi2 = std::atan2(R32 /cost2, R33 /cost2);
461 psi = std::abs(t1) <= std::abs(t2) ? psi1 : psi2;
462
463 auto phi1 = std::atan2(R21 / cost1, R11 / cost1);
464 auto phi2 = std::atan2(R21 / cost2, R11 / cost2);
465 phi = std::abs(t1) <= std::abs(t2) ? phi1 : phi2;
466 }
467 else {
468 phi = 0;
469 if (R31 == -1) {
470 theta = pi / 2.0f;
471 psi = phi + atan2(R12, R13);
472 }
473 else {
474 theta = -1.0f * pi / 2.0f;
475 psi = -1 * phi + atan2(-R12, -R13);
476 }
477 }
478 return 180.0f * glm::vec3(psi,theta,phi) / (float) pi;
479}
480/*
481* slower but more precise than above
482glm::quat averageWithEigen(std::vector<glm::quat> quaternions) {
483 auto qLength = quaternions.size();
484 float weight = 1.0f / qLength;
485 glm::mat4x4 accum = glm::zero<glm::mat4x4>();
486 for (int i = 0; i < qLength; i++) {
487 glm::quat q = quaternions[i];
488 glm::vec4 qVec = {q.x,q.y,q.z,q.w};
489 glm::mat4x4 prodW = glm::outerProduct(qVec , qVec) * weight;
490 accum += prodW;
491 }
492 //TODO find eigen value of the matrice
493}*/
494
495glm::vec3 quatToEuler(glm::quat q) {
496 //for eulerInOpenXRSpace
497 glm::vec3 out = {0,0,0};
498
499 //roll
500 double siny_cosp = 2 * (q.w * q.z + q.x * q.y);
501 double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
502 out[2] = std::atan2(siny_cosp, cosy_cosp) * (180.0 / M_PI);
503
504 // pitch
505 double sinp = 2 * (q.w * q.y - q.z * q.x);
506 if (std::abs(sinp) >= 1) {
507 out[1] = std::copysign(M_PI / 2, sinp) * (180.0 / M_PI); // use 90 degrees if out of range
508 }
509 else {
510 //rotationsOMAF[view][2] = std::asin(sinp) * (180.0 / M_PI);
511 out[1] = std::asin(sinp) * (180.0 / M_PI);
512 }
513
514 // Roll
515 double sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
516 double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
517 //rotationsOMAF[view][1] = -1 * std::atan2(sinr_cosp, cosr_cosp) * (180.0 / M_PI);
518 out[0] = std::atan2(sinr_cosp, cosr_cosp) * (180.0 / M_PI);
519
520 return out;
521}
File that contain the VulkanContext class to manage Vulkan Instance, Physical device,...
Class that contains helper functions for Vulkan.
class that manages tasks related to Vulkan context (Vulkan Instance, Vulkan Physical device,...
Definition: VulkanContext.h:59
bool hasStencilComponent(vk::Format format)
vk::Device device
Definition: VulkanContext.h:87
vk::Queue graphicsQueue
Definition: VulkanContext.h:89
file that contains the common include for the Vulkan part