HoviTron Video Pipeline
VulkanDrawing.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#include "VulkanDrawing.h"
16#include"VulkanContext.h"
17#include"VulkanRenderPassAbstract.h"
18#include "VulkanWrapper.h"
20#include<iostream>
21#include"../WindowAbstract.h"
22//#include"../RVSConfig/include/Config.h"
23#include"InputProvider.h"
24
25#ifdef __ANDROID__
26static const char* kTAG = "drawing";
27#define LOGI(...) \
28 ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
29#endif
30
31//using rvs::detail::ColorSpace;
32//using rvs::detail::g_color_space;
33
35{
36 this->context = context;
37 this->renderPass = renderPass;
38 this->window = window;
39 this->wrapper = wraps;
40
41}
42
44{
45 this->inputProvider = inputProvider;
46 viewNumber = window->getViewNumber(); //get the nb of sawpchain
47 for(int i = 0 ; i < viewNumber; i++){
48 currentFrame.push_back(0);
49 }
50
51 createCommandPool();
52 createCommandBuffers();
53 createSyncObjects();
54 initialized = true;
55
56
57 //streamFrameInfos.resize(inputProvider->enumerateStreamsParameters().size());
58}
59
61 for (int view = 0; view < viewNumber; view++) {
62
63 /*if (window->useOpenXR()) {
64 for (size_t i = 0; i < window->getViewNumber(); i++) {
65 //context->device.destroySemaphore(renderFinishedSemaphores[view][i]);
66 context->device.destroyFence(inFlightFences[view][i]);
67 }
68 }
69 else {*/
70 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
71 if (i < wrapper->getAttachmentSize()) {
72 //context->device.destroySemaphore(renderFinishedSemaphores[view][i]);
73 //context->device.destroySemaphore(imageAvailableSemaphores[view][i]);
74 context->device.destroyFence(inFlightFences[view][i]);
75 }
76 }
77 //}
78 }
79 //renderFinishedSemaphores.clear();
80 //execFences.clear();
81 //imageAvailableSemaphores.clear();
82 inFlightFences.clear();
83 imagesInFlight.clear();
84
85
86
87
88 context->device.destroyCommandPool(commandPool); //will automaticly destroy command buffers
89 commandBuffers.clear();
90}
91
92
93
94vk::Fence VulkanDrawing::submitDrawCall(std::tuple<int , uint32_t> idImage, const std::vector<vk::Semaphore> & imageAvailableSemaphores, std::vector<vk::Semaphore> & signalSemaphore, uint32_t depthIndex) {
95
96 int view;
97 uint32_t imageIndex;
98 std::tie(view, imageIndex) = idImage;
99 currentFrame[view] = imageIndex;
100
101 D(if (wrapper->isDepthOutputRecquired()) { assert(imageIndex == depthIndex); })
102
103 context->device.waitForFences(1, &inFlightFences[view][currentFrame[view]], true, UINT64_MAX);
104
105
106
107 auto streamFrameInfos = wrapper->getFramesInfo(view);
108
109 renderPass->updateBuffer(imageIndex, true, streamFrameInfos,view);
110
111 //if (resetCommandBuffer) {
112 commandBuffers[view][imageIndex].reset(vk::CommandBufferResetFlagBits::eReleaseResources);
113 recordCommandBuffers(commandBuffers[view][imageIndex], view, imageIndex, streamFrameInfos);
114 //}
115
116 if (imagesInFlight[view][imageIndex].has_value()) {
117 context->device.waitForFences(1, &imagesInFlight[view][imageIndex].value(), true, UINT64_MAX);
118 }
119
120 imagesInFlight[view][imageIndex] = inFlightFences[view][currentFrame[view]];
121
122
123 vk::PipelineStageFlags waitDestinationStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput);
124 context->device.resetFences(1, &inFlightFences[view][currentFrame[view]]);
125 if (window->isSwapChainNeeded() ) {
126 uint32_t semSize = imageAvailableSemaphores.size();
127 vk::SubmitInfo submitInfo(
128 semSize,
129 semSize > 0 ? imageAvailableSemaphores.data() : nullptr,//&imageAvailableSemaphores[view][currentFrame[view]],
130 semSize > 0 ? &waitDestinationStageMask : nullptr,
131 1,
132 &commandBuffers[view][imageIndex],
133 static_cast<uint32_t>(signalSemaphore.size()),
134 signalSemaphore.size() == 0 ? nullptr : signalSemaphore.data()//&renderFinishedSemaphores[view][currentFrame[view]]
135 );
136 context->graphicsQueue.submit(submitInfo, inFlightFences[view][currentFrame[view]]);
137 }
138 else {
139 vk::SubmitInfo submitInfo(
140 0,
141 {},
142 nullptr,
143 1,
144 &commandBuffers[view][imageIndex],
145 0,
146 nullptr
147 );
148 context->graphicsQueue.submit(submitInfo, inFlightFences[view][currentFrame[view]]);
149
150 const uint32_t timeoutNs = 1 * 1000 * 1000 * 1000;
151 for (int i = 0; i < 5; ++i) {
152 auto res = context->device.waitForFences(1, &inFlightFences[view][currentFrame[view]], VK_TRUE, timeoutNs);
153 if (res == vk::Result::eSuccess) {
154 // Buffer can be executed multiple times...
155 break;
156 }
157 std::cout << "Waiting for CmdBuffer fence timed out, retrying..." << std::endl;
158 }
159 }
160
161
162 inputProvider->releaseStreamsFrames();
163 /*
164 std::variant<vk::Semaphore,vk::Fence> res;
165 if (!window->useOpenXR()) {
166 res = renderFinishedSemaphores[view][currentFrame[view]];
167 }
168 else {
169 res =
170 }*/
171
172 //currentFrame[view] = (currentFrame[view] + 1) % MAX_FRAMES_IN_FLIGHT;
173 return inFlightFences[view][currentFrame[view]];
174}
175
176
177
178
179
180
181
182void VulkanDrawing::createCommandPool()
183{
184 QueueFamilyIndices queueFamilyIndices = context->findQueueFamilies(context->physicalDevice);
185
186 // create a CommandPool to allocate a CommandBuffer from
187 vk::CommandPoolCreateFlags flags = vk::CommandPoolCreateFlags();
188 //if (resetCommandBuffer) {
189 flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
190 //}
191 vk::CommandPoolCreateInfo commandPoolInfo(flags, queueFamilyIndices.graphicsFamily.value());
192 commandPool = context->device.createCommandPool(commandPoolInfo);
193}
194
195void VulkanDrawing::createCommandBuffers()
196{
197 commandBuffers.resize(viewNumber);
198 // allocate a CommandBuffer from the CommandPool
199 for(int view = 0; view < viewNumber; view++) {
200 vk::CommandBufferAllocateInfo commandBufferInfo(commandPool,
201 vk::CommandBufferLevel::ePrimary,
202 (uint32_t) wrapper->getAttachmentSize());
203
204 commandBuffers[view] = context->device.allocateCommandBuffers(commandBufferInfo);
205
206#ifndef NDEBUG
207 for (int i = 0; i < commandBuffers[view].size(); i++) {
208 auto name = "cmdBuff Draw v: " + std::to_string(view) + " " + std::to_string(i);
209 vk::DebugUtilsObjectNameInfoEXT debug(vk::ObjectType::eCommandBuffer, (uint64_t) static_cast<VkCommandBuffer>(commandBuffers[view][i]), name.c_str());
210 context->device.setDebugUtilsObjectNameEXT(debug);
211 }
212#endif
213
214 }
215
216}
217
218void VulkanDrawing::recordCommandBuffers(vk::CommandBuffer& buff, int view , int i, std::span<InputProvider::StreamFrameInfo> frameInfos)
219{
220 const bool renderDirectlyToSwapchain = !context->isIndepFromWindowDimension();
221 buff.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlags()));
222
223 renderPass->recordCommandBuffer(buff, i, frameInfos, view);
224 //Followings lines are here to blit/copy the image if needed
225
226 vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eColor;
227 vk::ImageSubresourceRange subRessources(
228 aspectMask, //aspectMask
229 0, //baseMipLevel
230 1, //levelCount
231 0, //baseArrayLayer
232 1 //layerCount
233 );
234
235 vk::ImageSubresourceLayers srcRes(vk::ImageAspectFlagBits::eColor, 0, 0, 1);
236 vk::ImageSubresourceLayers dstRes(vk::ImageAspectFlagBits::eColor, 0, 0, 1);
237
238 uint32_t sourceWidth = wrapper->getSwapchainExtend(view).width;
239 uint32_t sourceHeight = wrapper->getSwapchainExtend(view).height;
240 std::array<vk::Offset3D, 2> offsetSrc = { vk::Offset3D(0, 0, 0),
241 //vk::Offset3D(renderPass->renderingExtent.width,
242 // renderPass->renderingExtent.height,
243 vk::Offset3D(sourceWidth,sourceHeight,1) };
244
245
246 if (!renderDirectlyToSwapchain) {
247 //for the case where the swapchains images are not in the framebuffer
248 //calculation for ratio
249
250
251 double wRatio = (double)(window->getSwapchainExtent(view).width) /
252 sourceWidth;
253 //renderPass->renderingExtent.width;
254 double hRatio = (double)(window->getSwapchainExtent(view).height) /
255 sourceHeight;
256 //renderPass->renderingExtent.height;
257 double minRatio = std::min(wRatio, hRatio);
258 uint32_t newH = minRatio * sourceHeight;
259 uint32_t newW = minRatio * sourceWidth;
260
261 if (window->useOpenXR()) {
262 wRatio = window->getSwapchainExtent(view).width;
263 hRatio = window->getSwapchainExtent(view).height;
264 }
265
266
267 std::array<vk::Offset3D, 2> offsetDst = { vk::Offset3D(0, 0, 0),
268 vk::Offset3D(newW, newH, 1) };
269
270
271 if (wrapper->getAttachmentSize() || window->useOpenXR()) {
272 //use of Blit Command
273
274 //vk::Image dstImage = presentation->swapChainImages[view][i].image;
275 vk::Image dstImage = window->getSwapchainImage(view, i);
276
277 vk::ImageMemoryBarrier barrierBeforeBlit(
278 {}, // srcAccessMask //TODO
279 vk::AccessFlagBits::eTransferWrite, //dstAccesMask //TODO
280 vk::ImageLayout::eUndefined, //oldLayout
281 vk::ImageLayout::eTransferDstOptimal, //newLayout
282 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
283 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
284 dstImage, //image
285 subRessources //subressourcesRange
286 );
287 buff.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
288 vk::PipelineStageFlagBits::eTransfer, {}, 0, nullptr,
289 0, nullptr, 1, &barrierBeforeBlit);
290 //test
291 vk::ImageBlit region(srcRes, offsetSrc, dstRes, offsetDst);
292 vk::ImageLayout newLayout = window->useOpenXR() ? vk::ImageLayout::eColorAttachmentOptimal : vk::ImageLayout::ePresentSrcKHR;
293 /*if (renderPass->inputImage % 2 == 1) {
294 //copy attachment 6
295 buff.blitImage(renderPass->attachementAccuColor2[i].image,
296 vk::ImageLayout::eTransferSrcOptimal,
297 dstImage,
298 vk::ImageLayout::eTransferDstOptimal, region,
299 vk::Filter::eNearest);
300 }
301 else {
302 //copy attachment 4
303 buff.blitImage(renderPass->attachementAccuColor[i].image,
304 vk::ImageLayout::eTransferSrcOptimal,
305 dstImage,
306 vk::ImageLayout::eTransferDstOptimal, region,
307 vk::Filter::eNearest);
308 }*/
309 buff.blitImage(renderPass->getImageToBlit(i),
310 vk::ImageLayout::eTransferSrcOptimal,
311 dstImage,
312 vk::ImageLayout::eTransferDstOptimal, region,
313 vk::Filter::eNearest);
314 vk::ImageMemoryBarrier barrier(
315 vk::AccessFlagBits::eTransferWrite, // srcAccessMask //TODO
316 vk::AccessFlagBits::eMemoryRead, //dstAccesMask //TODO
317 vk::ImageLayout::eTransferDstOptimal, //oldLayout
318 newLayout, //newLayout
319 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
320 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
321 dstImage, //image
322 subRessources //subressourcesRange
323 );
324 buff.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
325 vk::PipelineStageFlagBits::eTopOfPipe, {}, 0, nullptr,
326 0, nullptr, 1, &barrier);
327 }
328 else {
329 //This part concern the mode without window
330 //no blit Command for image memory
331 vk::ImageSubresourceLayers subRessources(
332 vk::ImageAspectFlagBits::eColor, //aspectMask
333 0, //mipLevel
334 0, //baseArrayLayer
335 1 //layerCount
336 );
337 vk::BufferImageCopy region(
338 0, //bufferOffset
339 0, //bufferRowLength
340 0, //bufferImageHeight
341 subRessources, //imageSubresource
342 { 0,0,0 }, //imageOffset
343 { sourceWidth,sourceHeight,1 } //imageextent
344 );
345
346 auto buf = window->getRenderingDestination();
347 buff.copyImageToBuffer(renderPass->getImageToBlit(i), vk::ImageLayout::eTransferSrcOptimal, buf, 1, &region);
348 }
349 }
350
351 //Ask the windows if there is additional images to blit to
352 //Example for the mirror in debug (openxr window)
353 auto destinations = window->getBlitDestinations(view, i);
354 auto destinationsExtents = window->getBlitExtentDestinations(view, i);
355 if (destinations.size() > 0) {
356
357
358 //vk::Image srcImage = presentation->swapChainImages[view][i].image;
359
360 vk::Image srcImage = window->getSwapchainImage(view, i);
361
362 vk::ImageMemoryBarrier barrierSrcBeforeBlit(
363 {}, // srcAccessMask //TODO
364 vk::AccessFlagBits::eTransferWrite, //dstAccesMask
365 vk::ImageLayout::eUndefined, //oldLayout
366 vk::ImageLayout::eTransferSrcOptimal, //newLayout
367 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
368 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
369 srcImage, //image
370 subRessources //subressourcesRange
371 );
372 for (int d = 0; d < destinations.size(); d++) {
373 vk::ImageMemoryBarrier barrierDstBeforeBlit(
374 {}, // srcAccessMask
375 vk::AccessFlagBits::eTransferWrite, //dstAccesMask
376 vk::ImageLayout::eUndefined, //oldLayout
377 vk::ImageLayout::eTransferDstOptimal, //newLayout
378 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
379 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
380 destinations[d], //image
381 subRessources //subressourcesRange
382 );
383 std::array<vk::ImageMemoryBarrier, 2> barriers = { barrierSrcBeforeBlit,barrierDstBeforeBlit };
384 buff.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
385 vk::PipelineStageFlagBits::eTransfer, {}, 0, nullptr,
386 0, nullptr, static_cast<uint32_t>(barriers.size()), barriers.data());
387
388 double wRatio = (double)(destinationsExtents[d].width) / (sourceWidth);
389 double hRatio = (double)(destinationsExtents[d].height) / (sourceHeight);
390 double minRatio = std::min(wRatio, hRatio);
391 uint32_t newH = minRatio * sourceHeight;
392 uint32_t newW = minRatio * sourceWidth;
393 std::array<vk::Offset3D, 2> offsetDst = { vk::Offset3D(0, 0, 0),
394 vk::Offset3D(newW, newH , 1) };
395 vk::ImageBlit region(srcRes, offsetSrc, dstRes, offsetDst);
396 buff.blitImage(srcImage,
397 vk::ImageLayout::eTransferSrcOptimal,
398 destinations[d],
399 vk::ImageLayout::eTransferDstOptimal, region,
400 vk::Filter::eNearest);
401
402 vk::ImageMemoryBarrier barrierDest(
403 vk::AccessFlagBits::eTransferWrite, // srcAccessMask //TODO
404 vk::AccessFlagBits::eMemoryRead, //dstAccesMask //TODO
405 vk::ImageLayout::eTransferDstOptimal, //oldLayout
406 vk::ImageLayout::ePresentSrcKHR, //newLayout
407 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
408 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
409 destinations[d], //image
410 subRessources //subressourcesRange
411 );
412 buff.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
413 vk::PipelineStageFlagBits::eTopOfPipe, {}, 0, nullptr,
414 0, nullptr, 1, &barrierDest);
415
416 }
417
418 vk::ImageMemoryBarrier barrier(
419 vk::AccessFlagBits::eTransferWrite, // srcAccessMask //TODO
420 vk::AccessFlagBits::eMemoryRead, //dstAccesMask //TODO
421 vk::ImageLayout::eTransferSrcOptimal, //oldLayout
422 vk::ImageLayout::eColorAttachmentOptimal, //newLayout
423 VK_QUEUE_FAMILY_IGNORED, // srcqueueFamily
424 VK_QUEUE_FAMILY_IGNORED, //dstQueueFamily
425 srcImage, //image
426 subRessources //subressourcesRange
427 );
428 buff.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
429 vk::PipelineStageFlagBits::eTopOfPipe, {}, 0, nullptr,
430 0, nullptr, 1, &barrier);
431 }
432 buff.end();
433}
434
435void VulkanDrawing::createSyncObjects()
436{
437
438 /*if (window->useOpenXR()) {
439 inFlightFences.resize(viewNumber);
440 for (int view = 0; view < viewNumber; view++) {
441 int swapSize = wrapper->getAttachmentSize();
442 inFlightFences[view].resize(swapSize);
443 vk::FenceCreateInfo fenceInfo = {};
444 for (size_t i = 0; i < swapSize; i++) {
445 inFlightFences[view][i] = context->device.createFence(fenceInfo);
446 }
447 }
448 }
449 else {*/
450 //imageAvailableSemaphores.resize(viewNumber);
451 //renderFinishedSemaphores.resize(viewNumber);
452 inFlightFences.resize(viewNumber);
453 imagesInFlight.resize(viewNumber);
454
455 for (int view = 0; view < viewNumber; view++) {
456 //imageAvailableSemaphores[view].resize(MAX_FRAMES_IN_FLIGHT);
457 //renderFinishedSemaphores[view].resize(MAX_FRAMES_IN_FLIGHT);
458 inFlightFences[view].resize(MAX_FRAMES_IN_FLIGHT);
459 imagesInFlight[view].resize(MAX_FRAMES_IN_FLIGHT, std::nullopt);
460
461 vk::FenceCreateInfo fenceInfo(vk::FenceCreateFlagBits::eSignaled);
462
463 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
464 //renderFinishedSemaphores[view][i] = context->device.createSemaphore(semaphoreInfo);
465 //imageAvailableSemaphores[view][i] = context->device.createSemaphore(semaphoreInfo);
466 inFlightFences[view][i] = context->device.createFence(fenceInfo);
467
468 }
469 //}
470 }
471}
472
474 return initialized;
475}
File that contain the VulkanContext class to manage Vulkan Instance, Physical device,...
Contains the class that manage the drawing process (manage and record command buffer,...
Class that contains helper functions for Vulkan.
file that contains the VulkanWrapper class that manages the classes related to Vulkan code and ease t...
Abstract interface around getting source views parameters and data.
Definition: InputProvider.h:35
class that manages tasks related to Vulkan context (Vulkan Instance, Vulkan Physical device,...
Definition: VulkanContext.h:59
vk::PhysicalDevice physicalDevice
Definition: VulkanContext.h:85
vk::Device device
Definition: VulkanContext.h:87
vk::Queue graphicsQueue
Definition: VulkanContext.h:89
bool isIndepFromWindowDimension()
QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device)
vk::Fence submitDrawCall(std::tuple< int, uint32_t > idImage, const std::vector< vk::Semaphore > &imageAvailableSemaphores, std::vector< vk::Semaphore > &signalSemaphore, uint32_t depthIndex=UINT_MAX)
void init(InputProvider *inputProvider)
VulkanDrawing(VulkanContext *context, VulkanRenderPassAbstract *renderPass, WindowAbstract *window, VulkanWrapper *wraps)
An abstract class that contains a common base of code for the class that inherit from it.
virtual vk::Image getImageToBlit(int imageIndex)=0
virtual void recordCommandBuffer(vk::CommandBuffer &commandBuffer, int i, std::span< InputProvider::StreamFrameInfo > frameInfos, int view)
virtual void updateBuffer(uint32_t currentImage, bool initAll, std::span< InputProvider::StreamFrameInfo > infos, int view)
Class that manages the classes related to Vulkan code and act as a wrapper around them.
Definition: VulkanWrapper.h:66
const std::vector< InputProvider::StreamFrameInfo > getFramesInfo(int view)
bool isDepthOutputRecquired()
vk::Extent2D getSwapchainExtend(int view=0)
Abstraction of the way of the result is displayed (screen or HMD).
virtual const bool useOpenXR()
virtual vk::Buffer getRenderingDestination()
virtual vk::Extent2D getSwapchainExtent(int view)
bool isSwapChainNeeded()
virtual std::vector< vk::Extent2D > getBlitExtentDestinations(int view, int elem)
virtual std::vector< vk::Image > getBlitDestinations(int view, int elem)
virtual vk::Image getSwapchainImage(int view, int index)
Struct to encapsulate the indice of the queues families.
Definition: VulkanContext.h:38
std::optional< uint32_t > graphicsFamily
Definition: VulkanContext.h:40