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 :
|