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 |