Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "gpu/vulkan/vulkan_swap_chain.h" | |
| 6 | |
| 7 #include "gpu/vulkan/vulkan_command_buffer.h" | |
| 8 #include "gpu/vulkan/vulkan_command_pool.h" | |
| 9 #include "gpu/vulkan/vulkan_implementation.h" | |
| 10 | |
| 11 namespace gpu { | |
| 12 | |
| 13 VulkanSwapChain::VulkanSwapChain() {} | |
| 14 | |
| 15 VulkanSwapChain::~VulkanSwapChain() { | |
| 16 DCHECK(images_.empty()); | |
| 17 DCHECK_EQ(static_cast<VkSwapchainKHR>(VK_NULL_HANDLE), swap_chain_); | |
| 18 } | |
| 19 | |
| 20 bool VulkanSwapChain::Initialize(VkSurfaceKHR surface, | |
| 21 const VkSurfaceCapabilitiesKHR& surface_caps, | |
| 22 const VkSurfaceFormatKHR& surface_format) { | |
| 23 return InitializeSwapChain(surface, surface_caps, surface_format) && | |
| 24 InitializeImageLayoutCommandBuffer() && | |
| 25 InitializeSwapImages(surface_caps, surface_format) && | |
| 26 InitializeInitialBuffer(); | |
| 27 } | |
| 28 | |
| 29 void VulkanSwapChain::Destroy() { | |
| 30 DestroySwapImages(); | |
| 31 DestroyImageLayoutCommandBuffer(); | |
| 32 DestroySwapChain(); | |
| 33 } | |
| 34 | |
| 35 gfx::SwapResult VulkanSwapChain::SwapBuffers() { | |
| 36 VkResult result = VK_SUCCESS; | |
| 37 | |
| 38 scoped_ptr<ImageData>& current_image_data = images_[current_image_]; | |
| 39 | |
| 40 // Default image subresource range. | |
| 41 VkImageSubresourceRange image_subresource_range = {}; | |
| 42 image_subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| 43 image_subresource_range.baseMipLevel = 0; | |
| 44 image_subresource_range.levelCount = 1; | |
| 45 image_subresource_range.baseArrayLayer = 0; | |
| 46 image_subresource_range.layerCount = 1; | |
| 47 | |
| 48 // Submit our command buffer for the current buffer. | |
| 49 if (!current_image_data->command_buffer->Submit( | |
| 50 GetVulkanQueue(), 1, ¤t_image_data->render_layout_semaphore, 1, | |
| 51 ¤t_image_data->render_semaphore)) { | |
| 52 return gfx::SwapResult::SWAP_FAILED; | |
| 53 } | |
| 54 | |
| 55 // Prepare the image layout to present. | |
| 56 { | |
| 57 ScopedSingleUseCommandBufferRecorder recorder( | |
| 58 *image_layout_command_buffer_); | |
| 59 | |
| 60 VkImageMemoryBarrier image_memory_barrier = {}; | |
| 61 image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| 62 image_memory_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| 63 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| 64 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
| 65 image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 66 image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 67 image_memory_barrier.subresourceRange = image_subresource_range; | |
| 68 image_memory_barrier.image = current_image_data->image; | |
| 69 | |
| 70 vkCmdPipelineBarrier(recorder.handle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, | |
| 71 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, | |
| 72 nullptr, 1, &image_memory_barrier); | |
| 73 } | |
| 74 if (!image_layout_command_buffer_->Submit( | |
| 75 GetVulkanQueue(), 1, ¤t_image_data->render_semaphore, 1, | |
| 76 ¤t_image_data->present_layout_semaphore)) { | |
| 77 return gfx::SwapResult::SWAP_FAILED; | |
| 78 } | |
| 79 | |
| 80 // Queue the present. | |
| 81 VkPresentInfoKHR present_info = {}; | |
| 82 present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
| 83 present_info.waitSemaphoreCount = 1; | |
| 84 present_info.pWaitSemaphores = ¤t_image_data->present_layout_semaphore; | |
| 85 present_info.swapchainCount = 1; | |
| 86 present_info.pSwapchains = &swap_chain_; | |
| 87 present_info.pImageIndices = ¤t_image_; | |
| 88 | |
| 89 result = vkQueuePresentKHR(GetVulkanQueue(), &present_info); | |
| 90 if (VK_SUCCESS != result) { | |
| 91 return gfx::SwapResult::SWAP_FAILED; | |
| 92 } | |
| 93 | |
| 94 // Setup for the next available buffer. | |
| 95 // TODO(dyen): Look into if this does what I'm expecting. More studying of | |
| 96 // the spec is required here. It doesn't seem clear if "success" when timeout | |
| 97 // is 0 means it will give you a buffer even before it's done presenting. That | |
| 98 // is probably what we want and the present/render_layout semaphore will | |
| 99 // do the synchronization for us. | |
| 100 result = vkAcquireNextImageKHR(GetVulkanDevice(), swap_chain_, 0, | |
| 101 current_image_data->present_semaphore, | |
|
David Yen
2016/03/10 02:19:38
Hmm... looking at this code again I'm unclear whic
David Yen
2016/03/11 00:59:46
Chatted separately. This semaphore and the followi
| |
| 102 VK_NULL_HANDLE, ¤t_image_); | |
| 103 if (VK_SUCCESS != result) { | |
| 104 return gfx::SwapResult::SWAP_FAILED; | |
| 105 } | |
| 106 | |
| 107 // Setup the previous layout back to the render layout after presentation. | |
| 108 { | |
| 109 ScopedSingleUseCommandBufferRecorder recorder( | |
| 110 *image_layout_command_buffer_); | |
| 111 | |
| 112 VkImageMemoryBarrier image_memory_barrier = {}; | |
| 113 image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| 114 image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| 115 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
| 116 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| 117 image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 118 image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 119 image_memory_barrier.subresourceRange = image_subresource_range; | |
| 120 image_memory_barrier.image = current_image_data->image; | |
| 121 | |
| 122 vkCmdPipelineBarrier(recorder.handle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, | |
| 123 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, | |
| 124 nullptr, 1, &image_memory_barrier); | |
| 125 } | |
| 126 // TODO(dyen): This signals render_layout_semaphore which is waited upon | |
| 127 // above. When this submit is called it is possible the render layout | |
| 128 // semaphore is in an unsignalled state, however by the time the present | |
| 129 // semaphore is signalled it is guaranteed to be in an unsignalled state. | |
| 130 // We need to make sure this is ok, the spec doesn't specify any constraints | |
| 131 // here. | |
| 132 if (!image_layout_command_buffer_->Submit( | |
| 133 GetVulkanQueue(), 1, ¤t_image_data->present_semaphore, 1, | |
| 134 ¤t_image_data->render_layout_semaphore)) { | |
| 135 return gfx::SwapResult::SWAP_FAILED; | |
| 136 } | |
| 137 | |
| 138 return gfx::SwapResult::SWAP_ACK; | |
| 139 } | |
| 140 | |
| 141 bool VulkanSwapChain::InitializeSwapChain( | |
| 142 VkSurfaceKHR surface, | |
| 143 const VkSurfaceCapabilitiesKHR& surface_caps, | |
| 144 const VkSurfaceFormatKHR& surface_format) { | |
| 145 VkDevice device = GetVulkanDevice(); | |
| 146 VkResult result = VK_SUCCESS; | |
| 147 | |
| 148 VkSwapchainCreateInfoKHR swap_chain_create_info = {}; | |
| 149 swap_chain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
| 150 swap_chain_create_info.surface = surface; | |
| 151 swap_chain_create_info.minImageCount = | |
| 152 std::min(2u, surface_caps.maxImageCount); | |
| 153 swap_chain_create_info.imageFormat = surface_format.format; | |
| 154 swap_chain_create_info.imageColorSpace = surface_format.colorSpace; | |
| 155 swap_chain_create_info.imageExtent = surface_caps.currentExtent; | |
| 156 swap_chain_create_info.imageArrayLayers = 1; | |
| 157 swap_chain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
| 158 swap_chain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| 159 swap_chain_create_info.preTransform = surface_caps.currentTransform; | |
| 160 swap_chain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
| 161 swap_chain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; | |
| 162 swap_chain_create_info.clipped = true; | |
| 163 swap_chain_create_info.oldSwapchain = swap_chain_; | |
| 164 | |
| 165 VkSwapchainKHR new_swap_chain = VK_NULL_HANDLE; | |
| 166 result = vkCreateSwapchainKHR(device, &swap_chain_create_info, nullptr, | |
| 167 &new_swap_chain); | |
| 168 if (VK_SUCCESS != result) { | |
| 169 DLOG(ERROR) << "vkCreateSwapchainKHR() failed: " << result; | |
| 170 return false; | |
| 171 } | |
| 172 | |
| 173 // Destroy the old swap chain and buffers. | |
| 174 // TODO(dyen): Look into how to do this safely while commands in flight. | |
| 175 Destroy(); | |
| 176 | |
| 177 swap_chain_ = new_swap_chain; | |
| 178 return true; | |
| 179 } | |
| 180 | |
| 181 void VulkanSwapChain::DestroySwapChain() { | |
| 182 VkDevice device = GetVulkanDevice(); | |
| 183 | |
| 184 if (swap_chain_ != VK_NULL_HANDLE) { | |
| 185 vkDestroySwapchainKHR(device, swap_chain_, nullptr); | |
| 186 swap_chain_ = VK_NULL_HANDLE; | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 bool VulkanSwapChain::InitializeImageLayoutCommandBuffer() { | |
| 191 command_pool_ = CreateCommandPool(); | |
| 192 if (!command_pool_) | |
| 193 return false; | |
| 194 | |
| 195 image_layout_command_buffer_ = command_pool_->CreatePrimaryCommandBuffer(); | |
| 196 if (!image_layout_command_buffer_) | |
| 197 return false; | |
| 198 | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 void VulkanSwapChain::DestroyImageLayoutCommandBuffer() { | |
| 203 if (image_layout_command_buffer_) { | |
| 204 image_layout_command_buffer_->Destroy(); | |
| 205 image_layout_command_buffer_.reset(); | |
| 206 } | |
| 207 | |
| 208 if (command_pool_) { | |
| 209 command_pool_->Destroy(); | |
| 210 command_pool_.reset(); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 bool VulkanSwapChain::InitializeSwapImages( | |
| 215 const VkSurfaceCapabilitiesKHR& surface_caps, | |
| 216 const VkSurfaceFormatKHR& surface_format) { | |
| 217 VkDevice device = GetVulkanDevice(); | |
| 218 VkResult result = VK_SUCCESS; | |
| 219 | |
| 220 uint32_t image_count = 0; | |
| 221 result = vkGetSwapchainImagesKHR(device, swap_chain_, &image_count, nullptr); | |
| 222 if (VK_SUCCESS != result) { | |
| 223 DLOG(ERROR) << "vkGetSwapchainImagesKHR(NULL) failed: " << result; | |
| 224 return false; | |
| 225 } | |
| 226 | |
| 227 std::vector<VkImage> images(image_count); | |
| 228 result = | |
| 229 vkGetSwapchainImagesKHR(device, swap_chain_, &image_count, images.data()); | |
| 230 if (VK_SUCCESS != result) { | |
| 231 DLOG(ERROR) << "vkGetSwapchainImagesKHR(images) failed: " << result; | |
| 232 return false; | |
| 233 } | |
| 234 | |
| 235 // Generic semaphore creation structure. | |
| 236 VkSemaphoreCreateInfo semaphore_create_info = {}; | |
| 237 semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
| 238 | |
| 239 // Default image subresource range. | |
| 240 VkImageSubresourceRange image_subresource_range = {}; | |
| 241 image_subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| 242 image_subresource_range.baseMipLevel = 0; | |
| 243 image_subresource_range.levelCount = 1; | |
| 244 image_subresource_range.baseArrayLayer = 0; | |
| 245 image_subresource_range.layerCount = 1; | |
| 246 | |
| 247 // The image memory barrier is used to setup the image layout. | |
| 248 VkImageMemoryBarrier image_memory_barrier = {}; | |
| 249 image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| 250 image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| 251 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| 252 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| 253 image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 254 image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 255 image_memory_barrier.subresourceRange = image_subresource_range; | |
| 256 | |
| 257 // We must create an image view for each image. | |
| 258 VkImageViewCreateInfo image_view_create_info = {}; | |
| 259 image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
| 260 image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
| 261 image_view_create_info.format = surface_format.format; | |
| 262 image_view_create_info.components = { | |
| 263 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 264 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}; | |
| 265 image_view_create_info.subresourceRange = image_subresource_range; | |
| 266 | |
| 267 { | |
| 268 ScopedSingleUseCommandBufferRecorder recorder( | |
| 269 *image_layout_command_buffer_); | |
| 270 images_.resize(image_count); | |
| 271 for (uint32_t i = 0; i < image_count; ++i) { | |
| 272 images_[i].reset(new ImageData); | |
| 273 scoped_ptr<ImageData>& image_data = images_[i]; | |
| 274 image_data->image = images[i]; | |
| 275 | |
| 276 // Setup semaphores. | |
| 277 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 278 &image_data->render_layout_semaphore); | |
| 279 if (VK_SUCCESS != result) { | |
| 280 DLOG(ERROR) << "vkCreateSemaphore(render_layout) failed: " << result; | |
| 281 return false; | |
| 282 } | |
| 283 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 284 &image_data->render_semaphore); | |
| 285 if (VK_SUCCESS != result) { | |
| 286 DLOG(ERROR) << "vkCreateSemaphore(render) failed: " << result; | |
| 287 return false; | |
| 288 } | |
| 289 | |
| 290 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 291 &image_data->present_layout_semaphore); | |
| 292 if (VK_SUCCESS != result) { | |
| 293 DLOG(ERROR) << "vkCreateSemaphore(present_layout) failed: " << result; | |
| 294 return false; | |
| 295 } | |
| 296 | |
| 297 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 298 &image_data->present_semaphore); | |
| 299 if (VK_SUCCESS != result) { | |
| 300 DLOG(ERROR) << "vkCreateSemaphore(present) failed: " << result; | |
| 301 return false; | |
| 302 } | |
| 303 | |
| 304 // Setup the Image Layout. | |
| 305 image_memory_barrier.image = images[i]; | |
| 306 vkCmdPipelineBarrier(recorder.handle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, | |
| 307 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, | |
| 308 nullptr, 1, &image_memory_barrier); | |
| 309 | |
| 310 // Create the image view. | |
| 311 image_view_create_info.image = images[i]; | |
| 312 result = vkCreateImageView(device, &image_view_create_info, nullptr, | |
| 313 &image_data->image_view); | |
| 314 if (VK_SUCCESS != result) { | |
| 315 DLOG(ERROR) << "vkCreateImageView() failed: " << result; | |
| 316 return false; | |
| 317 } | |
| 318 | |
| 319 // Initialize the command buffer for this buffer data. | |
| 320 image_data->command_buffer = command_pool_->CreatePrimaryCommandBuffer(); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 // Submit the image layout commands, and tie them all to the render layout. | |
| 325 std::vector<VkSemaphore> initial_render_layout_semaphores(image_count); | |
| 326 for (uint32_t i = 0; i < image_count; ++i) { | |
| 327 initial_render_layout_semaphores[i] = images_[i]->render_layout_semaphore; | |
| 328 } | |
| 329 if (!image_layout_command_buffer_->Submit( | |
| 330 GetVulkanQueue(), 0, nullptr, image_count, | |
| 331 initial_render_layout_semaphores.data())) { | |
| 332 return false; | |
| 333 } | |
| 334 | |
| 335 return true; | |
| 336 } | |
| 337 | |
| 338 void VulkanSwapChain::DestroySwapImages() { | |
| 339 VkDevice device = GetVulkanDevice(); | |
| 340 | |
| 341 for (const scoped_ptr<ImageData>& image_data : images_) { | |
| 342 if (image_data->command_buffer) { | |
| 343 image_data->command_buffer->Destroy(); | |
| 344 image_data->command_buffer.reset(); | |
| 345 } | |
| 346 | |
| 347 // Destroy Image View. | |
| 348 if (VK_NULL_HANDLE != image_data->image_view) { | |
| 349 vkDestroyImageView(device, image_data->image_view, nullptr); | |
| 350 image_data->image_view = VK_NULL_HANDLE; | |
| 351 } | |
| 352 | |
| 353 // Destroy Semaphores. | |
| 354 if (VK_NULL_HANDLE != image_data->present_semaphore) { | |
| 355 vkDestroySemaphore(device, image_data->present_semaphore, nullptr); | |
| 356 image_data->present_semaphore = VK_NULL_HANDLE; | |
| 357 } | |
| 358 | |
| 359 if (VK_NULL_HANDLE != image_data->present_layout_semaphore) { | |
| 360 vkDestroySemaphore(device, image_data->present_layout_semaphore, nullptr); | |
| 361 image_data->present_layout_semaphore = VK_NULL_HANDLE; | |
| 362 } | |
| 363 | |
| 364 if (VK_NULL_HANDLE != image_data->render_semaphore) { | |
| 365 vkDestroySemaphore(device, image_data->render_semaphore, nullptr); | |
| 366 image_data->render_semaphore = VK_NULL_HANDLE; | |
| 367 } | |
| 368 | |
| 369 if (VK_NULL_HANDLE != image_data->render_layout_semaphore) { | |
| 370 vkDestroySemaphore(device, image_data->render_layout_semaphore, nullptr); | |
| 371 image_data->render_layout_semaphore = VK_NULL_HANDLE; | |
| 372 } | |
| 373 | |
| 374 image_data->image = VK_NULL_HANDLE; | |
| 375 } | |
| 376 images_.clear(); | |
| 377 } | |
| 378 | |
| 379 bool VulkanSwapChain::InitializeInitialBuffer() { | |
| 380 // Acquire the initial buffer, all the buffers have their initial render | |
| 381 // layout semaphore setup so this is a special case. | |
| 382 const VkResult result = | |
| 383 vkAcquireNextImageKHR(GetVulkanDevice(), swap_chain_, 0, VK_NULL_HANDLE, | |
| 384 VK_NULL_HANDLE, ¤t_image_); | |
| 385 if (VK_SUCCESS != result) { | |
| 386 DLOG(ERROR) << "vkAcquireNextImageKHR() failed: " << result; | |
| 387 return false; | |
| 388 } | |
| 389 | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 VulkanSwapChain::ImageData::ImageData() {} | |
| 394 | |
| 395 VulkanSwapChain::ImageData::~ImageData() {} | |
| 396 | |
| 397 } // namespace gpu | |
| OLD | NEW |