35dcc067f3f16fa5aa8747dabc0e85fdbd9630a5
[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     mCurrentBufferIndex( 0u )
89   {
90     mSwapchainCreateInfoKHR.setSurface( mSurface->GetSurfaceKHR() )
91       .setPreTransform( vk::SurfaceTransformFlagBitsKHR::eIdentity )
92       .setPresentMode( vk::PresentModeKHR::eFifo )
93       .setOldSwapchain( nullptr ) //@todo support surface replacement!
94       .setMinImageCount( mBufferCount )
95       .setImageUsage( vk::ImageUsageFlagBits::eColorAttachment )
96       .setImageSharingMode( vk::SharingMode::eExclusive )
97       .setImageArrayLayers( 1 )
98       .setCompositeAlpha( vk::CompositeAlphaFlagBitsKHR::eOpaque )
99       .setClipped( true )
100       .setQueueFamilyIndexCount( 0 )
101       .setPQueueFamilyIndices( nullptr );
102   }
103
104   ~Impl() = default;
105
106   Impl( const Impl& ) = delete;
107   Impl& operator=( const Impl& ) = delete;
108
109   bool Initialise()
110   {
111     if( !SetImageFormat() )
112     {
113       return false;
114     }
115
116     // get extents
117     mSwapchainExtent = mSurface->GetSize();
118
119     mSwapchainCreateInfoKHR.setImageFormat( mSwapchainImageFormat );
120     mSwapchainCreateInfoKHR.setImageExtent( mSwapchainExtent );
121     mSwapchainCreateInfoKHR.setImageColorSpace( mSwapchainColorSpace );
122
123     Create();
124
125     InitialiseSwapchainBuffers();
126
127     PrepareFramebuffers();
128
129     mFirstPresent = true;
130     return true;
131   }
132
133   bool InitialiseSwapchainBuffers()
134   {
135     mSwapchainBuffer.clear();
136     for( auto&& fb : mFramebuffers )
137     {
138       auto cmdPool   = CommandPool::New( mGraphics, vk::CommandPoolCreateInfo{}.setFlags( vk::CommandPoolCreateFlagBits::eResetCommandBuffer ) );
139       auto masterCmd = cmdPool->NewCommandBuffer( true );
140
141       auto swapBuffer              = SwapchainBuffer{};
142       swapBuffer.framebuffer       = fb;
143       swapBuffer.index             = 0;
144       swapBuffer.masterCmdBuffer   = masterCmd;
145       swapBuffer.masterCommandPool = cmdPool;
146       swapBuffer.endOfFrameFence   = Fence::New( mGraphics );
147       swapBuffer.firstUse          = true;
148       mSwapchainBuffer.emplace_back( swapBuffer );
149     }
150
151     return true;
152   }
153
154 #pragma GCC diagnostic push
155 #pragma GCC diagnostic ignored "-Wframe-larger-than="
156   void PrepareFramebuffers()
157   {
158     /*
159      * After creating the new swapchain we need to make sure
160      * the layout of images is correct to start with. To do so
161      * we will wait till the whole device is idle ( there should
162      * be a mechanism preventing from using the GPU past that point )
163      * and submit pipeline barriers setting up the layouts of
164      * all framebuffer images in one go. There will be no fancy
165      * synchronisation here as it's not really needed at this point.
166      * Waiting for queue or device idle should be enough.
167      */
168
169     const auto& device    = mGraphics.GetDevice();
170
171     device.waitIdle();
172
173     /*
174      * Create temporary command pool
175      */
176     auto commandPool = CommandPool::New( mGraphics );
177     auto cmdBuffer   = commandPool->NewCommandBuffer();
178
179     std::vector<vk::ImageMemoryBarrier> barriers;
180     ImageViewRef                        depthStencilImage{};
181
182     for( auto&& buffer : mSwapchainBuffer )
183     {
184       auto colorImages = buffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
185
186       // expecting to use one depth stencil image for all swapbuffers
187       if( !depthStencilImage )
188       {
189         depthStencilImage = buffer.framebuffer->GetAttachment( Framebuffer::AttachmentType::DEPTH_STENCIL, 0u );
190       }
191
192       /*
193        * Add barriers for color images
194        */
195       for( auto&& colorImageView : colorImages )
196       {
197         auto image   = colorImageView->GetImage();
198         auto vkImage = image->GetVkImage();
199
200         vk::ImageSubresourceRange range;
201         range.setLayerCount( image->GetLayerCount() )
202           .setLevelCount( image->GetLevelCount() )
203           .setBaseMipLevel( 0 )
204           .setBaseArrayLayer( 0 )
205           .setAspectMask( vk::ImageAspectFlagBits::eColor );
206         auto colorBarrier = vk::ImageMemoryBarrier{}
207                               .setImage( vkImage )
208                               .setSubresourceRange( range )
209                               .setSrcAccessMask( vk::AccessFlags{} )
210                               .setDstAccessMask( vk::AccessFlagBits::eColorAttachmentWrite )
211                               .setOldLayout( vk::ImageLayout::eUndefined )
212                               .setNewLayout( vk::ImageLayout::eColorAttachmentOptimal );
213
214         barriers.emplace_back( colorBarrier );
215       }
216     }
217
218     /*
219      * Add barrier for depth stencil image
220      */
221     if( depthStencilImage )
222     {
223       auto image   = depthStencilImage->GetImage();
224       auto vkImage = image->GetVkImage();
225
226       vk::ImageSubresourceRange range;
227       range.setLayerCount( image->GetLayerCount() )
228         .setLevelCount( image->GetLevelCount() )
229         .setBaseMipLevel( 0 )
230         .setBaseArrayLayer( 0 )
231         .setAspectMask( vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil );
232       auto depthStencilBarrier = vk::ImageMemoryBarrier{}
233                                    .setImage( vkImage )
234                                    .setSubresourceRange( range )
235                                    .setSrcAccessMask( vk::AccessFlags{} )
236                                    .setDstAccessMask( vk::AccessFlagBits::eDepthStencilAttachmentWrite )
237                                    .setOldLayout( vk::ImageLayout::eUndefined )
238                                    .setNewLayout( vk::ImageLayout::eDepthStencilAttachmentOptimal );
239       barriers.emplace_back( depthStencilBarrier );
240     }
241
242     /*
243      * Record command buffer with pipeline barrier
244      */
245     cmdBuffer->Begin( vk::CommandBufferUsageFlagBits::eOneTimeSubmit );
246     cmdBuffer->PipelineBarrier( vk::PipelineStageFlagBits::eTopOfPipe,
247                                 vk::PipelineStageFlagBits::eTopOfPipe,
248                                 vk::DependencyFlags{},
249                                 std::vector<vk::MemoryBarrier>{},
250                                 std::vector<vk::BufferMemoryBarrier>{},
251                                 barriers );
252     cmdBuffer->End();
253
254     // use presentation queue to submit the call
255     mQueue.Submit( cmdBuffer, FenceRef{} );
256     mQueue.WaitIdle();
257   }
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     return swapBuffer.framebuffer;
442   }
443
444   void BeginPrimaryRenderPass()
445   {
446     auto& currentBuffer = mSwapchainBuffer[mCurrentBufferIndex];
447
448     vk::RenderPassBeginInfo rpInfo{};
449     rpInfo.setRenderPass( currentBuffer.framebuffer->GetVkRenderPass() )
450       .setFramebuffer( currentBuffer.framebuffer->GetVkFramebuffer() )
451       .setPClearValues( currentBuffer.framebuffer->GetDefaultClearValues().data() )
452       .setClearValueCount( U32( currentBuffer.framebuffer->GetDefaultClearValues().size() ) )
453       .setRenderArea( vk::Rect2D( {0, 0}, mSurface->GetSize() ) );
454
455     currentBuffer.masterCmdBuffer->BeginRenderPass( rpInfo, vk::SubpassContents::eSecondaryCommandBuffers );
456   }
457
458   void BeginPrimaryRenderPass( std::vector<std::array<float,4>> colors )
459   {
460     auto& currentBuffer = mSwapchainBuffer[mCurrentBufferIndex];
461
462     vk::RenderPassBeginInfo rpInfo{};
463
464     auto newColors = currentBuffer.framebuffer->GetDefaultClearValues();
465     newColors[0].color.setFloat32( { colors[0][0],
466                                      colors[0][1],
467                                      colors[0][2],
468                                      colors[0][3]
469                                    } );
470
471     rpInfo.setRenderArea( vk::Rect2D( {0, 0}, mSurface->GetSize() ) )
472           .setRenderPass( currentBuffer.framebuffer->GetVkRenderPass() )
473           .setFramebuffer( currentBuffer.framebuffer->GetVkFramebuffer() )
474           .setPClearValues( newColors.data() )
475           .setClearValueCount( U32( currentBuffer.framebuffer->GetDefaultClearValues().size() ) );
476
477     currentBuffer.masterCmdBuffer->BeginRenderPass( rpInfo, vk::SubpassContents::eSecondaryCommandBuffers );
478   }
479
480   void EndPrimaryRenderPass( SwapchainBuffer& currentBuffer )
481   {
482     currentBuffer.masterCmdBuffer->EndRenderPass();
483   }
484
485   /**
486    * Performs layout transition for all the color attachments
487    * in the current framebuffer.
488    * The master command buffer must be in the recording state
489    * @param swapBuffer
490    */
491   void UpdateLayoutPresentToColorAttachment( SwapchainBuffer& swapBuffer )
492   {
493     auto& cmdBuf = swapBuffer.masterCmdBuffer;
494
495     //todo: test the state of th ebuffer, must be recording
496
497     auto attachments = swapBuffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
498
499     std::vector<vk::ImageMemoryBarrier> barriers;
500     vk::ImageMemoryBarrier              barrier;
501     barrier.setSrcAccessMask( vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eColorAttachmentRead )
502       .setDstAccessMask( vk::AccessFlagBits::eColorAttachmentWrite )
503       .setOldLayout( vk::ImageLayout::ePresentSrcKHR )
504       .setNewLayout( vk::ImageLayout::eColorAttachmentOptimal )
505       .setSubresourceRange( vk::ImageSubresourceRange{}.setAspectMask( vk::ImageAspectFlagBits::eColor ).setBaseArrayLayer(0)
506                                                        .setBaseMipLevel(0)
507                                                        .setLevelCount(1)
508                                                        .setLayerCount(1));
509
510     for( auto&& imageView : attachments )
511     {
512       barriers.emplace_back( barrier.setImage( *imageView->GetImage() ) );
513     }
514
515     cmdBuf->PipelineBarrier( vk::PipelineStageFlagBits::eBottomOfPipe,
516                              vk::PipelineStageFlagBits::eColorAttachmentOutput,
517                              vk::DependencyFlags{},
518                              std::vector<vk::MemoryBarrier>{},
519                              std::vector<vk::BufferMemoryBarrier>{},
520                              barriers );
521   }
522
523   CommandBufferRef GetPrimaryCommandBuffer() const
524   {
525     return mSwapchainBuffer[mCurrentBufferIndex].masterCmdBuffer;
526   }
527
528   bool Present()
529   {
530     auto& swapBuffer = mSwapchainBuffer[mCurrentBufferIndex];
531
532     // end render pass
533     EndPrimaryRenderPass( swapBuffer );
534
535     // end command buffer
536     swapBuffer.masterCmdBuffer->End();
537
538     // submit
539     swapBuffer.endOfFrameFence->Reset();
540     mQueue.Submit( swapBuffer.masterCmdBuffer, swapBuffer.endOfFrameFence );
541     swapBuffer.endOfFrameFence->Wait( 0u );
542
543     // fixme: use semaphores to synchronize all previously submitted command buffers!
544     vk::PresentInfoKHR presentInfo{};
545     vk::Result result;
546     presentInfo.setPImageIndices( &mCurrentBufferIndex )
547       .setPResults( &result )
548       .setPSwapchains( &mSwapchainKHR )
549       .setSwapchainCount( 1 )
550       .setPWaitSemaphores( nullptr )
551       .setWaitSemaphoreCount( 0 );
552     mQueue.Present( presentInfo );
553
554     // just to speed things up :P
555     mQueue.WaitIdle();
556
557     return true;
558   }
559
560   // same as present but additionally waits for semaphores
561   // needed when present queue is different from graphics queue
562   bool Present( std::vector<vk::Semaphore> semaphores )
563   {
564     vk::PresentInfoKHR presentInfo{};
565     vk::Result         result{};
566     presentInfo.setPImageIndices( &mCurrentBufferIndex )
567       .setPResults( &result )
568       .setPSwapchains( &mSwapchainKHR )
569       .setSwapchainCount( 1 )
570       .setPWaitSemaphores( nullptr )
571       .setWaitSemaphoreCount( 0 );
572
573     mQueue.Present( presentInfo );
574
575     return true;
576   }
577
578   Swapchain& mOwner;
579   Graphics&  mGraphics;
580   Queue&     mQueue;
581   SurfaceRef mSurface;
582
583   uint32_t mBufferCount;
584   uint32_t mFlags;
585   uint32_t mCurrentBufferIndex;
586
587   FenceRef mFrameFence;
588
589   // swapchain framebuffers
590   std::vector<FramebufferRef> mFramebuffers;
591
592   vk::SwapchainKHR           mSwapchainKHR;
593   vk::SwapchainCreateInfoKHR mSwapchainCreateInfoKHR;
594
595   vk::Format        mSwapchainImageFormat;
596   vk::ColorSpaceKHR mSwapchainColorSpace;
597   vk::Extent2D      mSwapchainExtent;
598
599   std::vector<SwapchainBuffer> mSwapchainBuffer;
600
601   bool mFirstPresent;
602 };
603
604 /**
605  * Swapchain API
606  */
607 SwapchainRef Swapchain::New(
608   Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
609 {
610   auto retval = SwapchainRef( new Swapchain( graphics, presentationQueue, surface, bufferCount, flags ) );
611
612   if( retval->mImpl->Initialise() )
613   {
614     return retval;
615   }
616
617   return SwapchainRef();
618 }
619
620 Swapchain::Swapchain(
621   Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
622 {
623   mImpl = std::make_unique<Impl>( *this, graphics, presentationQueue, surface, bufferCount, flags );
624 }
625
626 Swapchain::Swapchain()  = default;
627 Swapchain::~Swapchain() = default;
628
629 FramebufferRef Swapchain::GetCurrentFramebuffer() const
630 {
631   return GetFramebuffer( mImpl->mCurrentBufferIndex );
632 }
633
634 FramebufferRef Swapchain::GetFramebuffer( uint32_t index ) const
635 {
636   return mImpl->mSwapchainBuffer[index].framebuffer;
637 }
638
639 FramebufferRef Swapchain::AcquireNextFramebuffer()
640 {
641   return mImpl->AcquireNextFramebuffer();
642 }
643
644 void Swapchain::Present()
645 {
646   mImpl->Present();
647 }
648
649 void Swapchain::Present( std::vector<vk::Semaphore> waitSemaphores )
650 {
651   mImpl->Present( waitSemaphores );
652 }
653
654 CommandBufferRef Swapchain::GetPrimaryCommandBuffer() const
655 {
656   return mImpl->GetPrimaryCommandBuffer();
657 }
658
659 void Swapchain::BeginPrimaryRenderPass()
660 {
661   mImpl->BeginPrimaryRenderPass( );
662 }
663
664 void Swapchain::BeginPrimaryRenderPass( std::vector<std::array<float,4>> colors )
665 {
666   mImpl->BeginPrimaryRenderPass( colors );
667 }
668
669
670 } // namespace Vulkan
671 } // namespace Graphics
672 } // namespace Dali