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 = MakeUnique< Fence >(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.get(), *mFrameFence.get())->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->Get();
117 std::array< vk::ClearValue, 2 > clearValues;
119 static float r = 0.0f;
124 clearValues[0].color.setFloat32({r, 0.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::eInline};
135 vkCmdBuf.beginRenderPass(rpInfo, subpassContents);
139 void Surface::EndRenderPass()
141 // todo: use semaphores and do not create fences all over again
142 auto& swapImage = mSwapImages[mCurrentBufferIndex];
143 auto vkCmdBuf = swapImage.mainCmdBuf->Get();
145 // complete render pass
146 vkCmdBuf.endRenderPass();
148 // finalize command buffer
149 swapImage.mainCmdBuf->End();
152 auto& queue = mGraphics.GetGraphicsQueue();
153 queue.Submit(*swapImage.mainCmdBuf.get(), *mFrameFence.get())->WaitForFence();
156 void Surface::Present()
158 // complete render pass and command buffer
161 auto& swapImage = mSwapImages[mCurrentBufferIndex];
162 auto result = mGraphics.GetGraphicsQueue().Present(mSwapchain, mCurrentBufferIndex);
163 if(result != vk::Result::eSuccess)
165 //todo: handle swapchain invalidation
167 swapImage.layout = vk::ImageLayout::ePresentSrcKHR;
168 // todo: test result against swapchain expiration
171 void Surface::CreateSwapchain()
174 auto formats = VkAssert(mGraphics.GetPhysicalDevice().getSurfaceFormatsKHR(mSurface));
175 // find first which is not UNDEFINED
176 mFormat = vk::Format::eUndefined;
177 for(auto& format : formats)
179 if(format.format != vk::Format::eUndefined && mFormat == vk::Format::eUndefined)
181 mFormat = format.format;
182 mColorSpace = format.colorSpace;
187 assert(mFormat != vk::Format::eUndefined && "No supported surface format!");
189 mCapabilities.reset(new vk::SurfaceCapabilitiesKHR(
190 VkAssert(mGraphics.GetPhysicalDevice().getSurfaceCapabilitiesKHR(mSurface))));
192 mExtent = mCapabilities->currentExtent;
194 CreateVulkanSwapchain();
196 // initialise default render pass
197 InitialiseRenderPass();
199 // if successful continue with obtaining images etc. also each swapchain will obtain default renderpass
200 InitialiseSwapchain();
202 // prerecord command buffers per each image in order to provide layout transition
203 CreateCommandBuffers();
206 void Surface::CreateVulkanSwapchain()
208 auto info = vk::SwapchainCreateInfoKHR{};
210 info.setClipped(true)
211 .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
212 .setImageArrayLayers(1)
213 .setImageColorSpace(mColorSpace)
214 .setImageExtent(mCapabilities->currentExtent)
215 .setImageFormat(mFormat)
216 .setImageSharingMode(vk::SharingMode::eExclusive)
217 .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
218 .setMinImageCount(mBufferCount)
219 .setPresentMode(vk::PresentModeKHR::eFifo)
220 .setPreTransform(mCapabilities->currentTransform)
221 .setOldSwapchain(nullptr)
222 .setPQueueFamilyIndices(nullptr)
223 .setQueueFamilyIndexCount(0)
224 .setSurface(mSurface);
226 mSwapchain = VkAssert(mGraphics.GetDevice().createSwapchainKHR(
227 reinterpret_cast< vk::SwapchainCreateInfoKHR& >(info), mGraphics.GetAllocator()));
230 void Surface::DestroySwapchain()
232 mGraphics.GetDevice().destroySwapchainKHR(mSwapchain, mGraphics.GetAllocator());
235 void Surface::InitialiseSwapchain()
237 const auto& device = mGraphics.GetDevice();
239 auto images = VkAssert(device.getSwapchainImagesKHR(mSwapchain));
240 assert(mBufferCount == images.size() && "Swapchain images count not equal requested value!");
243 auto swapImages = std::vector< SwapchainImage >{};
245 // for each image create framebuffer and image view
246 for(auto& image : images)
248 AddSwapchainImage(image, swapImages);
251 mSwapImages = std::move(swapImages);
256 CreateDepthStencil();
260 void Surface::AddSwapchainImage(vk::Image image, std::vector< SwapchainImage >& swapchainImages)
262 auto swapImage = std::move(SwapchainImage{});
263 swapImage.image = MakeUnique<Image>( mGraphics, image );
266 CreateImageView(swapImage);
268 // Create framebuffer ( there must be already render pass and information whether
269 // we use depth or not )
270 CreateFramebuffer(swapImage);
272 // initialise semaphores
273 CreateSemaphores(swapImage);
275 swapImage.layout = vk::ImageLayout::eUndefined;
277 swapchainImages.push_back(std::move(swapImage));
280 void Surface::CreateImageView(SwapchainImage& swapImage)
282 // Simple image view 2D as a color attachment
283 auto ivInfo = vk::ImageViewCreateInfo{};
284 ivInfo.setFormat(mFormat)
285 .setComponents(VK_COMPONENT_MAPPING_RGBA)
286 .setImage(swapImage.image->GetImage())
287 .setSubresourceRange(vk::ImageSubresourceRange()
288 .setAspectMask(vk::ImageAspectFlagBits::eColor)
289 .setBaseArrayLayer(0)
293 .setViewType(vk::ImageViewType::e2D);
295 swapImage.imageView = swapImage.image->CreateView( ivInfo );
298 void Surface::CreateFramebuffer(SwapchainImage& swapImage)
300 vk::FramebufferCreateInfo fbInfo;
301 fbInfo.setAttachmentCount(mHasDepthStencil ? 2 : 1)
302 .setPAttachments(&swapImage.imageView->GetImageView()) // todo: add depth/stencil attachment
303 .setHeight(mExtent.height)
304 .setWidth(mExtent.width)
306 .setRenderPass(mDefaultRenderPass);
308 swapImage.framebuffer =
309 VkAssert(mGraphics.GetDevice().createFramebuffer(fbInfo, mGraphics.GetAllocator()));
312 void Surface::CreateSemaphores(SwapchainImage& swapImage)
315 VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
316 swapImage.presentSem =
317 VkAssert(mGraphics.GetDevice().createSemaphore(vk::SemaphoreCreateInfo(), mGraphics.GetAllocator()));
320 #pragma GCC diagnostic push
321 #pragma GCC diagnostic ignored "-Wframe-larger-than="
322 void Surface::InitialiseRenderPass()
324 auto att = std::vector<vk::AttachmentDescription>{ 2 };
329 .setLoadOp(vk::AttachmentLoadOp::eClear)
330 .setStoreOp(vk::AttachmentStoreOp::eStore)
331 .setSamples(vk::SampleCountFlagBits::e1)
332 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
333 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
334 .setInitialLayout(vk::ImageLayout::eColorAttachmentOptimal)
335 .setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
337 // optional depth/stencil attachment
339 .setFormat(mDepthStencilFormat)
340 .setLoadOp(vk::AttachmentLoadOp::eClear)
341 .setStoreOp(vk::AttachmentStoreOp::eDontCare)
342 .setSamples(vk::SampleCountFlagBits::e1)
343 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
344 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
345 .setInitialLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)
346 .setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
348 auto attRef = std::array<vk::AttachmentReference, 2>{};
349 attRef[0].setLayout(vk::ImageLayout::eColorAttachmentOptimal).setAttachment(0);
350 attRef[1].setLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal).setAttachment(1);
352 // prepare default subpass
353 vk::SubpassDescription subpass;
354 subpass.setColorAttachmentCount(1)
355 .setPColorAttachments(&attRef[0])
356 .setPDepthStencilAttachment(mHasDepthStencil ? &attRef[1] : nullptr)
357 .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
359 vk::RenderPassCreateInfo info;
360 info.setPAttachments(att.data()).setAttachmentCount(mHasDepthStencil ? 2 : 1).setPSubpasses(&subpass).setSubpassCount(1);
362 mDefaultRenderPass = VkAssert(mGraphics.GetDevice().createRenderPass(info, mGraphics.GetAllocator()));
364 #pragma GCC diagnostic pop
366 void Surface::CreateDepthStencil()
368 assert("Surface::CreateDepthStencil() not implemented!");
372 void Surface::DestroyDepthStencil()
375 assert("Surface::DestroyDepthStencil() not implemented!");
378 void Surface::CreateCommandBuffers()
382 auto info = vk::CommandPoolCreateInfo{}.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer);
383 info.setQueueFamilyIndex(0); // todo: get correct queue family index ( 0 works by default ;) )
384 mCommandPool = MakeUnique< CommandPool >(mGraphics, info);
387 // allocate command buffers
388 auto cmdBuffers = std::vector< CommandBufferRef >{};
390 vk::CommandBufferAllocateInfo{}.setCommandBufferCount(1).setLevel(vk::CommandBufferLevel::ePrimary);
392 for(auto& swapImage : mSwapImages)
394 swapImage.layoutToColorCmdBuf = mCommandPool->AllocateCommandBuffer(cmdInfo);
395 swapImage.mainCmdBuf = mCommandPool->AllocateCommandBuffer(cmdInfo);
397 // Record layout transition for each image, after transition command buffers will be re-recorded
398 // and will take in account only present -> color layout transition
399 swapImage.layoutToColorCmdBuf->Begin();
400 swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetImage(),
402 vk::ImageLayout::eColorAttachmentOptimal,
403 vk::ImageAspectFlagBits::eColor);
404 swapImage.layoutToColorCmdBuf->End();
405 swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
407 cmdBuffers.push_back(std::ref(*swapImage.layoutToColorCmdBuf.get()));
410 // submit to the queue
412 auto& queue = mGraphics.GetGraphicsQueue();
413 auto fence = Fence(mGraphics);
414 auto submission = queue.Submit(cmdBuffers, fence);
415 submission->WaitForFence();
418 // record present to color transitions for each buffer for further reusing
419 for(auto& swapImage : mSwapImages)
421 swapImage.layoutToColorCmdBuf->Reset();
422 swapImage.layoutToColorCmdBuf->Begin();
423 swapImage.layoutToColorCmdBuf->ImageLayoutTransition(swapImage.image->GetImage(),
424 vk::ImageLayout::ePresentSrcKHR,
425 vk::ImageLayout::eColorAttachmentOptimal,
426 vk::ImageAspectFlagBits::eColor);
427 swapImage.layoutToColorCmdBuf->End();
428 swapImage.layout = vk::ImageLayout::eColorAttachmentOptimal;
432 vk::SurfaceKHR Surface::GetSurfaceKHR() const
437 vk::RenderPass Surface::GetRenderPass() const
439 return mDefaultRenderPass;
442 vk::Framebuffer Surface::GetFramebuffer(uint32_t index) const
444 return mSwapImages[index].framebuffer;
447 ImageView& Surface::GetImageView(uint32_t index) const
449 return *mSwapImages[index].imageView;
452 Image& Surface::GetImage(uint32_t index) const
454 return *mSwapImages[index].image;
457 } // namespace Vulkan
458 } // namespace Graphics