LCOV - code coverage report
Current view: top level - xenolith/backend/vkgui - XLVkSwapchain.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 213 277 76.9 %
Date: 2024-05-12 00:16:13 Functions: 27 34 79.4 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "XLVkSwapchain.h"
      25             : #include "XLVkGuiConfig.h"
      26             : 
      27             : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk {
      28             : 
      29          20 : Surface::~Surface() {
      30          10 :         if (_surface) {
      31          10 :                 _instance->vkDestroySurfaceKHR(_instance->getInstance(), _surface, nullptr);
      32          10 :                 _surface = VK_NULL_HANDLE;
      33             :         }
      34          10 :         _window = nullptr;
      35          20 : }
      36             : 
      37          10 : bool Surface::init(Instance *instance, VkSurfaceKHR surface, Ref *win) {
      38          10 :         if (!surface) {
      39           0 :                 return false;
      40             :         }
      41          10 :         _instance = instance;
      42          10 :         _surface = surface;
      43          10 :         _window = win;
      44          10 :         return true;
      45             : }
      46             : 
      47          40 : SwapchainHandle::~SwapchainHandle() {
      48          70 :         for (auto &it : _images) {
      49         100 :                 for (auto &v : it.views) {
      50          50 :                         if (v.second) {
      51          50 :                                 v.second->runReleaseCallback();
      52          50 :                                 v.second->invalidate();
      53          50 :                                 v.second = nullptr;
      54             :                         }
      55             :                 }
      56             :         }
      57          20 :         invalidate();
      58          40 : }
      59             : 
      60          20 : bool SwapchainHandle::init(Device &dev, const core::SurfaceInfo &info, const core::SwapchainConfig &cfg, ImageInfo &&swapchainImageInfo,
      61             :                 core::PresentMode presentMode, Surface *surface, uint32_t families[2], SwapchainHandle *old) {
      62             : 
      63          20 :         _device = &dev;
      64             : 
      65          20 :         VkSwapchainCreateInfoKHR swapChainCreateInfo{}; sanitizeVkStruct(swapChainCreateInfo);
      66          20 :         swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
      67          20 :         swapChainCreateInfo.surface = surface->getSurface();
      68          20 :         swapChainCreateInfo.minImageCount = cfg.imageCount;
      69          20 :         swapChainCreateInfo.imageFormat = VkFormat(swapchainImageInfo.format);
      70          20 :         swapChainCreateInfo.imageColorSpace = VkColorSpaceKHR(cfg.colorSpace);
      71          20 :         swapChainCreateInfo.imageExtent = VkExtent2D({swapchainImageInfo.extent.width, swapchainImageInfo.extent.height});
      72          20 :         swapChainCreateInfo.imageArrayLayers = swapchainImageInfo.arrayLayers.get();
      73          20 :         swapChainCreateInfo.imageUsage = VkImageUsageFlags(swapchainImageInfo.usage);
      74             : 
      75          20 :         if (families[0] != families[1]) {
      76           0 :                 swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
      77           0 :                 swapChainCreateInfo.queueFamilyIndexCount = 2;
      78           0 :                 swapChainCreateInfo.pQueueFamilyIndices = families;
      79             :         } else {
      80          20 :                 swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
      81             :         }
      82             : 
      83          20 :         if ((cfg.transform & core::SurfaceTransformFlags::PreRotated) != core::SurfaceTransformFlags::None) {
      84           0 :                 swapChainCreateInfo.preTransform = VkSurfaceTransformFlagBitsKHR(core::getPureTransform(cfg.transform));
      85             :         } else {
      86          20 :                 swapChainCreateInfo.preTransform = VkSurfaceTransformFlagBitsKHR(cfg.transform);
      87             :         }
      88          20 :         swapChainCreateInfo.compositeAlpha = VkCompositeAlphaFlagBitsKHR(cfg.alpha);
      89          20 :         swapChainCreateInfo.presentMode = getVkPresentMode(presentMode);
      90          20 :         swapChainCreateInfo.clipped = (cfg.clipped ? VK_TRUE : VK_FALSE);
      91             : 
      92          20 :         if (old) {
      93          10 :                 swapChainCreateInfo.oldSwapchain = old->getSwapchain();
      94             :         } else {
      95          10 :                 swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
      96             :         }
      97             : 
      98          20 :         VkResult result = VK_ERROR_UNKNOWN;
      99          20 :         dev.makeApiCall([&, this] (const DeviceTable &table, VkDevice device) {
     100             : #if XL_VKAPI_DEBUG
     101             :                 auto t = xenolith::platform::clock(core::ClockType::Monotonic);
     102             :                 result = table.vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &_swapchain);
     103             :                 XL_VKAPI_LOG("vkCreateSwapchainKHR: ", result,
     104             :                                 " [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
     105             : #else
     106          20 :                 result = table.vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &_swapchain);
     107             : #endif
     108          20 :         });
     109          20 :         if (result == VK_SUCCESS) {
     110          20 :                 if (old) {
     111          10 :                         std::unique_lock<Mutex> lock(_resourceMutex);
     112          10 :                         std::unique_lock<Mutex> lock2(old->_resourceMutex);
     113          10 :                         _semaphores = move(old->_semaphores);
     114          30 :                         for (auto &it : old->_presentSemaphores) {
     115          20 :                                 releaseSemaphore(move(it));
     116             :                         }
     117          10 :                 }
     118             : 
     119          20 :                 Vector<VkImage> swapchainImages;
     120             : 
     121          20 :                 uint32_t imageCount = 0;
     122          20 :                 dev.getTable()->vkGetSwapchainImagesKHR(dev.getDevice(), _swapchain, &imageCount, nullptr);
     123          20 :                 swapchainImages.resize(imageCount);
     124          20 :                 dev.getTable()->vkGetSwapchainImagesKHR(dev.getDevice(), _swapchain, &imageCount, swapchainImages.data());
     125             : 
     126          20 :                 _images.reserve(imageCount);
     127          20 :                 _presentSemaphores.resize(imageCount);
     128             : 
     129          20 :                 auto swapchainImageViewInfo = getSwapchainImageViewInfo(swapchainImageInfo);
     130             : 
     131          70 :                 for (auto &it : swapchainImages) {
     132          50 :                         auto image = Rc<Image>::create(dev, it, swapchainImageInfo, _images.size());
     133             : 
     134          50 :                         Map<ImageViewInfo, Rc<ImageView>> views;
     135          50 :                         views.emplace(swapchainImageViewInfo, Rc<ImageView>::create(dev, image.get(), swapchainImageViewInfo));
     136             : 
     137          50 :                         _images.emplace_back(SwapchainImageData{move(image), move(views)});
     138          50 :                 }
     139             : 
     140          20 :                 _rebuildMode = _presentMode = presentMode;
     141          20 :                 _imageInfo = move(swapchainImageInfo);
     142          20 :                 _config = move(cfg);
     143          20 :                 _config.imageCount = imageCount;
     144          20 :                 _surface = surface;
     145          20 :                 _surfaceInfo = info;
     146             : 
     147          20 :                 return core::Object::init(dev, [] (core::Device *dev, core::ObjectType, ObjectHandle ptr, void *) {
     148          20 :                         auto d = ((Device *)dev);
     149          20 :                         d->makeApiCall([&] (const DeviceTable &table, VkDevice device) {
     150             : #if XL_VKAPI_DEBUG
     151             :                                 auto t = xenolith::platform::clock(core::ClockType::Monotonic);
     152             :                                 table.vkDestroySwapchainKHR(device, (VkSwapchainKHR)ptr.get(), nullptr);
     153             :                                 XL_VKAPI_LOG("vkDestroySwapchainKHR: [",xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
     154             : #else
     155          20 :                                 table.vkDestroySwapchainKHR(device, (VkSwapchainKHR)ptr.get(), nullptr);
     156             : #endif
     157          20 :                         });
     158          60 :                 }, core::ObjectType::Swapchain, ObjectHandle(_swapchain));
     159          20 :         }
     160           0 :         return false;
     161             : }
     162             : 
     163        4630 : bool SwapchainHandle::isDeprecated() {
     164        4630 :         return _deprecated;
     165             : }
     166             : 
     167           0 : bool SwapchainHandle::isOptimal() const {
     168           0 :         return _presentMode == _config.presentMode;
     169             : }
     170             : 
     171          20 : bool SwapchainHandle::deprecate(bool fast) {
     172          20 :         auto tmp = _deprecated;
     173          20 :         _deprecated = true;
     174          20 :         if (fast && _config.presentModeFast != core::PresentMode::Unsupported) {
     175           0 :                 _rebuildMode = _config.presentModeFast;
     176             :         }
     177          20 :         return !tmp;
     178             : }
     179             : 
     180        4650 : auto SwapchainHandle::acquire(bool lockfree, const Rc<Fence> &fence) -> Rc<SwapchainAcquiredImage> {
     181        4650 :         if (_deprecated) {
     182           0 :                 return nullptr;
     183             :         }
     184             : 
     185        4650 :         uint64_t timeout = lockfree ? 0 : maxOf<uint64_t>();
     186        4650 :         Rc<Semaphore> sem = acquireSemaphore();
     187        4650 :         uint32_t imageIndex = maxOf<uint32_t>();
     188        4650 :         VkResult ret = VK_ERROR_UNKNOWN;
     189        4650 :         _device->makeApiCall([&, this] (const DeviceTable &table, VkDevice device) {
     190             : #if XL_VKAPI_DEBUG
     191             :                 auto t = xenolith::platform::clock(core::ClockType::Monotonic);
     192             :                 ret = table.vkAcquireNextImageKHR(device, _swapchain, timeout,
     193             :                                 sem ? sem->getSemaphore() : VK_NULL_HANDLE, fence->getFence(), &imageIndex);
     194             :                 XL_VKAPI_LOG("vkAcquireNextImageKHR: ", imageIndex, " ", ret,
     195             :                                 " [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
     196             : #else
     197       13950 :                 ret = table.vkAcquireNextImageKHR(device, _swapchain, timeout,
     198       13950 :                                 sem ? sem->getSemaphore() : VK_NULL_HANDLE, fence ? fence->getFence() : VK_NULL_HANDLE, &imageIndex);
     199             : #endif
     200        4650 :         });
     201             : 
     202        4650 :         Rc<SwapchainAcquiredImage> image;
     203        4650 :         switch (ret) {
     204        4650 :         case VK_SUCCESS:
     205        4650 :                 if (sem) {
     206        4650 :                         sem->setSignaled(true);
     207             :                 }
     208        4650 :                 if (fence) {
     209        4650 :                         fence->setTag("SwapchainHandle::acquire");
     210        4650 :                         fence->setArmed();
     211             :                 }
     212        4650 :                 ++ _acquiredImages;
     213        9300 :                 return Rc<SwapchainAcquiredImage>::alloc(imageIndex, &_images.at(imageIndex), move(sem), this);
     214             :                 break;
     215           0 :         case VK_SUBOPTIMAL_KHR:
     216           0 :                 if (sem) {
     217           0 :                         sem->setSignaled(true);
     218             :                 }
     219           0 :                 if (fence) {
     220           0 :                         fence->setTag("SwapchainHandle::acquire");
     221           0 :                         fence->setArmed();
     222             :                 }
     223           0 :                 _deprecated = true;
     224           0 :                 ++ _acquiredImages;
     225           0 :                 return Rc<SwapchainAcquiredImage>::alloc(imageIndex, &_images.at(imageIndex), move(sem), this);
     226             :                 break;
     227           0 :         default:
     228           0 :                 releaseSemaphore(move(sem));
     229           0 :                 break;
     230             :         }
     231             : 
     232           0 :         return nullptr;
     233        4650 : }
     234             : 
     235        4640 : VkResult SwapchainHandle::present(DeviceQueue &queue, const Rc<ImageStorage> &image) {
     236        4640 :         auto waitSem = ((Semaphore *)image->getSignalSem().get())->getSemaphore();
     237        4640 :         auto imageIndex = image->getImageIndex();
     238             : 
     239        4640 :         VkPresentInfoKHR presentInfo{}; sanitizeVkStruct(presentInfo);
     240        4640 :         presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
     241             : 
     242        4640 :         presentInfo.waitSemaphoreCount = 1;
     243        4640 :         presentInfo.pWaitSemaphores = &waitSem;
     244             : 
     245        4640 :         presentInfo.swapchainCount = 1;
     246        4640 :         presentInfo.pSwapchains = &_swapchain;
     247        4640 :         presentInfo.pImageIndices = &imageIndex;
     248        4640 :         presentInfo.pResults = nullptr; // Optional
     249             : 
     250        4640 :         VkResult result = VK_ERROR_UNKNOWN;
     251        4640 :         _device->makeApiCall([&] (const DeviceTable &table, VkDevice device) {
     252             : #if XL_VKAPI_DEBUG
     253             :                 auto t = xenolith::platform::clock(core::ClockType::Monotonic);
     254             :                 result = table.vkQueuePresentKHR(queue.getQueue(), &presentInfo);
     255             :                 XL_VKAPI_LOG("[", image->getFrameIndex(), "] vkQueuePresentKHR: ", imageIndex, " ", result,
     256             :                                 " [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "] [timeout: ", t - _presentTime,
     257             :                                 "] [acquisition: ", t - image->getAcquisitionTime(), "]");
     258             :                 _presentTime = t;
     259             : #else
     260        4640 :                 result = table.vkQueuePresentKHR(queue.getQueue(), &presentInfo);
     261             : #endif
     262        4640 :         });
     263             : 
     264             :         do {
     265        4640 :                 std::unique_lock<Mutex> lock(_resourceMutex);
     266        4640 :                 ((SwapchainImage *)image.get())->setPresented();
     267        4640 :                 -- _acquiredImages;
     268        4640 :         } while (0);
     269             : 
     270        4640 :         if (result == VK_SUCCESS) {
     271        4640 :                 if (_presentSemaphores[imageIndex]) {
     272        4590 :                         _presentSemaphores[imageIndex]->setWaited(true);
     273        4590 :                         releaseSemaphore(move(_presentSemaphores[imageIndex]));
     274             :                 }
     275        4640 :                 _presentSemaphores[imageIndex] = (Semaphore *)image->getSignalSem().get();
     276             : 
     277        4640 :                 ++ _presentedFrames;
     278        4640 :                 if (_presentedFrames == config::MaxSuboptimalFrames && _presentMode == _config.presentModeFast
     279           0 :                                 && _config.presentModeFast != _config.presentMode) {
     280           0 :                         result = VK_SUBOPTIMAL_KHR;
     281           0 :                         _rebuildMode = _config.presentMode;
     282             :                 }
     283             :         }
     284             : 
     285        4640 :         return result;
     286             : }
     287             : 
     288          10 : void SwapchainHandle::invalidateImage(const ImageStorage *image) {
     289          10 :         if (!((SwapchainImage *)image)->isPresented()) {
     290          10 :                 std::unique_lock<Mutex> lock(_resourceMutex);
     291          10 :                 -- _acquiredImages;
     292          10 :         }
     293          10 : }
     294             : 
     295        9300 : Rc<Semaphore> SwapchainHandle::acquireSemaphore() {
     296        9300 :         std::unique_lock<Mutex> lock(_resourceMutex);
     297        9300 :         if (!_semaphores.empty()) {
     298        5209 :                 auto sem = _semaphores.back();
     299        5209 :                 _semaphores.pop_back();
     300        5209 :                 return sem;
     301        5209 :         }
     302        4091 :         lock.unlock();
     303             : 
     304        4091 :         return Rc<Semaphore>::create(*_device);
     305        9300 : }
     306             : 
     307        5229 : void SwapchainHandle::releaseSemaphore(Rc<Semaphore> &&sem) {
     308        5229 :         if (sem && sem->reset()) {
     309        5209 :                 std::unique_lock<Mutex> lock(_resourceMutex);
     310        5209 :                 _semaphores.emplace_back(move(sem));
     311        5209 :         }
     312        5229 : }
     313             : 
     314           0 : Rc<core::ImageView> SwapchainHandle::makeView(const Rc<core::ImageObject> &image, const ImageViewInfo &viewInfo) {
     315           0 :         auto img = (Image *)image.get();
     316           0 :         auto idx = img->getIndex();
     317             : 
     318           0 :         auto it = _images[idx].views.find(viewInfo);
     319           0 :         if (it != _images[idx].views.end()) {
     320           0 :                 return it->second;
     321             :         }
     322             : 
     323           0 :         it = _images[idx].views.emplace(viewInfo, Rc<ImageView>::create(*_device, img, viewInfo)).first;
     324           0 :         return it->second;
     325             : }
     326             : 
     327          20 : ImageViewInfo SwapchainHandle::getSwapchainImageViewInfo(const ImageInfo &image) const {
     328          20 :         ImageViewInfo info;
     329          20 :         switch (image.imageType) {
     330           0 :         case core::ImageType::Image1D:
     331           0 :                 info.type = core::ImageViewType::ImageView1D;
     332           0 :                 break;
     333          20 :         case core::ImageType::Image2D:
     334          20 :                 info.type = core::ImageViewType::ImageView2D;
     335          20 :                 break;
     336           0 :         case core::ImageType::Image3D:
     337           0 :                 info.type = core::ImageViewType::ImageView3D;
     338           0 :                 break;
     339             :         }
     340             : 
     341          40 :         return image.getViewInfo(info);
     342             : }
     343             : 
     344             : 
     345        9300 : SwapchainImage::~SwapchainImage() {
     346        4650 :         if (_state != State::Presented) {
     347          10 :                 if (_image) {
     348          10 :                         _swapchain->invalidateImage(this);
     349             :                 }
     350          10 :                 _image = nullptr;
     351          10 :                 _swapchain = nullptr;
     352          10 :                 _state = State::Presented;
     353             :         }
     354             :         // prevent views from released
     355        4650 :         _views.clear();
     356        9300 : }
     357             : 
     358        4640 : bool SwapchainImage::init(Rc<SwapchainHandle> &&swapchain, uint64_t order, uint64_t presentWindow) {
     359        4640 :         _swapchain = move(swapchain);
     360        4640 :         _order = order;
     361        4640 :         _presentWindow = presentWindow;
     362        4640 :         _state = State::Submitted;
     363        4640 :         _isSwapchainImage = true;
     364        4640 :         return true;
     365             : }
     366             : 
     367          10 : bool SwapchainImage::init(Rc<SwapchainHandle> &&swapchain, const SwapchainHandle::SwapchainImageData &image, Rc<Semaphore> &&sem) {
     368          10 :         _swapchain = move(swapchain);
     369          10 :         _image = image.image.get();
     370          20 :         for (auto &it : image.views) {
     371          10 :                 _views.emplace(it.first, it.second);
     372             :         }
     373          10 :         if (sem) {
     374          10 :                 _waitSem = sem.get();
     375             :         }
     376          10 :         _signalSem = _swapchain->acquireSemaphore().get();
     377          10 :         _state = State::Submitted;
     378          10 :         _isSwapchainImage = true;
     379          10 :         return true;
     380             : }
     381             : 
     382           0 : void SwapchainImage::cleanup() {
     383           0 :         stappler::log::info("SwapchainImage", "cleanup");
     384           0 : }
     385             : 
     386           0 : void SwapchainImage::rearmSemaphores(core::Loop &loop) {
     387           0 :         ImageStorage::rearmSemaphores(loop);
     388           0 : }
     389             : 
     390        4640 : void SwapchainImage::releaseSemaphore(core::Semaphore *sem) {
     391        4640 :         if (_state == State::Presented && sem == _waitSem && _swapchain) {
     392             :                 // work on last submit is over, wait sem no longer in use
     393         619 :                 _swapchain->releaseSemaphore((Semaphore *)sem);
     394         619 :                 _waitSem = nullptr;
     395             :         }
     396        4640 : }
     397             : 
     398       18540 : ImageInfoData SwapchainImage::getInfo() const {
     399       18540 :         if (_image) {
     400        7493 :                 return _image->getInfo();
     401       11047 :         } else if (_swapchain) {
     402       11047 :                 return _swapchain->getImageInfo();
     403             :         }
     404           0 :     return ImageInfoData();
     405             : }
     406             : 
     407           0 : Rc<core::ImageView> SwapchainImage::makeView(const ImageViewInfo &info) {
     408           0 :         auto it = _views.find(info);
     409           0 :         if (it != _views.end()) {
     410           0 :                 return it->second;
     411             :         }
     412             : 
     413           0 :         it = _views.emplace(info, _swapchain->makeView(_image, info)).first;
     414           0 :         return it->second;
     415             : }
     416             : 
     417        4640 : void SwapchainImage::setImage(Rc<SwapchainHandle> &&handle, const SwapchainHandle::SwapchainImageData &image, const Rc<Semaphore> &sem) {
     418        4640 :         _image = image.image.get();
     419        9280 :         for (auto &it : image.views) {
     420        4640 :                 _views.emplace(it.first, it.second);
     421             :         }
     422        4640 :         if (sem) {
     423        4640 :                 _waitSem = sem.get();
     424             :         }
     425        4640 :         _signalSem = _swapchain->acquireSemaphore().get();
     426        4640 : }
     427             : 
     428        4640 : void SwapchainImage::setPresented() {
     429        4640 :         _state = State::Presented;
     430        4640 : }
     431             : 
     432           0 : void SwapchainImage::invalidateImage() {
     433           0 :         if (_image) {
     434           0 :                 _swapchain->invalidateImage(this);
     435             :         }
     436           0 :         _swapchain = nullptr;
     437           0 :         _state = State::Presented;
     438           0 : }
     439             : 
     440           0 : void SwapchainImage::invalidateSwapchain() {
     441           0 :         _swapchain = nullptr;
     442           0 :         _image = nullptr;
     443           0 :         _state = State::Presented;
     444           0 : }
     445             : 
     446             : }
     447             : 

Generated by: LCOV version 1.14