Simplified Vulkan backend [WIP]
[platform/core/uifw/dali-core.git] / dali / graphics / vulkan / vulkan-surface.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // INTERNAL INCLUDES
19 #include <dali/graphics/vulkan/vulkan-command-buffer.h>
20 #include <dali/graphics/vulkan/vulkan-command-pool.h>
21 #include <dali/graphics/vulkan/vulkan-fence.h>
22 #include <dali/graphics/vulkan/vulkan-graphics.h>
23 #include <dali/graphics/vulkan/vulkan-queue.h>
24 #include <dali/graphics/vulkan/vulkan-surface.h>
25 #include <dali/graphics/vulkan/vulkan-image.h>
26
27 namespace Dali
28 {
29 namespace Graphics
30 {
31 namespace Vulkan
32 {
33
34 namespace
35 {
36 // constants
37 auto VK_COMPONENT_MAPPING_RGBA = vk::ComponentMapping{}
38                                      .setR(vk::ComponentSwizzle::eR)
39                                      .setG(vk::ComponentSwizzle::eG)
40                                      .setB(vk::ComponentSwizzle::eB)
41                                      .setA(vk::ComponentSwizzle::eA);
42 }
43
44 SwapchainImage::SwapchainImage()  = default;
45 SwapchainImage::~SwapchainImage() = default;
46
47 Surface::Surface(Graphics& graphics, vk::SurfaceKHR surface, uint32_t bufferCount, bool hasDepthStencil)
48 : mGraphics(graphics), mSurface(surface), mSwapchain{nullptr}, mBufferCount{bufferCount}, mHasDepthStencil(hasDepthStencil)
49 {
50 }
51
52 Surface::~Surface()
53 {
54   if(mSwapchain)
55   {
56     DestroySwapchain();
57   }
58   if(mSurface)
59   {
60     mGraphics.GetInstance().destroySurfaceKHR(mSurface, mGraphics.GetAllocator());
61   }
62 }
63
64 void Surface::AcquireNextImage()
65 {
66   // if swapchain hasn't been created yet, create it
67   // TODO: deferring may bring a lag when surface is swapped very first time
68   // it might be good to have an option to create swapchain regardless further usage
69   if(!mSwapchain)
70   {
71     CreateSwapchain();
72   }
73
74   if(!mFrameFence)
75   {
76     mFrameFence = MakeUnique< Fence >(mGraphics);
77   }
78
79   // acquire image, for simplicity using fence for acquiring as it is unknown what next command buffer will
80   // be executed yet
81   mFrameFence->Reset();
82   uint32_t index = VkAssert(mGraphics.GetDevice().acquireNextImageKHR(mSwapchain, 1000000, nullptr,
83                                                                       mFrameFence->GetFence()));
84   mFrameFence->Wait();
85   mFrameFence->Reset();
86
87   mCurrentBufferIndex = index;
88
89   auto& swapImage = mSwapImages[index];
90
91   // change layout if necessary to color attachment
92   if(swapImage.layout != vk::ImageLayout::eColorAttachmentOptimal)
93   {
94     auto& queue = mGraphics.GetGraphicsQueue();
95     queue.Submit(*swapImage.layoutToColorCmdBuf.get(), *mFrameFence.get())->WaitForFence();
96   }
97
98   mFrameFence->Reset();
99
100   // todo: anything to be done before beginning main command buffer?
101   BeginRenderPass();
102 }
103
104 void Surface::BeginRenderPass()
105 {
106   auto& swapImage = mSwapImages[mCurrentBufferIndex];
107
108   // begin command buffer ( can be directly obtained through Graphics( surface )
109   swapImage.mainCmdBuf->Begin(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
110
111   /*
112    * todo: automatically start main render pass -> this may have to be done manually in future
113    * if more flexibility is needed
114    */
115   auto vkCmdBuf = swapImage.mainCmdBuf->Get();
116   {
117     std::array< vk::ClearValue, 2 > clearValues;
118
119     static float r = 0.0f;
120     r += 0.01f;
121     if(r > 1.0f)
122       r -= 1.0f;
123
124     clearValues[0].color.setFloat32({r, 0.0f, 0.0f, 1.0f});
125     clearValues[1].depthStencil.setDepth(1.0f).setStencil(0.0f);
126
127     auto rpInfo = vk::RenderPassBeginInfo{};
128     rpInfo.setRenderPass(mDefaultRenderPass)
129         .setFramebuffer(swapImage.framebuffer)
130         .setRenderArea(vk::Rect2D{}.setOffset({0, 0}).setExtent(mExtent))
131         .setClearValueCount(mHasDepthStencil ? 2 : 1)
132         .setPClearValues(clearValues.data());
133
134     auto subpassContents = vk::SubpassContents{vk::SubpassContents::eInline};
135     vkCmdBuf.beginRenderPass(rpInfo, subpassContents);
136   }
137 }
138
139 void Surface::EndRenderPass()
140 {
141   // todo: use semaphores and do not create fences all over again
142   auto& swapImage = mSwapImages[mCurrentBufferIndex];
143   auto  vkCmdBuf  = swapImage.mainCmdBuf->Get();
144
145   // complete render pass
146   vkCmdBuf.endRenderPass();
147
148   // finalize command buffer
149   swapImage.mainCmdBuf->End();
150
151   // submit
152   auto& queue = mGraphics.GetGraphicsQueue();
153   queue.Submit(*swapImage.mainCmdBuf.get(), *mFrameFence.get())->WaitForFence();
154 }
155
156 void Surface::Present()
157 {
158   // complete render pass and command buffer
159   EndRenderPass();
160
161   auto& swapImage = mSwapImages[mCurrentBufferIndex];
162   auto  result    = mGraphics.GetGraphicsQueue().Present(mSwapchain, mCurrentBufferIndex);
163   if(result != vk::Result::eSuccess)
164   {
165     //todo: handle swapchain invalidation
166   }
167   swapImage.layout = vk::ImageLayout::ePresentSrcKHR;
168   // todo: test result against swapchain expiration
169 }
170
171 void Surface::CreateSwapchain()
172 {
173   {
174     auto formats = VkAssert(mGraphics.GetPhysicalDevice().getSurfaceFormatsKHR(mSurface));
175     // find first which is not UNDEFINED
176     mFormat = vk::Format::eUndefined;
177     for(auto& format : formats)
178     {
179       if(format.format != vk::Format::eUndefined && mFormat == vk::Format::eUndefined)
180       {
181         mFormat     = format.format;
182         mColorSpace = format.colorSpace;
183       }
184     }
185   }
186
187   assert(mFormat != vk::Format::eUndefined && "No supported surface format!");
188
189   mCapabilities.reset(new vk::SurfaceCapabilitiesKHR(
190       VkAssert(mGraphics.GetPhysicalDevice().getSurfaceCapabilitiesKHR(mSurface))));
191
192   mExtent = mCapabilities->currentExtent;
193
194   CreateVulkanSwapchain();
195
196   // initialise default render pass
197   InitialiseRenderPass();
198
199   // if successful continue with obtaining images etc. also each swapchain will obtain default renderpass
200   InitialiseSwapchain();
201
202   // prerecord command buffers per each image in order to provide layout transition
203   CreateCommandBuffers();
204 }
205
206 void Surface::CreateVulkanSwapchain()
207 {
208   auto info = vk::SwapchainCreateInfoKHR{};
209
210   info.setClipped(true)
211       .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
212       .setImageArrayLayers(1)
213       .setImageColorSpace(mColorSpace)
214       .setImageExtent(mCapabilities->currentExtent)
215       .setImageFormat(mFormat)
216       .setImageSharingMode(vk::SharingMode::eExclusive)
217       .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
218       .setMinImageCount(mBufferCount)
219       .setPresentMode(vk::PresentModeKHR::eFifo)
220       .setPreTransform(mCapabilities->currentTransform)
221       .setOldSwapchain(nullptr)
222       .setPQueueFamilyIndices(nullptr)
223       .setQueueFamilyIndexCount(0)
224       .setSurface(mSurface);
225
226   mSwapchain = VkAssert(mGraphics.GetDevice().createSwapchainKHR(
227       reinterpret_cast< vk::SwapchainCreateInfoKHR& >(info), mGraphics.GetAllocator()));
228 }
229
230 void Surface::DestroySwapchain()
231 {
232   mGraphics.GetDevice().destroySwapchainKHR(mSwapchain, mGraphics.GetAllocator());
233 }
234
235 void Surface::InitialiseSwapchain()
236 {
237   const auto& device = mGraphics.GetDevice();
238
239   auto images = VkAssert(device.getSwapchainImagesKHR(mSwapchain));
240   assert(mBufferCount == images.size() && "Swapchain images count not equal requested value!");
241
242   {
243     auto swapImages = std::vector< SwapchainImage >{};
244
245     // for each image create framebuffer and image view
246     for(auto& image : images)
247     {
248       AddSwapchainImage(image, swapImages);
249     }
250
251     mSwapImages = std::move(swapImages);
252   }
253
254   if(mHasDepthStencil)
255   {
256     CreateDepthStencil();
257   }
258 }
259
260 void Surface::AddSwapchainImage(vk::Image image, std::vector< SwapchainImage >& swapchainImages)
261 {
262   auto swapImage  = std::move(SwapchainImage{});
263   swapImage.image = MakeUnique<Image>( mGraphics, image );
264
265   // create ImageView
266   CreateImageView(swapImage);
267
268   // Create framebuffer ( there must be already render pass and information whether
269   // we use depth or not )
270   CreateFramebuffer(swapImage);
271
272   // initialise semaphores
273   CreateSemaphores(swapImage);
274
275   swapImage.layout = vk::ImageLayout::eUndefined;
276
277   swapchainImages.push_back(std::move(swapImage));
278 }
279
280 void Surface::CreateImageView(SwapchainImage& swapImage)
281 {
282   // Simple image view 2D as a color attachment
283   auto ivInfo = vk::ImageViewCreateInfo{};
284   ivInfo.setFormat(mFormat)
285       .setComponents(VK_COMPONENT_MAPPING_RGBA)
286       .setImage(swapImage.image->GetImage())
287       .setSubresourceRange(vk::ImageSubresourceRange()
288                                .setAspectMask(vk::ImageAspectFlagBits::eColor)
289                                .setBaseArrayLayer(0)
290                                .setBaseMipLevel(0)
291                                .setLayerCount(1)
292                                .setLevelCount(1))
293       .setViewType(vk::ImageViewType::e2D);
294
295   swapImage.imageView = swapImage.image->CreateView( ivInfo );
296 }
297
298 void Surface::CreateFramebuffer(SwapchainImage& swapImage)
299 {
300   vk::FramebufferCreateInfo fbInfo;
301   fbInfo.setAttachmentCount(mHasDepthStencil ? 2 : 1)
302       .setPAttachments(&swapImage.imageView->GetImageView()) // todo: add depth/stencil attachment
303       .setHeight(mExtent.height)
304       .setWidth(mExtent.width)
305       .setLayers(1)
306       .setRenderPass(mDefaultRenderPass);
307
308   swapImage.framebuffer =
309       VkAssert(mGraphics.GetDevice().createFramebuffer(fbInfo, mGraphics.GetAllocator()));
310 }
311
312 void Surface::CreateSemaphores(SwapchainImage& swapImage)
313 {
314   swapImage.acqSem =
315       VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
316   swapImage.presentSem =
317       VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
318 }
319
320 #pragma GCC diagnostic push
321 #pragma GCC diagnostic ignored "-Wframe-larger-than="
322 void Surface::InitialiseRenderPass()
323 {
324   auto att = std::vector<vk::AttachmentDescription>{ 2 };
325
326   // color attachment
327   att[0]
328       .setFormat(mFormat)
329       .setLoadOp(vk::AttachmentLoadOp::eClear)
330       .setStoreOp(vk::AttachmentStoreOp::eStore)
331       .setSamples(vk::SampleCountFlagBits::e1)
332       .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
333       .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
334       .setInitialLayout(vk::ImageLayout::eColorAttachmentOptimal)
335       .setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
336
337   // optional depth/stencil attachment
338   att[1]
339       .setFormat(mDepthStencilFormat)
340       .setLoadOp(vk::AttachmentLoadOp::eClear)
341       .setStoreOp(vk::AttachmentStoreOp::eDontCare)
342       .setSamples(vk::SampleCountFlagBits::e1)
343       .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
344       .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
345       .setInitialLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)
346       .setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
347
348   auto attRef = std::array<vk::AttachmentReference, 2>{};
349   attRef[0].setLayout(vk::ImageLayout::eColorAttachmentOptimal).setAttachment(0);
350   attRef[1].setLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal).setAttachment(1);
351
352   // prepare default subpass
353   vk::SubpassDescription subpass;
354   subpass.setColorAttachmentCount(1)
355       .setPColorAttachments(&attRef[0])
356       .setPDepthStencilAttachment(mHasDepthStencil ? &attRef[1] : nullptr)
357       .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
358
359   vk::RenderPassCreateInfo info;
360   info.setPAttachments(att.data()).setAttachmentCount(mHasDepthStencil ? 2 : 1).setPSubpasses(&subpass).setSubpassCount(1);
361
362   mDefaultRenderPass = VkAssert(mGraphics.GetDevice().createRenderPass(info, mGraphics.GetAllocator()));
363 }
364 #pragma GCC diagnostic pop
365
366 void Surface::CreateDepthStencil()
367 {
368   assert("Surface::CreateDepthStencil() not implemented!");
369   /// todo: implement
370 }
371
372 void Surface::DestroyDepthStencil()
373 {
374   /// todo: implement
375   assert("Surface::DestroyDepthStencil() not implemented!");
376 }
377
378 void Surface::CreateCommandBuffers()
379 {
380   if(!mCommandPool)
381   {
382     auto info = vk::CommandPoolCreateInfo{}.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer);
383     info.setQueueFamilyIndex(0); // todo: get correct queue family index ( 0 works by default ;) )
384     mCommandPool = MakeUnique< CommandPool >(mGraphics, info);
385   }
386
387   // allocate command buffers
388   auto cmdBuffers = std::vector< CommandBufferRef >{};
389   auto cmdInfo =
390       vk::CommandBufferAllocateInfo{}.setCommandBufferCount(1).setLevel(vk::CommandBufferLevel::ePrimary);
391
392   for(auto& swapImage : mSwapImages)
393   {
394     swapImage.layoutToColorCmdBuf = mCommandPool->AllocateCommandBuffer(cmdInfo);
395     swapImage.mainCmdBuf          = mCommandPool->AllocateCommandBuffer(cmdInfo);
396
397     // Record layout transition for each image, after transition command buffers will be re-recorded
398     // and will take in account only present -> color layout transition
399     swapImage.layoutToColorCmdBuf->Begin();
400     swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetImage(),
401                                                          swapImage.layout,
402                                                          vk::ImageLayout::eColorAttachmentOptimal,
403                                                          vk::ImageAspectFlagBits::eColor);
404     swapImage.layoutToColorCmdBuf->End();
405     swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
406
407     cmdBuffers.push_back(std::ref(*swapImage.layoutToColorCmdBuf.get()));
408   }
409
410   // submit to the queue
411   {
412     auto& queue      = mGraphics.GetGraphicsQueue();
413     auto  fence      = Fence(mGraphics);
414     auto  submission = queue.Submit(cmdBuffers, fence);
415     submission->WaitForFence();
416   }
417
418   // record present to color transitions for each buffer for further reusing
419   for(auto& swapImage : mSwapImages)
420   {
421     swapImage.layoutToColorCmdBuf->Reset();
422     swapImage.layoutToColorCmdBuf->Begin();
423     swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetImage(),
424                                                          vk::ImageLayout::ePresentSrcKHR,
425                                                          vk::ImageLayout::eColorAttachmentOptimal,
426                                                          vk::ImageAspectFlagBits::eColor);
427     swapImage.layoutToColorCmdBuf->End();
428     swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
429   }
430 }
431
432 vk::SurfaceKHR Surface::GetSurfaceKHR() const
433 {
434   return mSurface;
435 }
436
437 vk::RenderPass Surface::GetRenderPass() const
438 {
439   return mDefaultRenderPass;
440 }
441
442 vk::Framebuffer Surface::GetFramebuffer(uint32_t index) const
443 {
444   return mSwapImages[index].framebuffer;
445 }
446
447 ImageView& Surface::GetImageView(uint32_t index) const
448 {
449   return *mSwapImages[index].imageView;
450 }
451
452 Image& Surface::GetImage(uint32_t index) const
453 {
454   return *mSwapImages[index].image;
455 }
456
457 } // namespace Vulkan
458 } // namespace Graphics
459 } // namespace Dali