2 * Copyright (c) 2017 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/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>
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);
44 SwapchainImage::SwapchainImage() = default;
45 SwapchainImage::~SwapchainImage() = default;
47 Surface::Surface(Graphics& graphics, vk::SurfaceKHR surface, uint32_t bufferCount, bool hasDepthStencil)
48 : mGraphics(graphics), mSurface(surface), mSwapchain{nullptr}, mBufferCount{bufferCount}, mHasDepthStencil(hasDepthStencil)
60 mGraphics.GetInstance().destroySurfaceKHR(mSurface, mGraphics.GetAllocator());
64 void Surface::AcquireNextImage()
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
76 mFrameFence = Fence::New(mGraphics);
79 // acquire image, for simplicity using fence for acquiring as it is unknown what next command buffer will
82 uint32_t index = VkAssert(mGraphics.GetDevice().acquireNextImageKHR(mSwapchain, 1000000, nullptr,
83 mFrameFence->GetFence()));
87 mCurrentBufferIndex = index;
89 auto& swapImage = mSwapImages[index];
91 // change layout if necessary to color attachment
92 if(swapImage.layout != vk::ImageLayout::eColorAttachmentOptimal)
94 auto& queue = mGraphics.GetGraphicsQueue();
95 queue.Submit(swapImage.layoutToColorCmdBuf, mFrameFence)->WaitForFence();
100 // todo: anything to be done before beginning main command buffer?
104 void Surface::BeginRenderPass()
106 auto& swapImage = mSwapImages[mCurrentBufferIndex];
108 // begin command buffer ( can be directly obtained through Graphics( surface )
109 swapImage.mainCmdBuf->Begin(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
112 * todo: automatically start main render pass -> this may have to be done manually in future
113 * if more flexibility is needed
115 auto vkCmdBuf = swapImage.mainCmdBuf->GetVkCommandBuffer();
117 std::array< vk::ClearValue, 2 > clearValues;
119 static float r = 1.0f;
124 clearValues[0].color.setFloat32({0.0f, 1.0f, 0.0f, 1.0f});
125 clearValues[1].depthStencil.setDepth(1.0f).setStencil(0.0f);
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());
134 auto subpassContents = vk::SubpassContents{vk::SubpassContents::eSecondaryCommandBuffers};
135 vkCmdBuf.beginRenderPass(rpInfo, subpassContents);
139 std::vector<vk::ClearValue> Surface::GetClearValues() const
141 static float r = 0.0f;
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);
150 auto retval = std::vector<vk::ClearValue>{};
151 retval.emplace_back( clearValues[0] );
152 if( mHasDepthStencil )
154 retval.emplace_back( clearValues[1] );
159 vk::Extent2D Surface::GetSize() const
161 return mCapabilities->currentExtent;
164 Handle<CommandBuffer> Surface::GetCommandBuffer( uint32_t index )
166 return mSwapImages[index].mainCmdBuf;
169 Handle<CommandBuffer> Surface::GetCurrentCommandBuffer()
171 return mSwapImages[mCurrentBufferIndex].mainCmdBuf;
174 void Surface::EndRenderPass()
176 // todo: use semaphores and do not create fences all over again
177 auto& swapImage = mSwapImages[mCurrentBufferIndex];
178 auto vkCmdBuf = swapImage.mainCmdBuf->GetVkCommandBuffer();
180 // complete render pass
181 vkCmdBuf.endRenderPass();
183 // finalize command buffer
184 swapImage.mainCmdBuf->End();
187 auto& queue = mGraphics.GetGraphicsQueue();
188 queue.Submit(swapImage.mainCmdBuf, mFrameFence)->WaitForFence();
191 void Surface::Present()
193 // complete render pass and command buffer
196 auto& swapImage = mSwapImages[mCurrentBufferIndex];
197 auto result = mGraphics.GetGraphicsQueue().Present(mSwapchain, mCurrentBufferIndex);
198 if(result != vk::Result::eSuccess)
200 //todo: handle swapchain invalidation
202 swapImage.layout = vk::ImageLayout::ePresentSrcKHR;
203 // todo: test result against swapchain expiration
206 void Surface::CreateSwapchain()
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)
214 if(format.format != vk::Format::eUndefined && mFormat == vk::Format::eUndefined)
216 mFormat = format.format;
217 mColorSpace = format.colorSpace;
222 assert(mFormat != vk::Format::eUndefined && "No supported surface format!");
224 mCapabilities.reset(new vk::SurfaceCapabilitiesKHR(
225 VkAssert(mGraphics.GetPhysicalDevice().getSurfaceCapabilitiesKHR(mSurface))));
227 mExtent = mCapabilities->currentExtent;
229 CreateVulkanSwapchain();
231 // initialise default render pass
232 InitialiseRenderPass();
234 // if successful continue with obtaining images etc. also each swapchain will obtain default renderpass
235 InitialiseSwapchain();
237 // prerecord command buffers per each image in order to provide layout transition
238 CreateCommandBuffers();
241 void Surface::CreateVulkanSwapchain()
243 auto info = vk::SwapchainCreateInfoKHR{};
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);
261 mSwapchain = VkAssert(mGraphics.GetDevice().createSwapchainKHR(
262 reinterpret_cast< vk::SwapchainCreateInfoKHR& >(info), mGraphics.GetAllocator()));
265 void Surface::DestroySwapchain()
267 mGraphics.GetDevice().destroySwapchainKHR(mSwapchain, mGraphics.GetAllocator());
270 void Surface::InitialiseSwapchain()
272 const auto& device = mGraphics.GetDevice();
274 auto images = VkAssert(device.getSwapchainImagesKHR(mSwapchain));
275 assert(mBufferCount == images.size() && "Swapchain images count not equal requested value!");
278 auto swapImages = std::vector< SwapchainImage >{};
280 // for each image create framebuffer and image view
281 for(auto& image : images)
283 AddSwapchainImage(image, swapImages);
286 mSwapImages = std::move(swapImages);
291 CreateDepthStencil();
295 void Surface::AddSwapchainImage(vk::Image image, std::vector< SwapchainImage >& swapchainImages)
297 auto swapImage = std::move(SwapchainImage{});
298 swapImage.image = MakeRef<Image>( mGraphics, image );
301 CreateImageView(swapImage);
303 // Create framebuffer ( there must be already render pass and information whether
304 // we use depth or not )
305 CreateFramebuffer(swapImage);
307 // initialise semaphores
308 CreateSemaphores(swapImage);
310 swapImage.layout = vk::ImageLayout::eUndefined;
312 swapchainImages.push_back(std::move(swapImage));
315 void Surface::CreateImageView(SwapchainImage& swapImage)
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->GetImage())
322 .setSubresourceRange(vk::ImageSubresourceRange()
323 .setAspectMask(vk::ImageAspectFlagBits::eColor)
324 .setBaseArrayLayer(0)
328 .setViewType(vk::ImageViewType::e2D);
330 swapImage.imageView = swapImage.image->CreateView( ivInfo );
333 void Surface::CreateFramebuffer(SwapchainImage& swapImage)
335 vk::FramebufferCreateInfo fbInfo;
336 fbInfo.setAttachmentCount(mHasDepthStencil ? 2 : 1)
337 .setPAttachments(&swapImage.imageView->GetImageView()) // todo: add depth/stencil attachment
338 .setHeight(mExtent.height)
339 .setWidth(mExtent.width)
341 .setRenderPass(mDefaultRenderPass);
343 swapImage.framebuffer =
344 VkAssert(mGraphics.GetDevice().createFramebuffer(fbInfo, mGraphics.GetAllocator()));
347 void Surface::CreateSemaphores(SwapchainImage& swapImage)
350 VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
351 swapImage.presentSem =
352 VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
355 #pragma GCC diagnostic push
356 #pragma GCC diagnostic ignored "-Wframe-larger-than="
357 void Surface::InitialiseRenderPass()
359 auto att = std::vector<vk::AttachmentDescription>{ 2 };
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);
372 // optional depth/stencil attachment
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);
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);
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);
394 vk::RenderPassCreateInfo info;
395 info.setPAttachments(att.data()).setAttachmentCount(mHasDepthStencil ? 2 : 1).setPSubpasses(&subpass).setSubpassCount(1);
397 mDefaultRenderPass = VkAssert(mGraphics.GetDevice().createRenderPass(info, mGraphics.GetAllocator()));
399 #pragma GCC diagnostic pop
401 void Surface::CreateDepthStencil()
403 assert("Surface::CreateDepthStencil() not implemented!");
407 void Surface::DestroyDepthStencil()
410 assert("Surface::DestroyDepthStencil() not implemented!");
413 void Surface::CreateCommandBuffers()
417 mCommandPool = CommandPool::New( mGraphics, vk::CommandPoolCreateInfo{}.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer) );
420 // allocate command buffers
421 auto cmdBuffers = std::vector< CommandBufferRef >{};
423 vk::CommandBufferAllocateInfo{}.setCommandBufferCount(1).setLevel(vk::CommandBufferLevel::ePrimary);
425 for(auto& swapImage : mSwapImages)
427 swapImage.layoutToColorCmdBuf = mCommandPool->NewCommandBuffer(cmdInfo);
428 swapImage.mainCmdBuf = mCommandPool->NewCommandBuffer(cmdInfo);
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->GetImage(),
435 vk::ImageLayout::eColorAttachmentOptimal,
436 vk::ImageAspectFlagBits::eColor);
437 swapImage.layoutToColorCmdBuf->End();
438 swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
440 cmdBuffers.push_back(swapImage.layoutToColorCmdBuf);
443 // submit to the queue
445 auto& queue = mGraphics.GetGraphicsQueue();
446 auto fence = Fence::New(mGraphics);
447 auto submission = queue.Submit(cmdBuffers, fence);
448 submission->WaitForFence();
451 // record present to color transitions for each buffer for further reusing
452 for(auto& swapImage : mSwapImages)
454 swapImage.layoutToColorCmdBuf->Reset();
455 swapImage.layoutToColorCmdBuf->Begin();
456 swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetImage(),
457 vk::ImageLayout::ePresentSrcKHR,
458 vk::ImageLayout::eColorAttachmentOptimal,
459 vk::ImageAspectFlagBits::eColor);
460 swapImage.layoutToColorCmdBuf->End();
461 swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
465 vk::SurfaceKHR Surface::GetSurfaceKHR() const
470 vk::RenderPass Surface::GetRenderPass() const
472 return mDefaultRenderPass;
475 vk::Framebuffer Surface::GetFramebuffer(uint32_t index) const
477 return mSwapImages[index].framebuffer;
480 ImageView& Surface::GetImageView(uint32_t index) const
482 return *mSwapImages[index].imageView;
485 Image& Surface::GetImage(uint32_t index) const
487 return *mSwapImages[index].image;
490 } // namespace Vulkan
491 } // namespace Graphics