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 ),
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 )
99 .setQueueFamilyIndexCount( 0 )
100 .setPQueueFamilyIndices( nullptr );
105 Impl( const Impl& ) = delete;
106 Impl& operator=( const Impl& ) = delete;
110 if( !SetImageFormat() )
116 mSwapchainExtent = mSurface->GetSize();
118 mSwapchainCreateInfoKHR.setImageFormat( mSwapchainImageFormat );
119 mSwapchainCreateInfoKHR.setImageExtent( mSwapchainExtent );
120 mSwapchainCreateInfoKHR.setImageColorSpace( mSwapchainColorSpace );
124 InitialiseSwapchainBuffers();
126 PrepareFramebuffers();
128 mFirstPresent = true;
132 bool InitialiseSwapchainBuffers()
134 mSwapchainBuffer.clear();
135 for( auto&& fb : mFramebuffers )
137 auto cmdPool = CommandPool::New( mGraphics, vk::CommandPoolCreateInfo{}.setFlags( vk::CommandPoolCreateFlagBits::eResetCommandBuffer ) );
138 auto masterCmd = cmdPool->NewCommandBuffer( true );
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 );
153 #pragma GCC diagnostic push
154 #pragma GCC diagnostic ignored "-Wframe-larger-than="
155 void PrepareFramebuffers()
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.
168 const auto& device = mGraphics.GetDevice();
173 * Create temporary command pool
175 auto commandPool = CommandPool::New( mGraphics );
176 auto cmdBuffer = commandPool->NewCommandBuffer();
178 std::vector<vk::ImageMemoryBarrier> barriers;
179 ImageViewRef depthStencilImage{};
181 for( auto&& buffer : mSwapchainBuffer )
183 auto colorImages = buffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
185 // expecting to use one depth stencil image for all swapbuffers
186 if( !depthStencilImage )
188 depthStencilImage = buffer.framebuffer->GetAttachment( Framebuffer::AttachmentType::DEPTH_STENCIL, 0u );
192 * Add barriers for color images
194 for( auto&& colorImageView : colorImages )
196 auto image = colorImageView->GetImage();
197 auto vkImage = image->GetVkImage();
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{}
207 .setSubresourceRange( range )
208 .setSrcAccessMask( vk::AccessFlags{} )
209 .setDstAccessMask( vk::AccessFlagBits::eColorAttachmentWrite )
210 .setOldLayout( vk::ImageLayout::eUndefined )
211 .setNewLayout( vk::ImageLayout::eColorAttachmentOptimal );
213 barriers.emplace_back( colorBarrier );
218 * Add barrier for depth stencil image
220 if( depthStencilImage )
222 auto image = depthStencilImage->GetImage();
223 auto vkImage = image->GetVkImage();
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{}
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 );
242 * Record command buffer with pipeline barrier
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>{},
253 // use presentation queue to submit the call
254 mQueue.Submit( cmdBuffer, FenceRef{} );
257 #pragma GCC diagnostic pop
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 // Begins primary render pass
442 BeginPrimaryRenderPass( swapBuffer );
444 return swapBuffer.framebuffer;
446 #pragma GCC diagnostic pop
447 void BeginPrimaryRenderPass( SwapchainBuffer& currentBuffer )
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() ) );
456 currentBuffer.masterCmdBuffer->BeginRenderPass( rpInfo, vk::SubpassContents::eSecondaryCommandBuffers );
459 void EndPrimaryRenderPass( SwapchainBuffer& currentBuffer )
461 currentBuffer.masterCmdBuffer->EndRenderPass();
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
470 #pragma GCC diagnostic push
471 #pragma GCC diagnostic ignored "-Wframe-larger-than="
472 void UpdateLayoutPresentToColorAttachment( SwapchainBuffer& swapBuffer )
474 auto& cmdBuf = swapBuffer.masterCmdBuffer;
476 //todo: test the state of th ebuffer, must be recording
478 auto attachments = swapBuffer.framebuffer->GetAttachments( Framebuffer::AttachmentType::COLOR );
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)
491 for( auto&& imageView : attachments )
493 barriers.emplace_back( barrier.setImage( *imageView->GetImage() ) );
496 cmdBuf->PipelineBarrier( vk::PipelineStageFlagBits::eBottomOfPipe,
497 vk::PipelineStageFlagBits::eColorAttachmentOutput,
498 vk::DependencyFlags{},
499 std::vector<vk::MemoryBarrier>{},
500 std::vector<vk::BufferMemoryBarrier>{},
503 #pragma GCC diagnostic pop
505 CommandBufferRef GetPrimaryCommandBuffer() const
507 return mSwapchainBuffer[mCurrentBufferIndex].masterCmdBuffer;
512 auto& swapBuffer = mSwapchainBuffer[mCurrentBufferIndex];
515 EndPrimaryRenderPass( swapBuffer );
517 // end command buffer
518 swapBuffer.masterCmdBuffer->End();
521 swapBuffer.endOfFrameFence->Reset();
522 mQueue.Submit( swapBuffer.masterCmdBuffer, swapBuffer.endOfFrameFence );
523 swapBuffer.endOfFrameFence->Wait( 0u );
525 // fixme: use semaphores to synchronize all previously submitted command buffers!
526 vk::PresentInfoKHR presentInfo{};
528 presentInfo.setPImageIndices( &mCurrentBufferIndex )
529 .setPResults( &result )
530 .setPSwapchains( &mSwapchainKHR )
531 .setSwapchainCount( 1 )
532 .setPWaitSemaphores( nullptr )
533 .setWaitSemaphoreCount( 0 );
534 mQueue.Present( presentInfo );
536 // just to speed things up :P
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 )
546 vk::PresentInfoKHR presentInfo{};
548 presentInfo.setPImageIndices( &mCurrentBufferIndex )
549 .setPResults( &result )
550 .setPSwapchains( &mSwapchainKHR )
551 .setSwapchainCount( 1 )
552 .setPWaitSemaphores( nullptr )
553 .setWaitSemaphoreCount( 0 );
555 mQueue.Present( presentInfo );
565 uint32_t mBufferCount;
567 uint32_t mCurrentBufferIndex;
569 FenceRef mFrameFence;
571 // swapchain framebuffers
572 std::vector<FramebufferRef> mFramebuffers;
574 vk::SwapchainKHR mSwapchainKHR;
575 vk::SwapchainCreateInfoKHR mSwapchainCreateInfoKHR;
577 vk::Format mSwapchainImageFormat;
578 vk::ColorSpaceKHR mSwapchainColorSpace;
579 vk::Extent2D mSwapchainExtent;
581 std::vector<SwapchainBuffer> mSwapchainBuffer;
589 SwapchainRef Swapchain::New(
590 Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
592 auto retval = SwapchainRef( new Swapchain( graphics, presentationQueue, surface, bufferCount, flags ) );
594 if( retval->mImpl->Initialise() )
599 return SwapchainRef();
602 Swapchain::Swapchain(
603 Graphics& graphics, Queue& presentationQueue, SurfaceRef surface, uint8_t bufferCount, uint32_t flags )
605 mImpl = std::make_unique<Impl>( *this, graphics, presentationQueue, surface, bufferCount, flags );
608 Swapchain::Swapchain() = default;
609 Swapchain::~Swapchain() = default;
611 FramebufferRef Swapchain::GetCurrentFramebuffer() const
613 return GetFramebuffer( mImpl->mCurrentBufferIndex );
616 FramebufferRef Swapchain::GetFramebuffer( uint32_t index ) const
618 return mImpl->mSwapchainBuffer[index].framebuffer;
621 FramebufferRef Swapchain::AcquireNextFramebuffer()
623 return mImpl->AcquireNextFramebuffer();
626 void Swapchain::Present()
631 void Swapchain::Present( std::vector<vk::Semaphore> waitSemaphores )
633 mImpl->Present( waitSemaphores );
636 CommandBufferRef Swapchain::GetPrimaryCommandBuffer() const
638 return mImpl->GetPrimaryCommandBuffer();
641 } // namespace Vulkan
642 } // namespace Graphics