• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/vulkan/SwapChainVk.h"
16 
17 #include "common/Compiler.h"
18 #include "dawn_native/Instance.h"
19 #include "dawn_native/Surface.h"
20 #include "dawn_native/vulkan/AdapterVk.h"
21 #include "dawn_native/vulkan/BackendVk.h"
22 #include "dawn_native/vulkan/DeviceVk.h"
23 #include "dawn_native/vulkan/FencedDeleter.h"
24 #include "dawn_native/vulkan/TextureVk.h"
25 #include "dawn_native/vulkan/VulkanError.h"
26 
27 #include <algorithm>
28 
29 #if defined(DAWN_USE_X11)
30 #    include "dawn_native/XlibXcbFunctions.h"
31 #endif  // defined(DAWN_USE_X11)
32 
33 namespace dawn_native { namespace vulkan {
34 
35     // OldSwapChain
36 
37     // static
Create(Device * device,const SwapChainDescriptor * descriptor)38     Ref<OldSwapChain> OldSwapChain::Create(Device* device, const SwapChainDescriptor* descriptor) {
39         return AcquireRef(new OldSwapChain(device, descriptor));
40     }
41 
OldSwapChain(Device * device,const SwapChainDescriptor * descriptor)42     OldSwapChain::OldSwapChain(Device* device, const SwapChainDescriptor* descriptor)
43         : OldSwapChainBase(device, descriptor) {
44         const auto& im = GetImplementation();
45         DawnWSIContextVulkan wsiContext = {};
46         im.Init(im.userData, &wsiContext);
47 
48         ASSERT(im.textureUsage != WGPUTextureUsage_None);
49         mTextureUsage = static_cast<wgpu::TextureUsage>(im.textureUsage);
50     }
51 
~OldSwapChain()52     OldSwapChain::~OldSwapChain() {
53     }
54 
GetNextTextureImpl(const TextureDescriptor * descriptor)55     TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) {
56         const auto& im = GetImplementation();
57         DawnSwapChainNextTexture next = {};
58         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
59 
60         if (error) {
61             GetDevice()->HandleError(InternalErrorType::Internal, error);
62             return nullptr;
63         }
64 
65         ::VkImage image = NativeNonDispatachableHandleFromU64<::VkImage>(next.texture.u64);
66         VkImage nativeTexture = VkImage::CreateFromHandle(image);
67         return Texture::CreateForSwapChain(ToBackend(GetDevice()), descriptor, nativeTexture)
68             .Detach();
69     }
70 
OnBeforePresent(TextureViewBase * view)71     MaybeError OldSwapChain::OnBeforePresent(TextureViewBase* view) {
72         Device* device = ToBackend(GetDevice());
73 
74         // Perform the necessary pipeline barriers for the texture to be used with the usage
75         // requested by the implementation.
76         CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
77         ToBackend(view->GetTexture())
78             ->TransitionUsageNow(recordingContext, mTextureUsage, view->GetSubresourceRange());
79 
80         DAWN_TRY(device->SubmitPendingCommands());
81 
82         return {};
83     }
84 
85     // SwapChain
86 
87     namespace {
88 
CreateVulkanSurface(Adapter * adapter,Surface * surface)89         ResultOrError<VkSurfaceKHR> CreateVulkanSurface(Adapter* adapter, Surface* surface) {
90             const VulkanGlobalInfo& info = adapter->GetVulkanInstance()->GetGlobalInfo();
91             const VulkanFunctions& fn = adapter->GetVulkanInstance()->GetFunctions();
92             VkInstance instance = adapter->GetVulkanInstance()->GetVkInstance();
93 
94             // May not be used in the platform-specific switches below.
95             DAWN_UNUSED(info);
96             DAWN_UNUSED(fn);
97             DAWN_UNUSED(instance);
98 
99             switch (surface->GetType()) {
100 #if defined(DAWN_ENABLE_BACKEND_METAL)
101                 case Surface::Type::MetalLayer:
102                     if (info.HasExt(InstanceExt::MetalSurface)) {
103                         VkMetalSurfaceCreateInfoEXT createInfo;
104                         createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
105                         createInfo.pNext = nullptr;
106                         createInfo.flags = 0;
107                         createInfo.pLayer = surface->GetMetalLayer();
108 
109                         VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
110                         DAWN_TRY(CheckVkSuccess(
111                             fn.CreateMetalSurfaceEXT(instance, &createInfo, nullptr, &*vkSurface),
112                             "CreateMetalSurface"));
113                         return vkSurface;
114                     }
115                     break;
116 #endif  // defined(DAWN_ENABLE_BACKEND_METAL)
117 
118 #if defined(DAWN_PLATFORM_WINDOWS)
119                 case Surface::Type::WindowsHWND:
120                     if (info.HasExt(InstanceExt::Win32Surface)) {
121                         VkWin32SurfaceCreateInfoKHR createInfo;
122                         createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
123                         createInfo.pNext = nullptr;
124                         createInfo.flags = 0;
125                         createInfo.hinstance = static_cast<HINSTANCE>(surface->GetHInstance());
126                         createInfo.hwnd = static_cast<HWND>(surface->GetHWND());
127 
128                         VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
129                         DAWN_TRY(CheckVkSuccess(
130                             fn.CreateWin32SurfaceKHR(instance, &createInfo, nullptr, &*vkSurface),
131                             "CreateWin32Surface"));
132                         return vkSurface;
133                     }
134                     break;
135 #endif  // defined(DAWN_PLATFORM_WINDOWS)
136 
137 #if defined(DAWN_USE_X11)
138                 case Surface::Type::Xlib: {
139                     if (info.HasExt(InstanceExt::XlibSurface)) {
140                         VkXlibSurfaceCreateInfoKHR createInfo;
141                         createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
142                         createInfo.pNext = nullptr;
143                         createInfo.flags = 0;
144                         createInfo.dpy = static_cast<Display*>(surface->GetXDisplay());
145                         createInfo.window = surface->GetXWindow();
146 
147                         VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
148                         DAWN_TRY(CheckVkSuccess(
149                             fn.CreateXlibSurfaceKHR(instance, &createInfo, nullptr, &*vkSurface),
150                             "CreateXlibSurface"));
151                         return vkSurface;
152                     }
153 
154                     // Fall back to using XCB surfaces if the Xlib extension isn't available.
155                     // See https://xcb.freedesktop.org/MixingCalls/ for more information about
156                     // interoperability between Xlib and XCB
157                     const XlibXcbFunctions* xlibXcb =
158                         adapter->GetInstance()->GetOrCreateXlibXcbFunctions();
159                     ASSERT(xlibXcb != nullptr);
160 
161                     if (info.HasExt(InstanceExt::XcbSurface) && xlibXcb->IsLoaded()) {
162                         VkXcbSurfaceCreateInfoKHR createInfo;
163                         createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
164                         createInfo.pNext = nullptr;
165                         createInfo.flags = 0;
166                         // The XCB connection lives as long as the X11 display.
167                         createInfo.connection = xlibXcb->xGetXCBConnection(
168                             static_cast<Display*>(surface->GetXDisplay()));
169                         createInfo.window = surface->GetXWindow();
170 
171                         VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
172                         DAWN_TRY(CheckVkSuccess(
173                             fn.CreateXcbSurfaceKHR(instance, &createInfo, nullptr, &*vkSurface),
174                             "CreateXcbSurfaceKHR"));
175                         return vkSurface;
176                     }
177                     break;
178                 }
179 #endif  // defined(DAWN_USE_X11)
180 
181                 default:
182                     break;
183             }
184 
185             return DAWN_FORMAT_VALIDATION_ERROR("Unsupported surface type (%s) for Vulkan.",
186                                                 surface->GetType());
187         }
188 
ToVulkanPresentMode(wgpu::PresentMode mode)189         VkPresentModeKHR ToVulkanPresentMode(wgpu::PresentMode mode) {
190             switch (mode) {
191                 case wgpu::PresentMode::Fifo:
192                     return VK_PRESENT_MODE_FIFO_KHR;
193                 case wgpu::PresentMode::Immediate:
194                     return VK_PRESENT_MODE_IMMEDIATE_KHR;
195                 case wgpu::PresentMode::Mailbox:
196                     return VK_PRESENT_MODE_MAILBOX_KHR;
197             }
198             UNREACHABLE();
199         }
200 
MinImageCountForPresentMode(VkPresentModeKHR mode)201         uint32_t MinImageCountForPresentMode(VkPresentModeKHR mode) {
202             switch (mode) {
203                 case VK_PRESENT_MODE_FIFO_KHR:
204                 case VK_PRESENT_MODE_IMMEDIATE_KHR:
205                     return 2;
206                 case VK_PRESENT_MODE_MAILBOX_KHR:
207                     return 3;
208                 default:
209                     break;
210             }
211             UNREACHABLE();
212         }
213 
214     }  // anonymous namespace
215 
216     // static
Create(Device * device,Surface * surface,NewSwapChainBase * previousSwapChain,const SwapChainDescriptor * descriptor)217     ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device,
218                                                     Surface* surface,
219                                                     NewSwapChainBase* previousSwapChain,
220                                                     const SwapChainDescriptor* descriptor) {
221         Ref<SwapChain> swapchain = AcquireRef(new SwapChain(device, surface, descriptor));
222         DAWN_TRY(swapchain->Initialize(previousSwapChain));
223         return swapchain;
224     }
225 
226     SwapChain::~SwapChain() = default;
227 
DestroyImpl()228     void SwapChain::DestroyImpl() {
229         SwapChainBase::DestroyImpl();
230         DetachFromSurface();
231     }
232 
233     // Note that when we need to re-create the swapchain because it is out of date,
234     // previousSwapChain can be set to `this`.
Initialize(NewSwapChainBase * previousSwapChain)235     MaybeError SwapChain::Initialize(NewSwapChainBase* previousSwapChain) {
236         Device* device = ToBackend(GetDevice());
237         Adapter* adapter = ToBackend(GetDevice()->GetAdapter());
238 
239         VkSwapchainKHR previousVkSwapChain = VK_NULL_HANDLE;
240 
241         if (previousSwapChain != nullptr) {
242             // TODO(crbug.com/dawn/269): The first time a surface is used with a Device, check
243             // it is supported with vkGetPhysicalDeviceSurfaceSupportKHR.
244 
245             // TODO(crbug.com/dawn/269): figure out what should happen when surfaces are used by
246             // multiple backends one after the other. It probably needs to block until the backend
247             // and GPU are completely finished with the previous swapchain.
248             DAWN_INVALID_IF(previousSwapChain->GetBackendType() != wgpu::BackendType::Vulkan,
249                             "Vulkan SwapChain cannot switch backend types from %s to %s.",
250                             previousSwapChain->GetBackendType(), wgpu::BackendType::Vulkan);
251 
252             // TODO(crbug.com/dawn/269): use ToBackend once OldSwapChainBase is removed.
253             SwapChain* previousVulkanSwapChain = static_cast<SwapChain*>(previousSwapChain);
254 
255             // TODO(crbug.com/dawn/269): Figure out switching a single surface between multiple
256             // Vulkan devices on different VkInstances. Probably needs to block too!
257             VkInstance previousInstance =
258                 ToBackend(previousSwapChain->GetDevice())->GetVkInstance();
259             DAWN_INVALID_IF(previousInstance != ToBackend(GetDevice())->GetVkInstance(),
260                             "Vulkan SwapChain cannot switch between Vulkan instances.");
261 
262             // The previous swapchain is a dawn_native::vulkan::SwapChain so we can reuse its
263             // VkSurfaceKHR provided since they are on the same instance.
264             std::swap(previousVulkanSwapChain->mVkSurface, mVkSurface);
265 
266             // The previous swapchain was on the same Vulkan instance so we can use Vulkan's
267             // "oldSwapchain" mechanism to ensure a seamless transition. We track the previous
268             // swapchain for release immediately so it is not leaked in case of an error. (Vulkan
269             // allows destroying it immediately after the call to vkCreateSwapChainKHR but tracking
270             // using the fenced deleter makes the code simpler).
271             std::swap(previousVulkanSwapChain->mSwapChain, previousVkSwapChain);
272             ToBackend(previousSwapChain->GetDevice())
273                 ->GetFencedDeleter()
274                 ->DeleteWhenUnused(previousVkSwapChain);
275         }
276 
277         if (mVkSurface == VK_NULL_HANDLE) {
278             DAWN_TRY_ASSIGN(mVkSurface, CreateVulkanSurface(adapter, GetSurface()));
279         }
280 
281         VulkanSurfaceInfo surfaceInfo;
282         DAWN_TRY_ASSIGN(surfaceInfo, GatherSurfaceInfo(*adapter, mVkSurface));
283 
284         DAWN_TRY_ASSIGN(mConfig, ChooseConfig(surfaceInfo));
285 
286         // TODO Choose config instead of hardcoding
287         VkSwapchainCreateInfoKHR createInfo;
288         createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
289         createInfo.pNext = nullptr;
290         createInfo.flags = 0;
291         createInfo.surface = mVkSurface;
292         createInfo.minImageCount = mConfig.targetImageCount;
293         createInfo.imageFormat = mConfig.format;
294         createInfo.imageColorSpace = mConfig.colorSpace;
295         createInfo.imageExtent = mConfig.extent;
296         createInfo.imageArrayLayers = 1;
297         createInfo.imageUsage = mConfig.usage;
298         createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
299         createInfo.queueFamilyIndexCount = 0;
300         createInfo.pQueueFamilyIndices = nullptr;
301         createInfo.preTransform = mConfig.transform;
302         createInfo.compositeAlpha = mConfig.alphaMode;
303         createInfo.presentMode = mConfig.presentMode;
304         createInfo.clipped = false;
305         createInfo.oldSwapchain = previousVkSwapChain;
306 
307         DAWN_TRY(CheckVkSuccess(device->fn.CreateSwapchainKHR(device->GetVkDevice(), &createInfo,
308                                                               nullptr, &*mSwapChain),
309                                 "CreateSwapChain"));
310 
311         // Gather the swapchain's images. Implementations are allowed to return more images than the
312         // number we asked for.
313         uint32_t count = 0;
314         DAWN_TRY(CheckVkSuccess(
315             device->fn.GetSwapchainImagesKHR(device->GetVkDevice(), mSwapChain, &count, nullptr),
316             "GetSwapChainImages1"));
317 
318         mSwapChainImages.resize(count);
319         DAWN_TRY(CheckVkSuccess(
320             device->fn.GetSwapchainImagesKHR(device->GetVkDevice(), mSwapChain, &count,
321                                              AsVkArray(mSwapChainImages.data())),
322             "GetSwapChainImages2"));
323 
324         return {};
325     }
326 
ChooseConfig(const VulkanSurfaceInfo & surfaceInfo) const327     ResultOrError<SwapChain::Config> SwapChain::ChooseConfig(
328         const VulkanSurfaceInfo& surfaceInfo) const {
329         Config config;
330 
331         // Choose the present mode. The only guaranteed one is FIFO so it has to be the fallback for
332         // all other present modes. IMMEDIATE has tearing which is generally undesirable so it can't
333         // be the fallback for MAILBOX. So the fallback order is always IMMEDIATE -> MAILBOX ->
334         // FIFO.
335         {
336             auto HasPresentMode = [](const std::vector<VkPresentModeKHR>& modes,
337                                      VkPresentModeKHR target) -> bool {
338                 return std::find(modes.begin(), modes.end(), target) != modes.end();
339             };
340 
341             VkPresentModeKHR targetMode = ToVulkanPresentMode(GetPresentMode());
342             const std::array<VkPresentModeKHR, 3> kPresentModeFallbacks = {
343                 VK_PRESENT_MODE_IMMEDIATE_KHR,
344                 VK_PRESENT_MODE_MAILBOX_KHR,
345                 VK_PRESENT_MODE_FIFO_KHR,
346             };
347 
348             // Go to the target mode.
349             size_t modeIndex = 0;
350             while (kPresentModeFallbacks[modeIndex] != targetMode) {
351                 modeIndex++;
352             }
353 
354             // Find the first available fallback.
355             while (!HasPresentMode(surfaceInfo.presentModes, kPresentModeFallbacks[modeIndex])) {
356                 modeIndex++;
357             }
358 
359             ASSERT(modeIndex < kPresentModeFallbacks.size());
360             config.presentMode = kPresentModeFallbacks[modeIndex];
361         }
362 
363         // Choose the target width or do a blit.
364         if (GetWidth() < surfaceInfo.capabilities.minImageExtent.width ||
365             GetWidth() > surfaceInfo.capabilities.maxImageExtent.width ||
366             GetHeight() < surfaceInfo.capabilities.minImageExtent.height ||
367             GetHeight() > surfaceInfo.capabilities.maxImageExtent.height) {
368             config.needsBlit = true;
369         } else {
370             config.extent.width = GetWidth();
371             config.extent.height = GetHeight();
372         }
373 
374         // Choose the target usage or do a blit.
375         VkImageUsageFlags targetUsages =
376             VulkanImageUsage(GetUsage(), GetDevice()->GetValidInternalFormat(GetFormat()));
377         VkImageUsageFlags supportedUsages = surfaceInfo.capabilities.supportedUsageFlags;
378         if (!IsSubset(targetUsages, supportedUsages)) {
379             config.needsBlit = true;
380         } else {
381             config.usage = targetUsages;
382             config.wgpuUsage = GetUsage();
383         }
384 
385         // Only support BGRA8Unorm with SRGB color space for now.
386         bool hasBGRA8Unorm = false;
387         for (const VkSurfaceFormatKHR& format : surfaceInfo.formats) {
388             if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
389                 format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
390                 hasBGRA8Unorm = true;
391                 break;
392             }
393         }
394         if (!hasBGRA8Unorm) {
395             return DAWN_INTERNAL_ERROR(
396                 "Vulkan SwapChain must support BGRA8Unorm with sRGB colorspace.");
397         }
398         config.format = VK_FORMAT_B8G8R8A8_UNORM;
399         config.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
400         config.wgpuFormat = wgpu::TextureFormat::BGRA8Unorm;
401 
402         // Only the identity transform with opaque alpha is supported for now.
403         DAWN_INVALID_IF((surfaceInfo.capabilities.supportedTransforms &
404                          VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) == 0,
405                         "Vulkan SwapChain must support the identity transform.");
406 
407         config.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
408 
409         DAWN_INVALID_IF((surfaceInfo.capabilities.supportedCompositeAlpha &
410                          VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) == 0,
411                         "Vulkan SwapChain must support opaque alpha.");
412 
413         config.alphaMode = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
414 
415         // Choose the number of images for the swapchain= and clamp it to the min and max from the
416         // surface capabilities. maxImageCount = 0 means there is no limit.
417         ASSERT(surfaceInfo.capabilities.maxImageCount == 0 ||
418                surfaceInfo.capabilities.minImageCount <= surfaceInfo.capabilities.maxImageCount);
419         uint32_t targetCount = MinImageCountForPresentMode(config.presentMode);
420 
421         targetCount = std::max(targetCount, surfaceInfo.capabilities.minImageCount);
422         if (surfaceInfo.capabilities.maxImageCount != 0) {
423             targetCount = std::min(targetCount, surfaceInfo.capabilities.maxImageCount);
424         }
425 
426         config.targetImageCount = targetCount;
427 
428         // Choose a valid config for the swapchain texture that will receive the blit.
429         if (config.needsBlit) {
430             // Vulkan has provisions to have surfaces that adapt to the swapchain size. If that's
431             // the case it is very likely that the target extent works, but clamp it just in case.
432             // Using the target extent for the blit is better when possible so that texels don't
433             // get stretched. This case is exposed by having the special "-1" value in both
434             // dimensions of the extent.
435             constexpr uint32_t kSpecialValue = 0xFFFF'FFFF;
436             if (surfaceInfo.capabilities.currentExtent.width == kSpecialValue &&
437                 surfaceInfo.capabilities.currentExtent.height == kSpecialValue) {
438                 // extent = clamp(targetExtent, minExtent, maxExtent)
439                 config.extent.width = GetWidth();
440                 config.extent.width =
441                     std::min(config.extent.width, surfaceInfo.capabilities.maxImageExtent.width);
442                 config.extent.width =
443                     std::max(config.extent.width, surfaceInfo.capabilities.minImageExtent.width);
444 
445                 config.extent.height = GetHeight();
446                 config.extent.height =
447                     std::min(config.extent.height, surfaceInfo.capabilities.maxImageExtent.height);
448                 config.extent.height =
449                     std::max(config.extent.height, surfaceInfo.capabilities.minImageExtent.height);
450             } else {
451                 // If it is not an adaptable swapchain, just use the current extent for the blit
452                 // texture.
453                 config.extent = surfaceInfo.capabilities.currentExtent;
454             }
455 
456             // TODO(crbug.com/dawn/269): If the swapchain image doesn't support TRANSFER_DST
457             // then we'll need to have a second fallback that uses a blit shader :(
458             if ((supportedUsages & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
459                 return DAWN_INTERNAL_ERROR(
460                     "SwapChain cannot fallback to a blit because of a missing "
461                     "VK_IMAGE_USAGE_TRANSFER_DST_BIT");
462             }
463             config.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
464             config.wgpuUsage = wgpu::TextureUsage::CopyDst;
465         }
466 
467         return config;
468     }
469 
PresentImpl()470     MaybeError SwapChain::PresentImpl() {
471         Device* device = ToBackend(GetDevice());
472 
473         CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
474 
475         if (mConfig.needsBlit) {
476             // TODO ditto same as present below: eagerly transition the blit texture to CopySrc.
477             mBlitTexture->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc,
478                                              mBlitTexture->GetAllSubresources());
479             mTexture->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst,
480                                          mTexture->GetAllSubresources());
481 
482             VkImageBlit region;
483             region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
484             region.srcSubresource.mipLevel = 0;
485             region.srcSubresource.baseArrayLayer = 0;
486             region.srcSubresource.layerCount = 1;
487             region.srcOffsets[0] = {0, 0, 0};
488             region.srcOffsets[1] = {static_cast<int32_t>(mBlitTexture->GetWidth()),
489                                     static_cast<int32_t>(mBlitTexture->GetHeight()), 1};
490 
491             region.dstSubresource = region.srcSubresource;
492             region.dstOffsets[0] = {0, 0, 0};
493             region.dstOffsets[1] = {static_cast<int32_t>(mTexture->GetWidth()),
494                                     static_cast<int32_t>(mTexture->GetHeight()), 1};
495 
496             device->fn.CmdBlitImage(recordingContext->commandBuffer, mBlitTexture->GetHandle(),
497                                     mBlitTexture->GetCurrentLayoutForSwapChain(),
498                                     mTexture->GetHandle(), mTexture->GetCurrentLayoutForSwapChain(),
499                                     1, &region, VK_FILTER_LINEAR);
500 
501             // TODO(crbug.com/dawn/269): Find a way to reuse the blit texture between frames
502             // instead of creating a new one every time. This will involve "un-destroying" the
503             // texture or making the blit texture "external".
504             mBlitTexture->APIDestroy();
505             mBlitTexture = nullptr;
506         }
507 
508         // TODO(crbug.com/dawn/269): Remove the need for this by eagerly transitioning the
509         // presentable texture to present at the end of submits that use them and ideally even
510         // folding that in the free layout transition at the end of render passes.
511         mTexture->TransitionUsageNow(recordingContext, kPresentTextureUsage,
512                                      mTexture->GetAllSubresources());
513 
514         DAWN_TRY(device->SubmitPendingCommands());
515 
516         // Assuming that the present queue is the same as the graphics queue, the proper
517         // synchronization has already been done on the queue so we don't need to wait on any
518         // semaphores.
519         // TODO(crbug.com/dawn/269): Support the present queue not being the main queue.
520         VkPresentInfoKHR presentInfo;
521         presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
522         presentInfo.pNext = nullptr;
523         presentInfo.waitSemaphoreCount = 0;
524         presentInfo.pWaitSemaphores = nullptr;
525         presentInfo.swapchainCount = 1;
526         presentInfo.pSwapchains = &*mSwapChain;
527         presentInfo.pImageIndices = &mLastImageIndex;
528         presentInfo.pResults = nullptr;
529 
530         // Free the texture before present so error handling doesn't skip that step.
531         mTexture->APIDestroy();
532         mTexture = nullptr;
533 
534         VkResult result =
535             VkResult::WrapUnsafe(device->fn.QueuePresentKHR(device->GetQueue(), &presentInfo));
536 
537         switch (result) {
538             case VK_SUCCESS:
539             // VK_SUBOPTIMAL_KHR means "a swapchain no longer matches the surface properties
540             // exactly, but can still be used to present to the surface successfully", so we
541             // can also treat it as a "success" error code of vkQueuePresentKHR().
542             case VK_SUBOPTIMAL_KHR:
543                 return {};
544 
545             // This present cannot be recovered. Re-initialize the VkSwapchain so that future
546             // presents work..
547             case VK_ERROR_OUT_OF_DATE_KHR:
548                 return Initialize(this);
549 
550             // TODO(crbug.com/dawn/269): Allow losing the surface at Dawn's API level?
551             case VK_ERROR_SURFACE_LOST_KHR:
552             default:
553                 return CheckVkSuccess(::VkResult(result), "QueuePresent");
554         }
555     }
556 
GetCurrentTextureViewImpl()557     ResultOrError<TextureViewBase*> SwapChain::GetCurrentTextureViewImpl() {
558         return GetCurrentTextureViewInternal();
559     }
560 
GetCurrentTextureViewInternal(bool isReentrant)561     ResultOrError<TextureViewBase*> SwapChain::GetCurrentTextureViewInternal(bool isReentrant) {
562         Device* device = ToBackend(GetDevice());
563 
564         // Transiently create a semaphore that will be signaled when the presentation engine is done
565         // with the swapchain image. Further operations on the image will wait for this semaphore.
566         VkSemaphoreCreateInfo createInfo;
567         createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
568         createInfo.pNext = nullptr;
569         createInfo.flags = 0;
570 
571         VkSemaphore semaphore = VK_NULL_HANDLE;
572         DAWN_TRY(CheckVkSuccess(
573             device->fn.CreateSemaphore(device->GetVkDevice(), &createInfo, nullptr, &*semaphore),
574             "CreateSemaphore"));
575 
576         VkResult result = VkResult::WrapUnsafe(device->fn.AcquireNextImageKHR(
577             device->GetVkDevice(), mSwapChain, std::numeric_limits<uint64_t>::max(), semaphore,
578             VkFence{}, &mLastImageIndex));
579 
580         if (result == VK_SUCCESS) {
581             // TODO(crbug.com/dawn/269) put the semaphore on the texture so it is waited on when
582             // used instead of directly on the recording context?
583             device->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore);
584         } else {
585             // The semaphore wasn't actually used (? this is unclear in the spec). Delete it when
586             // we get a chance.
587             ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(semaphore);
588         }
589 
590         switch (result) {
591             // TODO(crbug.com/dawn/269): Introduce a mechanism to notify the application that
592             // the swapchain is in a suboptimal state?
593             case VK_SUBOPTIMAL_KHR:
594             case VK_SUCCESS:
595                 break;
596 
597             case VK_ERROR_OUT_OF_DATE_KHR: {
598                 // Prevent infinite recursive calls to GetCurrentTextureViewInternal when the
599                 // swapchains always return that they are out of date.
600                 if (isReentrant) {
601                     // TODO(crbug.com/dawn/269): Allow losing the surface instead?
602                     return DAWN_INTERNAL_ERROR(
603                         "Wasn't able to recuperate the surface after a VK_ERROR_OUT_OF_DATE_KHR");
604                 }
605 
606                 // Re-initialize the VkSwapchain and try getting the texture again.
607                 DAWN_TRY(Initialize(this));
608                 return GetCurrentTextureViewInternal(true);
609             }
610 
611             // TODO(crbug.com/dawn/269): Allow losing the surface at Dawn's API level?
612             case VK_ERROR_SURFACE_LOST_KHR:
613             default:
614                 DAWN_TRY(CheckVkSuccess(::VkResult(result), "AcquireNextImage"));
615         }
616 
617         TextureDescriptor textureDesc;
618         textureDesc.size.width = mConfig.extent.width;
619         textureDesc.size.height = mConfig.extent.height;
620         textureDesc.format = mConfig.wgpuFormat;
621         textureDesc.usage = mConfig.wgpuUsage;
622 
623         VkImage currentImage = mSwapChainImages[mLastImageIndex];
624         mTexture = Texture::CreateForSwapChain(device, &textureDesc, currentImage);
625 
626         // In the happy path we can use the swapchain image directly.
627         if (!mConfig.needsBlit) {
628             // TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
629             return mTexture->APICreateView();
630         }
631 
632         // The blit texture always perfectly matches what the user requested for the swapchain.
633         // We need to add the Vulkan TRANSFER_SRC flag for the vkCmdBlitImage call.
634         TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
635         DAWN_TRY_ASSIGN(mBlitTexture,
636                         Texture::Create(device, &desc, VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
637         // TODO(dawn:723): change to not use AcquireRef for reentrant object creation.
638         return mBlitTexture->APICreateView();
639     }
640 
DetachFromSurfaceImpl()641     void SwapChain::DetachFromSurfaceImpl() {
642         if (mTexture != nullptr) {
643             mTexture->APIDestroy();
644             mTexture = nullptr;
645         }
646 
647         if (mBlitTexture != nullptr) {
648             mBlitTexture->APIDestroy();
649             mBlitTexture = nullptr;
650         }
651 
652         // The swapchain images are destroyed with the swapchain.
653         if (mSwapChain != VK_NULL_HANDLE) {
654             ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mSwapChain);
655             mSwapChain = VK_NULL_HANDLE;
656         }
657 
658         if (mVkSurface != VK_NULL_HANDLE) {
659             ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mVkSurface);
660             mVkSurface = VK_NULL_HANDLE;
661         }
662     }
663 
664 }}  // namespace dawn_native::vulkan
665