a0a9d7a4540145746c67902ef35317dbba645557
[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 = Fence::New(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, mFrameFence)->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->GetVkCommandBuffer();
116   {
117     std::array< vk::ClearValue, 2 > clearValues;
118
119     static float r = 1.0f;
120     //r += 0.01f;
121     if(r > 1.0f)
122       r -= 1.0f;
123
124     clearValues[0].color.setFloat32({0.0f, 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::eSecondaryCommandBuffers};
135     vkCmdBuf.beginRenderPass(rpInfo, subpassContents);
136   }
137 }
138
139 std::vector<vk::ClearValue> Surface::GetClearValues() const
140 {
141   static float r = 0.0f;
142   r += 0.01f;
143   if(r > 1.0f)
144     r -= 1.0f;
145
146   std::array< vk::ClearValue, 2 > clearValues;
147   clearValues[0].color.setFloat32({r, 0.0f, 0.0f, 1.0f});
148   clearValues[1].depthStencil.setDepth(1.0f).setStencil(0.0f);
149
150   auto retval = std::vector<vk::ClearValue>{};
151   retval.emplace_back( clearValues[0] );
152   if( mHasDepthStencil )
153   {
154     retval.emplace_back( clearValues[1] );
155   }
156   return retval;
157 }
158
159 vk::Extent2D Surface::GetSize() const
160 {
161   return mCapabilities->currentExtent;
162 }
163
164 Handle<CommandBuffer> Surface::GetCommandBuffer( uint32_t index )
165 {
166   return mSwapImages[index].mainCmdBuf;
167 }
168
169 Handle<CommandBuffer> Surface::GetCurrentCommandBuffer()
170 {
171   return mSwapImages[mCurrentBufferIndex].mainCmdBuf;
172 }
173
174 void Surface::EndRenderPass()
175 {
176   // todo: use semaphores and do not create fences all over again
177   auto& swapImage = mSwapImages[mCurrentBufferIndex];
178   auto  vkCmdBuf  = swapImage.mainCmdBuf->GetVkCommandBuffer();
179
180   // complete render pass
181   vkCmdBuf.endRenderPass();
182
183   // finalize command buffer
184   swapImage.mainCmdBuf->End();
185
186   // submit
187   auto& queue = mGraphics.GetGraphicsQueue();
188   queue.Submit(swapImage.mainCmdBuf, mFrameFence)->WaitForFence();
189 }
190
191 void Surface::Present()
192 {
193   // complete render pass and command buffer
194   EndRenderPass();
195
196   auto& swapImage = mSwapImages[mCurrentBufferIndex];
197   auto  result    = mGraphics.GetGraphicsQueue().Present(mSwapchain, mCurrentBufferIndex);
198   if(result != vk::Result::eSuccess)
199   {
200     //todo: handle swapchain invalidation
201   }
202   swapImage.layout = vk::ImageLayout::ePresentSrcKHR;
203   // todo: test result against swapchain expiration
204 }
205
206 void Surface::CreateSwapchain()
207 {
208   {
209     auto formats = VkAssert(mGraphics.GetPhysicalDevice().getSurfaceFormatsKHR(mSurface));
210     // find first which is not UNDEFINED
211     mFormat = vk::Format::eUndefined;
212     for(auto& format : formats)
213     {
214       if(format.format != vk::Format::eUndefined && mFormat == vk::Format::eUndefined)
215       {
216         mFormat     = format.format;
217         mColorSpace = format.colorSpace;
218       }
219     }
220   }
221
222   assert(mFormat != vk::Format::eUndefined && "No supported surface format!");
223
224   mCapabilities.reset(new vk::SurfaceCapabilitiesKHR(
225       VkAssert(mGraphics.GetPhysicalDevice().getSurfaceCapabilitiesKHR(mSurface))));
226
227   mExtent = mCapabilities->currentExtent;
228
229   CreateVulkanSwapchain();
230
231   // initialise default render pass
232   InitialiseRenderPass();
233
234   // if successful continue with obtaining images etc. also each swapchain will obtain default renderpass
235   InitialiseSwapchain();
236
237   // prerecord command buffers per each image in order to provide layout transition
238   CreateCommandBuffers();
239 }
240
241 void Surface::CreateVulkanSwapchain()
242 {
243   auto info = vk::SwapchainCreateInfoKHR{};
244
245   info.setClipped(true)
246       .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
247       .setImageArrayLayers(1)
248       .setImageColorSpace(mColorSpace)
249       .setImageExtent(mCapabilities->currentExtent)
250       .setImageFormat(mFormat)
251       .setImageSharingMode(vk::SharingMode::eExclusive)
252       .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
253       .setMinImageCount(mBufferCount)
254       .setPresentMode(vk::PresentModeKHR::eFifo)
255       .setPreTransform(mCapabilities->currentTransform)
256       .setOldSwapchain(nullptr)
257       .setPQueueFamilyIndices(nullptr)
258       .setQueueFamilyIndexCount(0)
259       .setSurface(mSurface);
260
261   mSwapchain = VkAssert(mGraphics.GetDevice().createSwapchainKHR(
262       reinterpret_cast< vk::SwapchainCreateInfoKHR& >(info), mGraphics.GetAllocator()));
263 }
264
265 void Surface::DestroySwapchain()
266 {
267   mGraphics.GetDevice().destroySwapchainKHR(mSwapchain, mGraphics.GetAllocator());
268 }
269
270 void Surface::InitialiseSwapchain()
271 {
272   const auto& device = mGraphics.GetDevice();
273
274   auto images = VkAssert(device.getSwapchainImagesKHR(mSwapchain));
275   assert(mBufferCount == images.size() && "Swapchain images count not equal requested value!");
276
277   {
278     auto swapImages = std::vector< SwapchainImage >{};
279
280     // for each image create framebuffer and image view
281     for(auto& image : images)
282     {
283       AddSwapchainImage(image, swapImages);
284     }
285
286     mSwapImages = std::move(swapImages);
287   }
288
289   if(mHasDepthStencil)
290   {
291     CreateDepthStencil();
292   }
293 }
294
295 void Surface::AddSwapchainImage(vk::Image image, std::vector< SwapchainImage >& swapchainImages)
296 {
297   auto swapImage  = std::move(SwapchainImage{});
298   swapImage.image = NewRef<Image>( mGraphics, vk::ImageCreateInfo{}, image );
299
300   // create ImageView
301   CreateImageView(swapImage);
302
303   // Create framebuffer ( there must be already render pass and information whether
304   // we use depth or not )
305   CreateFramebuffer(swapImage);
306
307   // initialise semaphores
308   CreateSemaphores(swapImage);
309
310   swapImage.layout = vk::ImageLayout::eUndefined;
311
312   swapchainImages.push_back(std::move(swapImage));
313 }
314
315 void Surface::CreateImageView(SwapchainImage& swapImage)
316 {
317   // Simple image view 2D as a color attachment
318   auto ivInfo = vk::ImageViewCreateInfo{};
319   ivInfo.setFormat(mFormat)
320       .setComponents(VK_COMPONENT_MAPPING_RGBA)
321       .setImage(swapImage.image->GetVkImage())
322       .setSubresourceRange(vk::ImageSubresourceRange()
323                                .setAspectMask(vk::ImageAspectFlagBits::eColor)
324                                .setBaseArrayLayer(0)
325                                .setBaseMipLevel(0)
326                                .setLayerCount(1)
327                                .setLevelCount(1))
328       .setViewType(vk::ImageViewType::e2D);
329
330   swapImage.imageView = ImageView::New( mGraphics, swapImage.image, ivInfo );
331 }
332
333 void Surface::CreateFramebuffer(SwapchainImage& swapImage)
334 {
335   vk::FramebufferCreateInfo fbInfo;
336   fbInfo.setAttachmentCount(mHasDepthStencil ? 2 : 1)
337       .setPAttachments(&swapImage.imageView->GetVkImageView()) // todo: add depth/stencil attachment
338       .setHeight(mExtent.height)
339       .setWidth(mExtent.width)
340       .setLayers(1)
341       .setRenderPass(mDefaultRenderPass);
342
343   swapImage.framebuffer =
344       VkAssert(mGraphics.GetDevice().createFramebuffer(fbInfo, mGraphics.GetAllocator()));
345 }
346
347 void Surface::CreateSemaphores(SwapchainImage& swapImage)
348 {
349   swapImage.acqSem =
350       VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
351   swapImage.presentSem =
352       VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
353 }
354
355 #pragma GCC diagnostic push
356 #pragma GCC diagnostic ignored "-Wframe-larger-than="
357 void Surface::InitialiseRenderPass()
358 {
359   auto att = std::vector<vk::AttachmentDescription>{ 2 };
360
361   // color attachment
362   att[0]
363       .setFormat(mFormat)
364       .setLoadOp(vk::AttachmentLoadOp::eClear)
365       .setStoreOp(vk::AttachmentStoreOp::eStore)
366       .setSamples(vk::SampleCountFlagBits::e1)
367       .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
368       .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
369       .setInitialLayout(vk::ImageLayout::eColorAttachmentOptimal)
370       .setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
371
372   // optional depth/stencil attachment
373   att[1]
374       .setFormat(mDepthStencilFormat)
375       .setLoadOp(vk::AttachmentLoadOp::eClear)
376       .setStoreOp(vk::AttachmentStoreOp::eDontCare)
377       .setSamples(vk::SampleCountFlagBits::e1)
378       .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
379       .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
380       .setInitialLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)
381       .setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
382
383   auto attRef = std::array<vk::AttachmentReference, 2>{};
384   attRef[0].setLayout(vk::ImageLayout::eColorAttachmentOptimal).setAttachment(0);
385   attRef[1].setLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal).setAttachment(1);
386
387   // prepare default subpass
388   vk::SubpassDescription subpass;
389   subpass.setColorAttachmentCount(1)
390       .setPColorAttachments(&attRef[0])
391       .setPDepthStencilAttachment(mHasDepthStencil ? &attRef[1] : nullptr)
392       .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
393
394   vk::RenderPassCreateInfo info;
395   info.setPAttachments(att.data()).setAttachmentCount(mHasDepthStencil ? 2 : 1).setPSubpasses(&subpass).setSubpassCount(1);
396
397   mDefaultRenderPass = VkAssert(mGraphics.GetDevice().createRenderPass(info, mGraphics.GetAllocator()));
398 }
399 #pragma GCC diagnostic pop
400
401 void Surface::CreateDepthStencil()
402 {
403   assert("Surface::CreateDepthStencil() not implemented!");
404   /// todo: implement
405 }
406
407 void Surface::DestroyDepthStencil()
408 {
409   /// todo: implement
410   assert("Surface::DestroyDepthStencil() not implemented!");
411 }
412
413 void Surface::CreateCommandBuffers()
414 {
415   if(!mCommandPool)
416   {
417     mCommandPool = CommandPool::New( mGraphics, vk::CommandPoolCreateInfo{}.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer) );
418   }
419
420   // allocate command buffers
421   auto cmdBuffers = std::vector< CommandBufferRef >{};
422   auto cmdInfo =
423       vk::CommandBufferAllocateInfo{}.setCommandBufferCount(1).setLevel(vk::CommandBufferLevel::ePrimary);
424
425   for(auto& swapImage : mSwapImages)
426   {
427     swapImage.layoutToColorCmdBuf = mCommandPool->NewCommandBuffer(cmdInfo);
428     swapImage.mainCmdBuf          = mCommandPool->NewCommandBuffer(cmdInfo);
429
430     // Record layout transition for each image, after transition command buffers will be re-recorded
431     // and will take in account only present -> color layout transition
432     swapImage.layoutToColorCmdBuf->Begin();
433     swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetVkImage(),
434                                                          swapImage.layout,
435                                                          vk::ImageLayout::eColorAttachmentOptimal,
436                                                          vk::ImageAspectFlagBits::eColor);
437     swapImage.layoutToColorCmdBuf->End();
438     swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
439
440     cmdBuffers.push_back(swapImage.layoutToColorCmdBuf);
441   }
442
443   // submit to the queue
444   {
445     auto& queue      = mGraphics.GetGraphicsQueue();
446     auto  fence      = Fence::New(mGraphics);
447     auto  submission = queue.Submit(cmdBuffers, fence);
448     submission->WaitForFence();
449   }
450
451   // record present to color transitions for each buffer for further reusing
452   for(auto& swapImage : mSwapImages)
453   {
454     swapImage.layoutToColorCmdBuf->Reset();
455     swapImage.layoutToColorCmdBuf->Begin();
456     swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetVkImage(),
457                                                          vk::ImageLayout::ePresentSrcKHR,
458                                                          vk::ImageLayout::eColorAttachmentOptimal,
459                                                          vk::ImageAspectFlagBits::eColor);
460     swapImage.layoutToColorCmdBuf->End();
461     swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
462   }
463 }
464
465 vk::SurfaceKHR Surface::GetSurfaceKHR() const
466 {
467   return mSurface;
468 }
469
470 vk::RenderPass Surface::GetRenderPass() const
471 {
472   return mDefaultRenderPass;
473 }
474
475 vk::Framebuffer Surface::GetFramebuffer(uint32_t index) const
476 {
477   return mSwapImages[index].framebuffer;
478 }
479
480 ImageView& Surface::GetImageView(uint32_t index) const
481 {
482   return *mSwapImages[index].imageView;
483 }
484
485 Image& Surface::GetImage(uint32_t index) const
486 {
487   return *mSwapImages[index].image;
488 }
489
490 } // namespace Vulkan
491 } // namespace Graphics
492 } // namespace Dali