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)) { | |
|
piman
2016/03/11 03:07:52
You don't need a semaphore to order command buffer
David Yen
2016/03/21 18:26:31
That would make things easier. I wasn't sure becau
| |
| 52 return gfx::SwapResult::SWAP_FAILED; | |
| 53 } | |
| 54 | |
| 55 // Prepare the image layout to present. | |
| 56 { | |
| 57 ScopedSingleUseCommandBufferRecorder recorder( | |
|
piman
2016/03/11 03:07:53
2 things:
1- you can't reuse the command buffer un
David Yen
2016/03/21 18:26:31
Done. To make how this is suppose to be done clear
| |
| 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. | |
|
piman
2016/03/11 03:07:53
vkAcquireNextImageKHR needs to be able to block, r
David Yen
2016/03/21 18:26:31
Done.
| |
| 100 result = vkAcquireNextImageKHR(GetVulkanDevice(), swap_chain_, 0, | |
| 101 current_image_data->present_semaphore, | |
| 102 VK_NULL_HANDLE, ¤t_image_); | |
| 103 if (VK_SUCCESS != result) { | |
| 104 return gfx::SwapResult::SWAP_FAILED; | |
| 105 } | |
| 106 | |
| 107 scoped_ptr<ImageData>& next_image_data = images_[current_image_]; | |
| 108 | |
| 109 // The present semaphore actually is associated with the new image data, but | |
| 110 // the previous one is guaranteed to be unused to swap it to the correct one. | |
| 111 std::swap(next_image_data->present_semaphore, | |
| 112 current_image_data->present_semaphore); | |
| 113 | |
| 114 // Setup the previous layout back to the render layout after presentation. | |
| 115 { | |
| 116 ScopedSingleUseCommandBufferRecorder recorder( | |
|
piman
2016/03/11 03:07:52
Same points as above.
David Yen
2016/03/21 18:26:31
Done.
| |
| 117 *image_layout_command_buffer_); | |
| 118 | |
| 119 VkImageMemoryBarrier image_memory_barrier = {}; | |
| 120 image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| 121 image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| 122 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
| 123 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| 124 image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 125 image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 126 image_memory_barrier.subresourceRange = image_subresource_range; | |
| 127 image_memory_barrier.image = next_image_data->image; | |
| 128 | |
| 129 vkCmdPipelineBarrier(recorder.handle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, | |
| 130 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, | |
| 131 nullptr, 1, &image_memory_barrier); | |
| 132 } | |
| 133 // TODO(dyen): This signals render_layout_semaphore which is waited upon | |
| 134 // above. When this submit is called it is possible the render layout | |
| 135 // semaphore is in an unsignalled state, however by the time the present | |
| 136 // semaphore is signalled it is guaranteed to be in an unsignalled state. | |
| 137 // We need to make sure this is ok, the spec doesn't specify any constraints | |
| 138 // here. | |
|
piman
2016/03/11 03:07:53
This should be ok (note that I don't think you nee
David Yen
2016/03/21 18:26:31
Acknowledged.
| |
| 139 if (!image_layout_command_buffer_->Submit( | |
| 140 GetVulkanQueue(), 1, &next_image_data->present_semaphore, 1, | |
| 141 &next_image_data->render_layout_semaphore)) { | |
| 142 return gfx::SwapResult::SWAP_FAILED; | |
| 143 } | |
| 144 | |
| 145 return gfx::SwapResult::SWAP_ACK; | |
| 146 } | |
| 147 | |
| 148 bool VulkanSwapChain::InitializeSwapChain( | |
| 149 VkSurfaceKHR surface, | |
| 150 const VkSurfaceCapabilitiesKHR& surface_caps, | |
| 151 const VkSurfaceFormatKHR& surface_format) { | |
| 152 VkDevice device = GetVulkanDevice(); | |
| 153 VkResult result = VK_SUCCESS; | |
| 154 | |
| 155 VkSwapchainCreateInfoKHR swap_chain_create_info = {}; | |
| 156 swap_chain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
| 157 swap_chain_create_info.surface = surface; | |
| 158 swap_chain_create_info.minImageCount = | |
| 159 std::min(2u, surface_caps.maxImageCount); | |
|
piman
2016/03/11 03:07:53
It does have to be more than surface_caps.minImage
David Yen
2016/03/21 18:26:31
Done.
| |
| 160 swap_chain_create_info.imageFormat = surface_format.format; | |
| 161 swap_chain_create_info.imageColorSpace = surface_format.colorSpace; | |
| 162 swap_chain_create_info.imageExtent = surface_caps.currentExtent; | |
| 163 swap_chain_create_info.imageArrayLayers = 1; | |
| 164 swap_chain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
| 165 swap_chain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| 166 swap_chain_create_info.preTransform = surface_caps.currentTransform; | |
| 167 swap_chain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
| 168 swap_chain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; | |
| 169 swap_chain_create_info.clipped = true; | |
| 170 swap_chain_create_info.oldSwapchain = swap_chain_; | |
| 171 | |
| 172 VkSwapchainKHR new_swap_chain = VK_NULL_HANDLE; | |
| 173 result = vkCreateSwapchainKHR(device, &swap_chain_create_info, nullptr, | |
| 174 &new_swap_chain); | |
| 175 if (VK_SUCCESS != result) { | |
| 176 DLOG(ERROR) << "vkCreateSwapchainKHR() failed: " << result; | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 // Destroy the old swap chain and buffers. | |
| 181 // TODO(dyen): Look into how to do this safely while commands in flight. | |
| 182 Destroy(); | |
| 183 | |
| 184 swap_chain_ = new_swap_chain; | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 void VulkanSwapChain::DestroySwapChain() { | |
| 189 VkDevice device = GetVulkanDevice(); | |
| 190 | |
| 191 if (swap_chain_ != VK_NULL_HANDLE) { | |
| 192 vkDestroySwapchainKHR(device, swap_chain_, nullptr); | |
| 193 swap_chain_ = VK_NULL_HANDLE; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 bool VulkanSwapChain::InitializeImageLayoutCommandBuffer() { | |
| 198 command_pool_ = CreateCommandPool(); | |
| 199 if (!command_pool_) | |
| 200 return false; | |
| 201 | |
| 202 image_layout_command_buffer_ = command_pool_->CreatePrimaryCommandBuffer(); | |
| 203 if (!image_layout_command_buffer_) | |
| 204 return false; | |
| 205 | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 void VulkanSwapChain::DestroyImageLayoutCommandBuffer() { | |
| 210 if (image_layout_command_buffer_) { | |
| 211 image_layout_command_buffer_->Destroy(); | |
| 212 image_layout_command_buffer_.reset(); | |
| 213 } | |
| 214 | |
| 215 if (command_pool_) { | |
| 216 command_pool_->Destroy(); | |
| 217 command_pool_.reset(); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 bool VulkanSwapChain::InitializeSwapImages( | |
| 222 const VkSurfaceCapabilitiesKHR& surface_caps, | |
| 223 const VkSurfaceFormatKHR& surface_format) { | |
| 224 VkDevice device = GetVulkanDevice(); | |
| 225 VkResult result = VK_SUCCESS; | |
| 226 | |
| 227 uint32_t image_count = 0; | |
| 228 result = vkGetSwapchainImagesKHR(device, swap_chain_, &image_count, nullptr); | |
| 229 if (VK_SUCCESS != result) { | |
| 230 DLOG(ERROR) << "vkGetSwapchainImagesKHR(NULL) failed: " << result; | |
| 231 return false; | |
| 232 } | |
| 233 | |
| 234 std::vector<VkImage> images(image_count); | |
| 235 result = | |
| 236 vkGetSwapchainImagesKHR(device, swap_chain_, &image_count, images.data()); | |
| 237 if (VK_SUCCESS != result) { | |
| 238 DLOG(ERROR) << "vkGetSwapchainImagesKHR(images) failed: " << result; | |
| 239 return false; | |
| 240 } | |
| 241 | |
| 242 // Generic semaphore creation structure. | |
| 243 VkSemaphoreCreateInfo semaphore_create_info = {}; | |
| 244 semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
| 245 | |
| 246 // Default image subresource range. | |
| 247 VkImageSubresourceRange image_subresource_range = {}; | |
| 248 image_subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| 249 image_subresource_range.baseMipLevel = 0; | |
| 250 image_subresource_range.levelCount = 1; | |
| 251 image_subresource_range.baseArrayLayer = 0; | |
| 252 image_subresource_range.layerCount = 1; | |
| 253 | |
| 254 // The image memory barrier is used to setup the image layout. | |
| 255 VkImageMemoryBarrier image_memory_barrier = {}; | |
| 256 image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
| 257 image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| 258 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| 259 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| 260 image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 261 image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
| 262 image_memory_barrier.subresourceRange = image_subresource_range; | |
| 263 | |
| 264 // We must create an image view for each image. | |
| 265 VkImageViewCreateInfo image_view_create_info = {}; | |
| 266 image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
| 267 image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
| 268 image_view_create_info.format = surface_format.format; | |
| 269 image_view_create_info.components = { | |
| 270 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 271 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}; | |
| 272 image_view_create_info.subresourceRange = image_subresource_range; | |
| 273 | |
| 274 { | |
| 275 ScopedSingleUseCommandBufferRecorder recorder( | |
| 276 *image_layout_command_buffer_); | |
| 277 images_.resize(image_count); | |
| 278 for (uint32_t i = 0; i < image_count; ++i) { | |
| 279 images_[i].reset(new ImageData); | |
| 280 scoped_ptr<ImageData>& image_data = images_[i]; | |
| 281 image_data->image = images[i]; | |
| 282 | |
| 283 // Setup semaphores. | |
| 284 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 285 &image_data->render_layout_semaphore); | |
| 286 if (VK_SUCCESS != result) { | |
| 287 DLOG(ERROR) << "vkCreateSemaphore(render_layout) failed: " << result; | |
| 288 return false; | |
| 289 } | |
| 290 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 291 &image_data->render_semaphore); | |
| 292 if (VK_SUCCESS != result) { | |
| 293 DLOG(ERROR) << "vkCreateSemaphore(render) failed: " << result; | |
| 294 return false; | |
| 295 } | |
| 296 | |
| 297 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 298 &image_data->present_layout_semaphore); | |
| 299 if (VK_SUCCESS != result) { | |
| 300 DLOG(ERROR) << "vkCreateSemaphore(present_layout) failed: " << result; | |
| 301 return false; | |
| 302 } | |
| 303 | |
| 304 result = vkCreateSemaphore(device, &semaphore_create_info, nullptr, | |
| 305 &image_data->present_semaphore); | |
| 306 if (VK_SUCCESS != result) { | |
| 307 DLOG(ERROR) << "vkCreateSemaphore(present) failed: " << result; | |
| 308 return false; | |
| 309 } | |
| 310 | |
| 311 // Setup the Image Layout. | |
| 312 image_memory_barrier.image = images[i]; | |
| 313 vkCmdPipelineBarrier(recorder.handle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, | |
| 314 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, | |
| 315 nullptr, 1, &image_memory_barrier); | |
| 316 | |
| 317 // Create the image view. | |
| 318 image_view_create_info.image = images[i]; | |
| 319 result = vkCreateImageView(device, &image_view_create_info, nullptr, | |
| 320 &image_data->image_view); | |
| 321 if (VK_SUCCESS != result) { | |
| 322 DLOG(ERROR) << "vkCreateImageView() failed: " << result; | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 // Initialize the command buffer for this buffer data. | |
| 327 image_data->command_buffer = command_pool_->CreatePrimaryCommandBuffer(); | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 // Submit the image layout commands, and tie them all to the render layout. | |
| 332 std::vector<VkSemaphore> initial_render_layout_semaphores(image_count); | |
| 333 for (uint32_t i = 0; i < image_count; ++i) { | |
| 334 initial_render_layout_semaphores[i] = images_[i]->render_layout_semaphore; | |
| 335 } | |
| 336 if (!image_layout_command_buffer_->Submit( | |
| 337 GetVulkanQueue(), 0, nullptr, image_count, | |
| 338 initial_render_layout_semaphores.data())) { | |
| 339 return false; | |
| 340 } | |
| 341 | |
| 342 return true; | |
| 343 } | |
| 344 | |
| 345 void VulkanSwapChain::DestroySwapImages() { | |
| 346 VkDevice device = GetVulkanDevice(); | |
| 347 | |
| 348 for (const scoped_ptr<ImageData>& image_data : images_) { | |
| 349 if (image_data->command_buffer) { | |
| 350 image_data->command_buffer->Destroy(); | |
| 351 image_data->command_buffer.reset(); | |
| 352 } | |
| 353 | |
| 354 // Destroy Image View. | |
| 355 if (VK_NULL_HANDLE != image_data->image_view) { | |
| 356 vkDestroyImageView(device, image_data->image_view, nullptr); | |
| 357 image_data->image_view = VK_NULL_HANDLE; | |
| 358 } | |
| 359 | |
| 360 // Destroy Semaphores. | |
| 361 if (VK_NULL_HANDLE != image_data->present_semaphore) { | |
| 362 vkDestroySemaphore(device, image_data->present_semaphore, nullptr); | |
| 363 image_data->present_semaphore = VK_NULL_HANDLE; | |
| 364 } | |
| 365 | |
| 366 if (VK_NULL_HANDLE != image_data->present_layout_semaphore) { | |
| 367 vkDestroySemaphore(device, image_data->present_layout_semaphore, nullptr); | |
| 368 image_data->present_layout_semaphore = VK_NULL_HANDLE; | |
| 369 } | |
| 370 | |
| 371 if (VK_NULL_HANDLE != image_data->render_semaphore) { | |
| 372 vkDestroySemaphore(device, image_data->render_semaphore, nullptr); | |
| 373 image_data->render_semaphore = VK_NULL_HANDLE; | |
| 374 } | |
| 375 | |
| 376 if (VK_NULL_HANDLE != image_data->render_layout_semaphore) { | |
| 377 vkDestroySemaphore(device, image_data->render_layout_semaphore, nullptr); | |
| 378 image_data->render_layout_semaphore = VK_NULL_HANDLE; | |
| 379 } | |
| 380 | |
| 381 image_data->image = VK_NULL_HANDLE; | |
| 382 } | |
| 383 images_.clear(); | |
| 384 } | |
| 385 | |
| 386 bool VulkanSwapChain::InitializeInitialBuffer() { | |
| 387 // Acquire the initial buffer, all the buffers have their initial render | |
| 388 // layout semaphore setup so this is a special case. | |
| 389 const VkResult result = | |
| 390 vkAcquireNextImageKHR(GetVulkanDevice(), swap_chain_, 0, VK_NULL_HANDLE, | |
| 391 VK_NULL_HANDLE, ¤t_image_); | |
| 392 if (VK_SUCCESS != result) { | |
| 393 DLOG(ERROR) << "vkAcquireNextImageKHR() failed: " << result; | |
| 394 return false; | |
| 395 } | |
| 396 | |
| 397 return true; | |
| 398 } | |
| 399 | |
| 400 VulkanSwapChain::ImageData::ImageData() {} | |
| 401 | |
| 402 VulkanSwapChain::ImageData::~ImageData() {} | |
| 403 | |
| 404 } // namespace gpu | |
| OLD | NEW |