2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
38 * SwapchainBuffer stores all per-buffer data
40 struct SwapchainBuffer
43 * Each buffer has own master command buffer which executes
46 CommandBufferRef masterCmdBuffer;
49 * Each buffer has a command pool to allocate from
51 CommandPoolRef masterCommandPool;
54 * Framebuffer object associated with the buffer
56 FramebufferRef framebuffer;
61 FenceRef endOfFrameFence;
69 * First use before presenting
74 struct Swapchain::Impl
76 Impl( Swapchain& owner,
78 Queue& presentationQueue,
83 mGraphics( graphics ),
84 mQueue( presentationQueue ),
86 mBufferCount( bufferCount ),
88 mCurrentBufferIndex( 0u )
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 )
100 .setQueueFamilyIndexCount( 0 )
101 .setPQueueFamilyIndices( nullptr );
106 Impl( const Impl& ) = delete;
107 Impl& operator=( const Impl& ) = delete;
111 if( !SetImageFormat() )
117 mSwapchainExtent = mSurface->GetSize();
119 mSwapchainCreateInfoKHR.setImageFormat( mSwapchainImageFormat );
120 mSwapchainCreateInfoKHR.setImageExtent( mSwapchainExtent );
121 mSwapchainCreateInfoKHR.setImageColorSpace( mSwapchainColorSpace );
125 InitialiseSwapchainBuffers();
127 PrepareFramebuffers();
129 mFirstPresent = true;
133 bool InitialiseSwapchainBuffers()
135 mSwapchainBuffer.clear();
136 for( auto&& fb : mFramebuffers )
138 auto cmdPool = CommandPool::New( mGraphics, vk::CommandPoolCreateInfo{}.setFlags( vk::CommandPoolCreateFlagBits::eResetCommandBuffer ) );
139 auto masterCmd = cmdPool->NewCommandBuffer( true );
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 );
154 #pragma GCC diagnostic push
155 #pragma GCC diagnostic ignored "-Wframe-larger-than="
156 void PrepareFramebuffers()
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.
169 const auto& device = mGraphics.GetDevice();
174 * Create temporary command pool
176 auto commandPool = CommandPool::New( mGraphics );
177 auto cmdBuffer = commandPool->NewCommandBuffer();
179 std::vector<vk::ImageMemoryBarrier> barriers;
180 ImageViewRef depthStencilImage{};
182 for( auto&& buffer : mSwapchainBuffer )
184 auto colorImages = buffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
186 // expecting to use one depth stencil image for all swapbuffers
187 if( !depthStencilImage )
189 depthStencilImage = buffer.framebuffer->GetAttachment( Framebuffer::AttachmentType::DEPTH_STENCIL, 0u );
193 * Add barriers for color images
195 for( auto&& colorImageView : colorImages )
197 auto image = colorImageView->GetImage();
198 auto vkImage = image->GetVkImage();
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{}
208 .setSubresourceRange( range )
209 .setSrcAccessMask( vk::AccessFlags{} )
210 .setDstAccessMask( vk::AccessFlagBits::eColorAttachmentWrite )
211 .setOldLayout( vk::ImageLayout::eUndefined )
212 .setNewLayout( vk::ImageLayout::eColorAttachmentOptimal );
214 barriers.emplace_back( colorBarrier );
219 * Add barrier for depth stencil image
221 if( depthStencilImage )
223 auto image = depthStencilImage->GetImage();
224 auto vkImage = image->GetVkImage();
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{}
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 );
243 * Record command buffer with pipeline barrier
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>{},
254 // use presentation queue to submit the call
255 mQueue.Submit( cmdBuffer, FenceRef{} );
259 bool SetImageFormat()
261 // obtain supported image format
262 auto formats = VkAssert( mGraphics.GetPhysicalDevice().getSurfaceFormatsKHR( mSurface->GetSurfaceKHR() ) );
263 mSwapchainImageFormat = vk::Format::eUndefined;
265 for( auto&& format : formats )
267 if( format.format != vk::Format::eUndefined )
269 mSwapchainColorSpace = format.colorSpace;
270 mSwapchainImageFormat = format.format;
275 if( vk::Format::eUndefined == mSwapchainImageFormat )
284 * Creates swapchain immediately
289 const auto& device = mGraphics.GetDevice();
290 const auto& allocator = mGraphics.GetAllocator();
293 mSwapchainKHR = VkAssert( device.createSwapchainKHR( mSwapchainCreateInfoKHR, allocator ) );
300 // pull images and create Framebuffers
301 auto images = VkAssert( device.getSwapchainImagesKHR( mSwapchainKHR ) );
303 // number of images must match requested buffering mode
304 if( images.size() != mBufferCount )
306 device.destroySwapchainKHR( mSwapchainKHR );
307 mSwapchainKHR = nullptr;
311 //@todo create depth-stencil image
312 auto depthStencilImageView = CreateDepthStencil();
315 * CREATE FRAMEBUFFERS
317 for( auto&& image : images )
319 mFramebuffers.emplace_back( CreateFramebuffer( image ) );
321 // set depth/stencil if supported
322 if( depthStencilImageView )
324 mFramebuffers.back()->SetAttachment( depthStencilImageView, Framebuffer::AttachmentType::DEPTH_STENCIL, 0u );
327 // create framebuffer and compatible render pass right now, no need to defer it
328 mFramebuffers.back()->Commit();
335 * Creates depth stencil if necessary
338 ImageViewRef CreateDepthStencil()
340 // create depth stencil image
341 auto dsImageRef = Image::New( mGraphics,
342 vk::ImageCreateInfo{}
343 .setFormat( vk::Format::eD24UnormS8Uint )
345 .setTiling( vk::ImageTiling::eOptimal )
346 .setImageType( vk::ImageType::e2D )
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 ) );
354 auto memory = mGraphics.GetDeviceMemoryManager().GetDefaultAllocator().Allocate(
355 dsImageRef, vk::MemoryPropertyFlagBits::eDeviceLocal );
357 dsImageRef->BindMemory( memory );
359 // create imageview to be used within framebuffer
360 auto dsImageViewRef = ImageView::New( mGraphics, dsImageRef );
361 return dsImageViewRef;
365 * Creates a Framebuffer and compatible RenderPass
369 #pragma GCC diagnostic push
370 #pragma GCC diagnostic ignored "-Wframe-larger-than="
371 FramebufferRef CreateFramebuffer( vk::Image& image )
373 auto fbRef = Framebuffer::New( mGraphics, mSwapchainExtent.width, mSwapchainExtent.height );
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} )
387 .setImageType( vk::ImageType::e2D )
388 .setTiling( vk::ImageTiling::eOptimal )
392 // Create basic imageview ( all mipmaps, all layers )
393 ImageViewRef iv = ImageView::New( mGraphics, imageRef );
395 fbRef->SetAttachment( iv, Framebuffer::AttachmentType::COLOR, 0u );
399 #pragma GCC diagnostic pop
402 * This function acquires next framebuffer
403 * @todo we should rather use roundrobin method
406 #pragma GCC diagnostic push
407 #pragma GCC diagnostic ignored "-Wframe-larger-than="
408 FramebufferRef AcquireNextFramebuffer()
410 const auto& device = mGraphics.GetDevice();
414 mFrameFence = Fence::New( mGraphics );
417 mCurrentBufferIndex =
418 VkAssert( device.acquireNextImageKHR( mSwapchainKHR, 1000000, nullptr, mFrameFence->GetFence() ) );
421 mFrameFence->Reset();
423 auto& swapBuffer = mSwapchainBuffer[mCurrentBufferIndex];
426 auto inheritanceInfo = vk::CommandBufferInheritanceInfo{}
427 .setFramebuffer( swapBuffer.framebuffer->GetVkFramebuffer() )
428 .setRenderPass(swapBuffer.framebuffer->GetVkRenderPass())
430 swapBuffer.masterCmdBuffer->Reset();
431 swapBuffer.masterCmdBuffer->Begin( vk::CommandBufferUsageFlagBits::eRenderPassContinue, &inheritanceInfo );
433 // change layout from present to color attachment if not done yet
434 // ( swapchain must track that )
435 if(!swapBuffer.firstUse)
437 UpdateLayoutPresentToColorAttachment(swapBuffer);
439 swapBuffer.firstUse = false;
441 return swapBuffer.framebuffer;
444 void BeginPrimaryRenderPass()
446 auto& currentBuffer = mSwapchainBuffer[mCurrentBufferIndex];
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() ) );
455 currentBuffer.masterCmdBuffer->BeginRenderPass( rpInfo, vk::SubpassContents::eSecondaryCommandBuffers );
458 void BeginPrimaryRenderPass( std::vector<std::array<float,4>> colors )
460 auto& currentBuffer = mSwapchainBuffer[mCurrentBufferIndex];
462 vk::RenderPassBeginInfo rpInfo{};
464 auto newColors = currentBuffer.framebuffer->GetDefaultClearValues();
465 newColors[0].color.setFloat32( { colors[0][0],
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() ) );
477 currentBuffer.masterCmdBuffer->BeginRenderPass( rpInfo, vk::SubpassContents::eSecondaryCommandBuffers );
480 void EndPrimaryRenderPass( SwapchainBuffer& currentBuffer )
482 currentBuffer.masterCmdBuffer->EndRenderPass();
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
491 void UpdateLayoutPresentToColorAttachment( SwapchainBuffer& swapBuffer )
493 auto& cmdBuf = swapBuffer.masterCmdBuffer;
495 //todo: test the state of th ebuffer, must be recording
497 auto attachments = swapBuffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
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)
510 for( auto&& imageView : attachments )
512 barriers.emplace_back( barrier.setImage( *imageView->GetImage() ) );
515 cmdBuf->PipelineBarrier( vk::PipelineStageFlagBits::eBottomOfPipe,
516 vk::PipelineStageFlagBits::eColorAttachmentOutput,
517 vk::DependencyFlags{},
518 std::vector<vk::MemoryBarrier>{},
519 std::vector<vk::BufferMemoryBarrier>{},
523 CommandBufferRef GetPrimaryCommandBuffer() const
525 return mSwapchainBuffer[mCurrentBufferIndex].masterCmdBuffer;
530 auto& swapBuffer = mSwapchainBuffer[mCurrentBufferIndex];
533 EndPrimaryRenderPass( swapBuffer );
535 // end command buffer
536 swapBuffer.masterCmdBuffer->End();
539 swapBuffer.endOfFrameFence->Reset();
540 mQueue.Submit( swapBuffer.masterCmdBuffer, swapBuffer.endOfFrameFence );
541 swapBuffer.endOfFrameFence->Wait( 0u );
543 // fixme: use semaphores to synchronize all previously submitted command buffers!
544 vk::PresentInfoKHR presentInfo{};
546 presentInfo.setPImageIndices( &mCurrentBufferIndex )
547 .setPResults( &result )
548 .setPSwapchains( &mSwapchainKHR )
549 .setSwapchainCount( 1 )
550 .setPWaitSemaphores( nullptr )
551 .setWaitSemaphoreCount( 0 );
552 mQueue.Present( presentInfo );
554 // just to speed things up :P
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 )
564 vk::PresentInfoKHR presentInfo{};
566 presentInfo.setPImageIndices( &mCurrentBufferIndex )
567 .setPResults( &result )
568 .setPSwapchains( &mSwapchainKHR )
569 .setSwapchainCount( 1 )
570 .setPWaitSemaphores( nullptr )
571 .setWaitSemaphoreCount( 0 );
573 mQueue.Present( presentInfo );
583 uint32_t mBufferCount;
585 uint32_t mCurrentBufferIndex;
587 FenceRef mFrameFence;
589 // swapchain framebuffers
590 std::vector<FramebufferRef> mFramebuffers;
592 vk::SwapchainKHR mSwapchainKHR;
593 vk::SwapchainCreateInfoKHR mSwapchainCreateInfoKHR;
595 vk::Format mSwapchainImageFormat;
596 vk::ColorSpaceKHR mSwapchainColorSpace;
597 vk::Extent2D mSwapchainExtent;
599 std::vector<SwapchainBuffer> mSwapchainBuffer;
607 SwapchainRef Swapchain::New(
608 Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
610 auto retval = SwapchainRef( new Swapchain( graphics, presentationQueue, surface, bufferCount, flags ) );
612 if( retval->mImpl->Initialise() )
617 return SwapchainRef();
620 Swapchain::Swapchain(
621 Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
623 mImpl = std::make_unique<Impl>( *this, graphics, presentationQueue, surface, bufferCount, flags );
626 Swapchain::Swapchain() = default;
627 Swapchain::~Swapchain() = default;
629 FramebufferRef Swapchain::GetCurrentFramebuffer() const
631 return GetFramebuffer( mImpl->mCurrentBufferIndex );
634 FramebufferRef Swapchain::GetFramebuffer( uint32_t index ) const
636 return mImpl->mSwapchainBuffer[index].framebuffer;
639 FramebufferRef Swapchain::AcquireNextFramebuffer()
641 return mImpl->AcquireNextFramebuffer();
644 void Swapchain::Present()
649 void Swapchain::Present( std::vector<vk::Semaphore> waitSemaphores )
651 mImpl->Present( waitSemaphores );
654 CommandBufferRef Swapchain::GetPrimaryCommandBuffer() const
656 return mImpl->GetPrimaryCommandBuffer();
659 void Swapchain::BeginPrimaryRenderPass()
661 mImpl->BeginPrimaryRenderPass( );
664 void Swapchain::BeginPrimaryRenderPass( std::vector<std::array<float,4>> colors )
666 mImpl->BeginPrimaryRenderPass( colors );
670 } // namespace Vulkan
671 } // namespace Graphics