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, ®ion, 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