[Vulkan] Image and Swapchain upgrade
[platform/core/uifw/dali-core.git] / dali / graphics / vulkan / vulkan-swapchain.cpp
1 /*
2  * Copyright (c) 2018 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/gpu-memory/vulkan-gpu-memory-allocator.h>
20 #include <dali/graphics/vulkan/gpu-memory/vulkan-gpu-memory-handle.h>
21 #include <dali/graphics/vulkan/gpu-memory/vulkan-gpu-memory-manager.h>
22 #include <dali/graphics/vulkan/vulkan-command-buffer.h>
23 #include <dali/graphics/vulkan/vulkan-command-pool.h>
24 #include <dali/graphics/vulkan/vulkan-fence.h>
25 #include <dali/graphics/vulkan/vulkan-framebuffer.h>
26 #include <dali/graphics/vulkan/vulkan-graphics.h>
27 #include <dali/graphics/vulkan/vulkan-image.h>
28 #include <dali/graphics/vulkan/vulkan-queue.h>
29 #include <dali/graphics/vulkan/vulkan-surface.h>
30 #include <dali/graphics/vulkan/vulkan-swapchain.h>
31 namespace Dali
32 {
33 namespace Graphics
34 {
35 namespace Vulkan
36 {
37 /**
38  * SwapchainBuffer stores all per-buffer data
39  */
40 struct SwapchainBuffer
41 {
42   /*
43    * Each buffer has own master command buffer which executes
44    * secondary buffers
45    */
46   CommandBufferRef masterCmdBuffer;
47
48   /*
49    * Each buffer has a command pool to allocate from
50    */
51   CommandPoolRef masterCommandPool;
52
53   /*
54    * Framebuffer object associated with the buffer
55    */
56   FramebufferRef framebuffer;
57
58   /*
59    * Sync primitives
60    */
61   FenceRef endOfFrameFence;
62
63   /*
64    * Buffer index
65    */
66   uint32_t index;
67
68   /*
69    * First use before presenting
70    */
71   bool firstUse;
72 };
73
74 struct Swapchain::Impl
75 {
76   Impl( Swapchain& owner,
77         Graphics&  graphics,
78         Queue&     presentationQueue,
79         SurfaceRef surface,
80         uint32_t   bufferCount,
81         uint32_t   flags )
82   : mOwner( owner ),
83     mGraphics( graphics ),
84     mQueue( presentationQueue ),
85     mSurface( surface ),
86     mBufferCount( bufferCount ),
87     mFlags( flags )
88   {
89     mSwapchainCreateInfoKHR.setSurface( mSurface->GetSurfaceKHR() )
90       .setPreTransform( vk::SurfaceTransformFlagBitsKHR::eIdentity )
91       .setPresentMode( vk::PresentModeKHR::eFifo )
92       .setOldSwapchain( nullptr ) //@todo support surface replacement!
93       .setMinImageCount( mBufferCount )
94       .setImageUsage( vk::ImageUsageFlagBits::eColorAttachment )
95       .setImageSharingMode( vk::SharingMode::eExclusive )
96       .setImageArrayLayers( 1 )
97       .setCompositeAlpha( vk::CompositeAlphaFlagBitsKHR::eOpaque )
98       .setClipped( true )
99       .setQueueFamilyIndexCount( 0 )
100       .setPQueueFamilyIndices( nullptr );
101   }
102
103   ~Impl() = default;
104
105   Impl( const Impl& ) = delete;
106   Impl& operator=( const Impl& ) = delete;
107
108   bool Initialise()
109   {
110     if( !SetImageFormat() )
111     {
112       return false;
113     }
114
115     // get extents
116     mSwapchainExtent = mSurface->GetSize();
117
118     mSwapchainCreateInfoKHR.setImageFormat( mSwapchainImageFormat );
119     mSwapchainCreateInfoKHR.setImageExtent( mSwapchainExtent );
120     mSwapchainCreateInfoKHR.setImageColorSpace( mSwapchainColorSpace );
121
122     Create();
123
124     InitialiseSwapchainBuffers();
125
126     PrepareFramebuffers();
127
128     mFirstPresent = true;
129     return true;
130   }
131
132   bool InitialiseSwapchainBuffers()
133   {
134     mSwapchainBuffer.clear();
135     for( auto&& fb : mFramebuffers )
136     {
137       auto cmdPool   = CommandPool::New( mGraphics, vk::CommandPoolCreateInfo{}.setFlags( vk::CommandPoolCreateFlagBits::eResetCommandBuffer ) );
138       auto masterCmd = cmdPool->NewCommandBuffer( true );
139
140       auto swapBuffer              = SwapchainBuffer{};
141       swapBuffer.framebuffer       = fb;
142       swapBuffer.index             = 0;
143       swapBuffer.masterCmdBuffer   = masterCmd;
144       swapBuffer.masterCommandPool = cmdPool;
145       swapBuffer.endOfFrameFence   = Fence::New( mGraphics );
146       swapBuffer.firstUse          = true;
147       mSwapchainBuffer.emplace_back( swapBuffer );
148     }
149
150     return true;
151   }
152
153 #pragma GCC diagnostic push
154 #pragma GCC diagnostic ignored "-Wframe-larger-than="
155   void PrepareFramebuffers()
156   {
157     /*
158      * After creating the new swapchain we need to make sure
159      * the layout of images is correct to start with. To do so
160      * we will wait till the whole device is idle ( there should
161      * be a mechanism preventing from using the GPU past that point )
162      * and submit pipeline barriers setting up the layouts of
163      * all framebuffer images in one go. There will be no fancy
164      * synchronisation here as it's not really needed at this point.
165      * Waiting for queue or device idle should be enough.
166      */
167
168     const auto& device    = mGraphics.GetDevice();
169
170     device.waitIdle();
171
172     /*
173      * Create temporary command pool
174      */
175     auto commandPool = CommandPool::New( mGraphics );
176     auto cmdBuffer   = commandPool->NewCommandBuffer();
177
178     std::vector<vk::ImageMemoryBarrier> barriers;
179     ImageViewRef                        depthStencilImage{};
180
181     for( auto&& buffer : mSwapchainBuffer )
182     {
183       auto colorImages = buffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
184
185       // expecting to use one depth stencil image for all swapbuffers
186       if( !depthStencilImage )
187       {
188         depthStencilImage = buffer.framebuffer->GetAttachment( Framebuffer::AttachmentType::DEPTH_STENCIL, 0u );
189       }
190
191       /*
192        * Add barriers for color images
193        */
194       for( auto&& colorImageView : colorImages )
195       {
196         auto image   = colorImageView->GetImage();
197         auto vkImage = image->GetVkImage();
198
199         vk::ImageSubresourceRange range;
200         range.setLayerCount( image->GetLayerCount() )
201           .setLevelCount( image->GetLevelCount() )
202           .setBaseMipLevel( 0 )
203           .setBaseArrayLayer( 0 )
204           .setAspectMask( vk::ImageAspectFlagBits::eColor );
205         auto colorBarrier = vk::ImageMemoryBarrier{}
206                               .setImage( vkImage )
207                               .setSubresourceRange( range )
208                               .setSrcAccessMask( vk::AccessFlags{} )
209                               .setDstAccessMask( vk::AccessFlagBits::eColorAttachmentWrite )
210                               .setOldLayout( vk::ImageLayout::eUndefined )
211                               .setNewLayout( vk::ImageLayout::eColorAttachmentOptimal );
212
213         barriers.emplace_back( colorBarrier );
214       }
215     }
216
217     /*
218      * Add barrier for depth stencil image
219      */
220     if( depthStencilImage )
221     {
222       auto image   = depthStencilImage->GetImage();
223       auto vkImage = image->GetVkImage();
224
225       vk::ImageSubresourceRange range;
226       range.setLayerCount( image->GetLayerCount() )
227         .setLevelCount( image->GetLevelCount() )
228         .setBaseMipLevel( 0 )
229         .setBaseArrayLayer( 0 )
230         .setAspectMask( vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil );
231       auto depthStencilBarrier = vk::ImageMemoryBarrier{}
232                                    .setImage( vkImage )
233                                    .setSubresourceRange( range )
234                                    .setSrcAccessMask( vk::AccessFlags{} )
235                                    .setDstAccessMask( vk::AccessFlagBits::eDepthStencilAttachmentWrite )
236                                    .setOldLayout( vk::ImageLayout::eUndefined )
237                                    .setNewLayout( vk::ImageLayout::eDepthStencilAttachmentOptimal );
238       barriers.emplace_back( depthStencilBarrier );
239     }
240
241     /*
242      * Record command buffer with pipeline barrier
243      */
244     cmdBuffer->Begin( vk::CommandBufferUsageFlagBits::eOneTimeSubmit );
245     cmdBuffer->PipelineBarrier( vk::PipelineStageFlagBits::eTopOfPipe,
246                                 vk::PipelineStageFlagBits::eTopOfPipe,
247                                 vk::DependencyFlags{},
248                                 std::vector<vk::MemoryBarrier>{},
249                                 std::vector<vk::BufferMemoryBarrier>{},
250                                 barriers );
251     cmdBuffer->End();
252
253     // use presentation queue to submit the call
254     mQueue.Submit( cmdBuffer, FenceRef{} );
255     mQueue.WaitIdle();
256   }
257 #pragma GCC diagnostic pop
258
259   bool SetImageFormat()
260   {
261     // obtain supported image format
262     auto formats          = VkAssert( mGraphics.GetPhysicalDevice().getSurfaceFormatsKHR( mSurface->GetSurfaceKHR() ) );
263     mSwapchainImageFormat = vk::Format::eUndefined;
264
265     for( auto&& format : formats )
266     {
267       if( format.format != vk::Format::eUndefined )
268       {
269         mSwapchainColorSpace  = format.colorSpace;
270         mSwapchainImageFormat = format.format;
271         break;
272       }
273     }
274
275     if( vk::Format::eUndefined == mSwapchainImageFormat )
276     {
277       return false;
278     }
279
280     return true;
281   }
282
283   /**
284    * Creates swapchain immediately
285    * @return
286    */
287   bool Create()
288   {
289     const auto& device    = mGraphics.GetDevice();
290     const auto& allocator = mGraphics.GetAllocator();
291
292     //@todo validation
293     mSwapchainKHR = VkAssert( device.createSwapchainKHR( mSwapchainCreateInfoKHR, allocator ) );
294
295     if( !mSwapchainKHR )
296     {
297       return false;
298     }
299
300     // pull images and create Framebuffers
301     auto images = VkAssert( device.getSwapchainImagesKHR( mSwapchainKHR ) );
302
303     // number of images must match requested buffering mode
304     if( images.size() != mBufferCount )
305     {
306       device.destroySwapchainKHR( mSwapchainKHR );
307       mSwapchainKHR = nullptr;
308       return false;
309     }
310
311     //@todo create depth-stencil image
312     auto depthStencilImageView = CreateDepthStencil();
313
314     /*
315      * CREATE FRAMEBUFFERS
316      */
317     for( auto&& image : images )
318     {
319       mFramebuffers.emplace_back( CreateFramebuffer( image ) );
320
321       // set depth/stencil if supported
322       if( depthStencilImageView )
323       {
324         mFramebuffers.back()->SetAttachment( depthStencilImageView, Framebuffer::AttachmentType::DEPTH_STENCIL, 0u );
325       }
326
327       // create framebuffer and compatible render pass right now, no need to defer it
328       mFramebuffers.back()->Commit();
329     }
330
331     return true;
332   }
333
334   /**
335    * Creates depth stencil if necessary
336    * @return
337    */
338   ImageViewRef CreateDepthStencil()
339   {
340     // create depth stencil image
341     auto dsImageRef = Image::New( mGraphics,
342                                   vk::ImageCreateInfo{}
343                                     .setFormat( vk::Format::eD24UnormS8Uint )
344                                     .setMipLevels( 1 )
345                                     .setTiling( vk::ImageTiling::eOptimal )
346                                     .setImageType( vk::ImageType::e2D )
347                                     .setArrayLayers( 1 )
348                                     .setExtent( {mSwapchainExtent.width, mSwapchainExtent.height, 1} )
349                                     .setUsage( vk::ImageUsageFlagBits::eDepthStencilAttachment )
350                                     .setSharingMode( vk::SharingMode::eExclusive )
351                                     .setInitialLayout( vk::ImageLayout::eUndefined )
352                                     .setSamples( vk::SampleCountFlagBits::e1 ) );
353
354     auto memory = mGraphics.GetDeviceMemoryManager().GetDefaultAllocator().Allocate(
355       dsImageRef, vk::MemoryPropertyFlagBits::eDeviceLocal );
356
357     dsImageRef->BindMemory( memory );
358
359     // create imageview to be used within framebuffer
360     auto dsImageViewRef = ImageView::New( mGraphics, dsImageRef );
361     return dsImageViewRef;
362   }
363
364   /**
365    * Creates a Framebuffer and compatible RenderPass
366    * @param image
367    * @return
368    */
369 #pragma GCC diagnostic push
370 #pragma GCC diagnostic ignored "-Wframe-larger-than="
371   FramebufferRef CreateFramebuffer( vk::Image& image )
372   {
373     auto fbRef = Framebuffer::New( mGraphics, mSwapchainExtent.width, mSwapchainExtent.height );
374
375     // Create external Image reference
376     // Note that despite we don't create VkImage, we still fill the createinfo structure
377     // as this data will be used later
378     ImageRef imageRef = Image::New( mGraphics,
379                                     vk::ImageCreateInfo{}
380                                       .setFormat( mSwapchainImageFormat )
381                                       .setSamples( vk::SampleCountFlagBits::e1 )
382                                       .setInitialLayout( vk::ImageLayout::eUndefined )
383                                       .setSharingMode( vk::SharingMode::eExclusive )
384                                       .setUsage( vk::ImageUsageFlagBits::eColorAttachment )
385                                       .setExtent( {mSwapchainExtent.width, mSwapchainExtent.height, 1} )
386                                       .setArrayLayers( 1 )
387                                       .setImageType( vk::ImageType::e2D )
388                                       .setTiling( vk::ImageTiling::eOptimal )
389                                       .setMipLevels( 1 ),
390                                     image );
391
392     // Create basic imageview ( all mipmaps, all layers )
393     ImageViewRef iv = ImageView::New( mGraphics, imageRef );
394
395     fbRef->SetAttachment( iv, Framebuffer::AttachmentType::COLOR, 0u );
396
397     return fbRef;
398   }
399 #pragma GCC diagnostic pop
400
401   /**
402    * This function acquires next framebuffer
403    * @todo we should rather use roundrobin method
404    * @return
405    */
406 #pragma GCC diagnostic push
407 #pragma GCC diagnostic ignored "-Wframe-larger-than="
408   FramebufferRef AcquireNextFramebuffer()
409   {
410     const auto& device    = mGraphics.GetDevice();
411
412     if( !mFrameFence )
413     {
414       mFrameFence = Fence::New( mGraphics );
415     }
416
417     mCurrentBufferIndex =
418       VkAssert( device.acquireNextImageKHR( mSwapchainKHR, 1000000, nullptr, mFrameFence->GetFence() ) );
419
420     mFrameFence->Wait();
421     mFrameFence->Reset();
422
423     auto& swapBuffer = mSwapchainBuffer[mCurrentBufferIndex];
424
425     // start recording
426     auto inheritanceInfo = vk::CommandBufferInheritanceInfo{}
427       .setFramebuffer( swapBuffer.framebuffer->GetVkFramebuffer() )
428       .setRenderPass(swapBuffer.framebuffer->GetVkRenderPass())
429       .setSubpass( 0 );
430     swapBuffer.masterCmdBuffer->Reset();
431     swapBuffer.masterCmdBuffer->Begin( vk::CommandBufferUsageFlagBits::eRenderPassContinue, &inheritanceInfo );
432
433     // change layout from present to color attachment if not done yet
434     // ( swapchain must track that )
435     if(!swapBuffer.firstUse)
436     {
437       UpdateLayoutPresentToColorAttachment(swapBuffer);
438     }
439     swapBuffer.firstUse = false;
440
441     // Begins primary render pass
442     BeginPrimaryRenderPass( swapBuffer );
443
444     return swapBuffer.framebuffer;
445   }
446 #pragma GCC diagnostic pop
447   void BeginPrimaryRenderPass( SwapchainBuffer& currentBuffer )
448   {
449     vk::RenderPassBeginInfo rpInfo{};
450     rpInfo.setRenderPass( currentBuffer.framebuffer->GetVkRenderPass() )
451       .setFramebuffer( currentBuffer.framebuffer->GetVkFramebuffer() )
452       .setPClearValues( currentBuffer.framebuffer->GetDefaultClearValues().data() )
453       .setClearValueCount( U32( currentBuffer.framebuffer->GetDefaultClearValues().size() ) )
454       .setRenderArea( vk::Rect2D( {0, 0}, mSurface->GetSize() ) );
455
456     currentBuffer.masterCmdBuffer->BeginRenderPass( rpInfo, vk::SubpassContents::eSecondaryCommandBuffers );
457   }
458
459   void EndPrimaryRenderPass( SwapchainBuffer& currentBuffer )
460   {
461     currentBuffer.masterCmdBuffer->EndRenderPass();
462   }
463
464   /**
465    * Performs layout transition for all the color attachments
466    * in the current framebuffer.
467    * The master command buffer must be in the recording state
468    * @param swapBuffer
469    */
470 #pragma GCC diagnostic push
471 #pragma GCC diagnostic ignored "-Wframe-larger-than="
472   void UpdateLayoutPresentToColorAttachment( SwapchainBuffer& swapBuffer )
473   {
474     auto& cmdBuf = swapBuffer.masterCmdBuffer;
475
476     //todo: test the state of th ebuffer, must be recording
477
478     auto attachments = swapBuffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
479
480     std::vector<vk::ImageMemoryBarrier> barriers;
481     vk::ImageMemoryBarrier              barrier;
482     barrier.setSrcAccessMask( vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eColorAttachmentRead )
483       .setDstAccessMask( vk::AccessFlagBits::eColorAttachmentWrite )
484       .setOldLayout( vk::ImageLayout::ePresentSrcKHR )
485       .setNewLayout( vk::ImageLayout::eColorAttachmentOptimal )
486       .setSubresourceRange( vk::ImageSubresourceRange{}.setAspectMask( vk::ImageAspectFlagBits::eColor ).setBaseArrayLayer(0)
487                                                        .setBaseMipLevel(0)
488                                                        .setLevelCount(1)
489                                                        .setLayerCount(1));
490
491     for( auto&& imageView : attachments )
492     {
493       barriers.emplace_back( barrier.setImage( *imageView->GetImage() ) );
494     }
495
496     cmdBuf->PipelineBarrier( vk::PipelineStageFlagBits::eBottomOfPipe,
497                              vk::PipelineStageFlagBits::eColorAttachmentOutput,
498                              vk::DependencyFlags{},
499                              std::vector<vk::MemoryBarrier>{},
500                              std::vector<vk::BufferMemoryBarrier>{},
501                              barriers );
502   }
503 #pragma GCC diagnostic pop
504
505   CommandBufferRef GetPrimaryCommandBuffer() const
506   {
507     return mSwapchainBuffer[mCurrentBufferIndex].masterCmdBuffer;
508   }
509
510   bool Present()
511   {
512     auto& swapBuffer = mSwapchainBuffer[mCurrentBufferIndex];
513
514     // end render pass
515     EndPrimaryRenderPass( swapBuffer );
516
517     // end command buffer
518     swapBuffer.masterCmdBuffer->End();
519
520     // submit
521     swapBuffer.endOfFrameFence->Reset();
522     mQueue.Submit( swapBuffer.masterCmdBuffer, swapBuffer.endOfFrameFence );
523     swapBuffer.endOfFrameFence->Wait( 0u );
524
525     // fixme: use semaphores to synchronize all previously submitted command buffers!
526     vk::PresentInfoKHR presentInfo{};
527     vk::Result result;
528     presentInfo.setPImageIndices( &mCurrentBufferIndex )
529       .setPResults( &result )
530       .setPSwapchains( &mSwapchainKHR )
531       .setSwapchainCount( 1 )
532       .setPWaitSemaphores( nullptr )
533       .setWaitSemaphoreCount( 0 );
534     mQueue.Present( presentInfo );
535
536     // just to speed things up :P
537     mQueue.WaitIdle();
538
539     return true;
540   }
541
542   // same as present but additionally waits for semaphores
543   // needed when present queue is different from graphics queue
544   bool Present( std::vector<vk::Semaphore> semaphores )
545   {
546     vk::PresentInfoKHR presentInfo{};
547     vk::Result         result{};
548     presentInfo.setPImageIndices( &mCurrentBufferIndex )
549       .setPResults( &result )
550       .setPSwapchains( &mSwapchainKHR )
551       .setSwapchainCount( 1 )
552       .setPWaitSemaphores( nullptr )
553       .setWaitSemaphoreCount( 0 );
554
555     mQueue.Present( presentInfo );
556
557     return true;
558   }
559
560   Swapchain& mOwner;
561   Graphics&  mGraphics;
562   Queue&     mQueue;
563   SurfaceRef mSurface;
564
565   uint32_t mBufferCount;
566   uint32_t mFlags;
567   uint32_t mCurrentBufferIndex;
568
569   FenceRef mFrameFence;
570
571   // swapchain framebuffers
572   std::vector<FramebufferRef> mFramebuffers;
573
574   vk::SwapchainKHR           mSwapchainKHR;
575   vk::SwapchainCreateInfoKHR mSwapchainCreateInfoKHR;
576
577   vk::Format        mSwapchainImageFormat;
578   vk::ColorSpaceKHR mSwapchainColorSpace;
579   vk::Extent2D      mSwapchainExtent;
580
581   std::vector<SwapchainBuffer> mSwapchainBuffer;
582
583   bool mFirstPresent;
584 };
585
586 /**
587  * Swapchain API
588  */
589 SwapchainRef Swapchain::New(
590   Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
591 {
592   auto retval = SwapchainRef( new Swapchain( graphics, presentationQueue, surface, bufferCount, flags ) );
593
594   if( retval->mImpl->Initialise() )
595   {
596     return retval;
597   }
598
599   return SwapchainRef();
600 }
601
602 Swapchain::Swapchain(
603   Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
604 {
605   mImpl = std::make_unique<Impl>( *this, graphics, presentationQueue, surface, bufferCount, flags );
606 }
607
608 Swapchain::Swapchain()  = default;
609 Swapchain::~Swapchain() = default;
610
611 FramebufferRef Swapchain::GetCurrentFramebuffer() const
612 {
613   return GetFramebuffer( mImpl->mCurrentBufferIndex );
614 }
615
616 FramebufferRef Swapchain::GetFramebuffer( uint32_t index ) const
617 {
618   return mImpl->mSwapchainBuffer[index].framebuffer;
619 }
620
621 FramebufferRef Swapchain::AcquireNextFramebuffer()
622 {
623   return mImpl->AcquireNextFramebuffer();
624 }
625
626 void Swapchain::Present()
627 {
628   mImpl->Present();
629 }
630
631 void Swapchain::Present( std::vector<vk::Semaphore> waitSemaphores )
632 {
633   mImpl->Present( waitSemaphores );
634 }
635
636 CommandBufferRef Swapchain::GetPrimaryCommandBuffer() const
637 {
638   return mImpl->GetPrimaryCommandBuffer();
639 }
640
641 } // namespace Vulkan
642 } // namespace Graphics
643 } // namespace Dali