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 "XLVkView.h"
25 : #include "XLVkLoop.h"
26 : #include "XLVkDevice.h"
27 : #include "XLVkTextureSet.h"
28 : #include "XLVkSwapchain.h"
29 : #include "XLVkGuiConfig.h"
30 : #include "XLDirector.h"
31 : #include "XLCoreImageStorage.h"
32 : #include "XLCoreFrameQueue.h"
33 : #include "XLCoreFrameRequest.h"
34 : #include "XLCoreFrameCache.h"
35 : #include "SPBitmap.h"
36 :
37 : #define XL_VKVIEW_DEBUG 0
38 :
39 : #ifndef XL_VKAPI_LOG
40 : #define XL_VKAPI_LOG(...)
41 : #endif
42 :
43 : #if XL_VKVIEW_DEBUG
44 : #define XL_VKVIEW_LOG(...) log::debug("vk::View", __VA_ARGS__)
45 : #else
46 : #define XL_VKVIEW_LOG(...)
47 : #endif
48 :
49 : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk {
50 :
51 10 : View::~View() {
52 :
53 10 : }
54 :
55 10 : bool View::init(Application &loop, const Device &dev, ViewInfo &&info) {
56 10 : if (!xenolith::View::init(loop, move(info))) {
57 0 : return false;
58 : }
59 :
60 10 : _threadName = toString("View:", info.name);
61 10 : _instance = static_cast<Instance *>(loop.getGlLoop()->getGlInstance().get());
62 10 : _device = const_cast<Device *>(static_cast<const Device *>(&dev));
63 10 : _director = Rc<Director>::create(_mainLoop, _constraints, this);
64 10 : if (_info.onCreated) {
65 10 : _mainLoop->performOnMainThread([this, c = _constraints] {
66 10 : _info.onCreated(*this, c);
67 10 : }, this);
68 : } else {
69 0 : run();
70 : }
71 10 : return true;
72 : }
73 :
74 10 : void View::threadInit() {
75 10 : _init = true;
76 10 : _running = true;
77 10 : _avgFrameInterval.reset(0);
78 :
79 10 : _refId = retain();
80 10 : thread::ThreadInfo::setThreadInfo(_threadName);
81 10 : _threadId = std::this_thread::get_id();
82 10 : _shouldQuit.test_and_set();
83 :
84 10 : auto info = getSurfaceOptions();
85 10 : auto cfg = _info.selectConfig(*this, info);
86 :
87 10 : if (info.surfaceDensity != 1.0f) {
88 0 : _constraints.density = _info.density * info.surfaceDensity;
89 : }
90 :
91 10 : createSwapchain(info, move(cfg), cfg.presentMode);
92 :
93 10 : if (_initImage && !_options.followDisplayLink) {
94 10 : presentImmediate(move(_initImage), nullptr);
95 10 : _initImage = nullptr;
96 : }
97 :
98 10 : mapWindow();
99 10 : }
100 :
101 10 : void View::threadDispose() {
102 10 : clearImages();
103 10 : _running = false;
104 :
105 10 : if (_options.renderImageOffscreen) {
106 : // offscreen does not need swapchain outside of view thread
107 0 : _swapchain->invalidate();
108 : }
109 10 : _swapchain = nullptr;
110 10 : _surface = nullptr;
111 :
112 10 : finalize();
113 :
114 10 : if (_threadStarted) {
115 10 : _thread.detach();
116 10 : _threadStarted = false;
117 : }
118 :
119 : #if SP_REF_DEBUG
120 : auto refcount = getReferenceCount();
121 : release(_refId);
122 : if (refcount > 1) {
123 : this->foreachBacktrace([] (uint64_t id, Time time, const std::vector<std::string> &vec) {
124 : StringStream stream;
125 : stream << "[" << id << ":" << time.toHttp<Interface>() << "]:\n";
126 : for (auto &it : vec) {
127 : stream << "\t" << it << "\n";
128 : }
129 : log::debug("vk::View", stream.str());
130 : });
131 : } else {
132 : _glLoop = nullptr;
133 : }
134 : #else
135 10 : release(_refId);
136 : #endif
137 10 : }
138 :
139 217166 : void View::update(bool displayLink) {
140 217166 : xenolith::View::update(displayLink);
141 :
142 217166 : updateFences();
143 :
144 217166 : if (displayLink && _options.followDisplayLink) {
145 : // ignore present windows
146 0 : for (auto &it : _scheduledPresent) {
147 0 : runScheduledPresent(move(it));
148 : }
149 0 : _scheduledPresent.clear();
150 : }
151 :
152 : do {
153 217166 : auto it = _fenceImages.begin();
154 217166 : while (it != _fenceImages.end()) {
155 0 : if (_fenceOrder < (*it)->getOrder()) {
156 0 : _scheduledImages.emplace_back(move(*it));
157 0 : it = _fenceImages.erase(it);
158 : } else {
159 0 : ++ it;
160 : }
161 : }
162 : } while (0);
163 :
164 217166 : acquireScheduledImage();
165 :
166 217166 : auto clock = xenolith::platform::clock(core::ClockType::Monotonic);
167 :
168 217166 : if (!_options.followDisplayLink) {
169 217166 : auto it = _scheduledPresent.begin();
170 388290 : while (it != _scheduledPresent.end()) {
171 171124 : if (!(*it)->getPresentWindow() || (*it)->getPresentWindow() < clock) {
172 4019 : runScheduledPresent(move(*it));
173 4019 : it = _scheduledPresent.erase(it);
174 : } else {
175 167105 : ++ it;
176 : }
177 : }
178 : }
179 :
180 217166 : if (_swapchain && !_swapchainInvalidated && _scheduledTime < clock && _options.renderOnDemand) {
181 206835 : auto acquiredImages = _swapchain->getAcquiredImagesCount();
182 206835 : if (_swapchain && _framesInProgress == 0 && acquiredImages == 0) {
183 : XL_VKVIEW_LOG("update - scheduleNextImage");
184 0 : scheduleNextImage(0, true);
185 : } else {
186 : //log::verbose("vk::View", "Frame dropped: ", acquiredImages, " ", _framesInProgress);
187 : }
188 : }
189 217166 : }
190 :
191 10 : void View::close() {
192 10 : xenolith::View::close();
193 10 : }
194 :
195 10 : void View::run() {
196 10 : auto refId = retain();
197 10 : _threadStarted = true;
198 10 : _thread = std::thread(View::workerThread, this, nullptr);
199 10 : performOnThread([this, refId] {
200 10 : release(refId);
201 10 : }, this);
202 10 : }
203 :
204 10 : void View::runWithQueue(const Rc<RenderQueue> &queue) {
205 : XL_VKVIEW_LOG("runWithQueue");
206 10 : auto a = queue->getPresentImageOutput();
207 10 : if (!a) {
208 0 : a = queue->getTransferImageOutput();
209 : }
210 10 : if (!a) {
211 0 : log::error("vk::View", "Fail to run view with queue '", queue->getName(), "': no usable output attachments found");
212 0 : return;
213 : }
214 :
215 10 : log::verbose("View", "View::runWithQueue");
216 :
217 10 : auto req = Rc<FrameRequest>::create(queue, _frameEmitter, _constraints);
218 10 : req->setOutput(a, [this] (core::FrameAttachmentData &attachment, bool success, Ref *data) {
219 10 : log::verbose("View", "View::runWithQueue - output");
220 10 : if (success) {
221 10 : _initImage = move(attachment.image);
222 : }
223 10 : run();
224 10 : return true;
225 : }, this);
226 :
227 10 : _mainLoop->performOnMainThread([this, req] {
228 10 : if (_director->acquireFrame(req)) {
229 10 : _glLoop->performOnGlThread([this, req = move(req)] () mutable {
230 10 : _frameEmitter->submitNextFrame(move(req));
231 10 : });
232 : }
233 10 : }, this);
234 10 : }
235 :
236 0 : void View::onAdded(Device &dev) {
237 0 : std::unique_lock<Mutex> lock(_mutex);
238 0 : _device = &dev;
239 0 : _running = true;
240 0 : }
241 :
242 0 : void View::onRemoved() {
243 0 : std::unique_lock<Mutex> lock(_mutex);
244 0 : _running = false;
245 0 : _callbacks.clear();
246 0 : lock.unlock();
247 0 : if (_threadStarted) {
248 0 : _thread.join();
249 : }
250 0 : }
251 :
252 10 : void View::deprecateSwapchain(bool fast) {
253 : XL_VKVIEW_LOG("deprecateSwapchain");
254 10 : if (!_running) {
255 0 : return;
256 : }
257 10 : performOnThread([this, fast] {
258 10 : if (!_swapchain) {
259 0 : return;
260 : }
261 :
262 10 : _swapchain->deprecate(fast);
263 10 : auto it = _scheduledPresent.begin();
264 10 : while (it != _scheduledPresent.end()) {
265 0 : runScheduledPresent(move(*it));
266 0 : it = _scheduledPresent.erase(it);
267 : }
268 :
269 10 : if (!_blockSwapchainRecreation && _swapchain->getAcquiredImagesCount() == 0) {
270 0 : recreateSwapchain(_swapchain->getRebuildMode());
271 : }
272 : }, this, true);
273 : }
274 :
275 4630 : bool View::present(Rc<ImageStorage> &&object) {
276 : XL_VKVIEW_LOG("present");
277 4630 : if (object->isSwapchainImage()) {
278 4630 : if (_options.followDisplayLink) {
279 0 : performOnThread([this, object = move(object)] () mutable {
280 0 : schedulePresent((SwapchainImage *)object.get(), 0);
281 0 : }, this);
282 0 : return false;
283 : }
284 4630 : auto clock = xenolith::platform::clock(core::ClockType::Monotonic);
285 4630 : auto img = (SwapchainImage *)object.get();
286 4630 : if (!img->getPresentWindow() || img->getPresentWindow() < clock) {
287 611 : if (_options.presentImmediate) {
288 0 : performOnThread([this, object = move(object)] () mutable {
289 0 : auto queue = _device->tryAcquireQueueSync(QueueOperations::Present, true);
290 0 : auto img = (SwapchainImage *)object.get();
291 0 : if (img->getSwapchain() == _swapchain && img->isSubmitted()) {
292 0 : presentWithQueue(*queue, move(object));
293 : }
294 0 : _glLoop->performOnGlThread([this, queue = move(queue)] () mutable {
295 0 : _device->releaseQueue(move(queue));
296 0 : }, this);
297 0 : }, this);
298 0 : return false;
299 : }
300 611 : auto queue = _device->tryAcquireQueueSync(QueueOperations::Present, false);
301 611 : if (queue) {
302 611 : performOnThread([this, queue = move(queue), object = move(object)] () mutable {
303 611 : auto img = (SwapchainImage *)object.get();
304 611 : if (img->getSwapchain() == _swapchain && img->isSubmitted()) {
305 611 : presentWithQueue(*queue, move(object));
306 : }
307 611 : _glLoop->performOnGlThread([this, queue = move(queue)] () mutable {
308 611 : _device->releaseQueue(move(queue));
309 611 : }, this);
310 611 : }, this);
311 : } else {
312 0 : _device->acquireQueue(QueueOperations::Present, *(Loop *)_glLoop.get(),
313 0 : [this, object = move(object)] (Loop &, const Rc<DeviceQueue> &queue) mutable {
314 0 : performOnThread([this, queue, object = move(object)] () mutable {
315 0 : auto img = (SwapchainImage *)object.get();
316 0 : if (img->getSwapchain() == _swapchain && img->isSubmitted()) {
317 0 : presentWithQueue(*queue, move(object));
318 : }
319 0 : _glLoop->performOnGlThread([this, queue = move(queue)] () mutable {
320 0 : _device->releaseQueue(move(queue));
321 0 : }, this);
322 0 : }, this);
323 0 : }, [this] (Loop &) {
324 0 : invalidate();
325 0 : }, this);
326 : }
327 611 : } else {
328 4019 : performOnThread([this, object = move(object), t = img->getPresentWindow() - clock] () mutable {
329 4019 : schedulePresent((SwapchainImage *)object.get(), t);
330 4019 : }, this, true);
331 : }
332 : } else {
333 0 : if (!_options.renderImageOffscreen) {
334 0 : return true;
335 : }
336 0 : auto gen = _gen;
337 0 : performOnThread([this, object = move(object), gen] () mutable {
338 0 : presentImmediate(move(object), [this, gen] (bool success) {
339 0 : if (gen == _gen) {
340 : XL_VKVIEW_LOG("present - scheduleNextImage");
341 0 : scheduleNextImage(0, false);
342 : }
343 0 : });
344 0 : if (_swapchain->isDeprecated()) {
345 0 : recreateSwapchain(_swapchain->getRebuildMode());
346 : }
347 0 : }, this);
348 0 : return true;
349 : }
350 4630 : return false;
351 : }
352 :
353 10 : bool View::presentImmediate(Rc<ImageStorage> &&object, Function<void(bool)> &&scheduleCb) {
354 : XL_VKVIEW_LOG("presentImmediate: ", _framesInProgress);
355 10 : if (!_swapchain) {
356 0 : return false;
357 : }
358 :
359 10 : auto ops = QueueOperations::Present;
360 10 : auto dev = (Device *)_device.get();
361 :
362 : VkFilter filter;
363 10 : if (!isImagePresentable(*object->getImage(), filter)) {
364 0 : return false;
365 : }
366 :
367 10 : Rc<DeviceQueue> queue;
368 10 : Rc<CommandPool> pool;
369 10 : Rc<Fence> presentFence;
370 :
371 10 : Rc<Image> sourceImage = (Image *)object->getImage().get();
372 10 : Rc<ImageStorage> targetImage;
373 :
374 10 : Vector<const CommandBuffer *> buffers;
375 10 : Loop *loop = (Loop *)_glLoop.get();
376 :
377 0 : auto cleanup = [&] {
378 0 : if (presentFence) {
379 0 : presentFence = nullptr;
380 : }
381 0 : if (pool) {
382 0 : dev->releaseCommandPoolUnsafe(move(pool));
383 0 : pool = nullptr;
384 : }
385 0 : if (queue) {
386 0 : dev->releaseQueue(move(queue));
387 0 : queue = nullptr;
388 : }
389 0 : return false;
390 10 : };
391 :
392 : #if XL_VKAPI_DEBUG
393 : auto t = xenolith::platform::clock(core::ClockType::Monotonic);
394 : #endif
395 :
396 10 : if (_options.waitOnSwapchainPassFence) {
397 0 : waitForFences(_frameOrder);
398 : }
399 :
400 : XL_VKAPI_LOG("[PresentImmediate] [waitForFences] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
401 :
402 10 : if (!scheduleCb) {
403 10 : presentFence = loop->acquireFence(0, false);
404 : }
405 :
406 10 : auto swapchainAcquiredImage = _swapchain->acquire(true, presentFence);;
407 10 : if (!swapchainAcquiredImage) {
408 : XL_VKAPI_LOG("[PresentImmediate] [acquire-failed] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
409 0 : if (presentFence) {
410 0 : presentFence->schedule(*loop);
411 : }
412 0 : return cleanup();
413 : }
414 :
415 10 : targetImage = Rc<SwapchainImage>::create(Rc<SwapchainHandle>(_swapchain), *swapchainAcquiredImage->data, move(swapchainAcquiredImage->sem));
416 :
417 : XL_VKAPI_LOG("[PresentImmediate] [acquire] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
418 :
419 10 : pool = dev->acquireCommandPool(ops);
420 :
421 10 : auto buf = pool->recordBuffer(*dev, [&] (CommandBuffer &buf) {
422 10 : auto targetImageObj = (Image *)targetImage->getImage().get();
423 10 : auto sourceLayout = VkImageLayout(object->getLayout());
424 :
425 10 : Vector<ImageMemoryBarrier> inputImageBarriers;
426 10 : inputImageBarriers.emplace_back(ImageMemoryBarrier(targetImageObj,
427 : VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
428 : VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL));
429 :
430 10 : Vector<ImageMemoryBarrier> outputImageBarriers;
431 10 : outputImageBarriers.emplace_back(ImageMemoryBarrier(targetImageObj,
432 : VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT,
433 : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR));
434 :
435 10 : if (sourceLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
436 0 : inputImageBarriers.emplace_back(ImageMemoryBarrier(sourceImage,
437 : VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
438 : sourceLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL));
439 : }
440 :
441 10 : if (!inputImageBarriers.empty()) {
442 10 : buf.cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, inputImageBarriers);
443 : }
444 :
445 10 : buf.cmdCopyImage(sourceImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, targetImageObj, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, filter);
446 :
447 10 : if (!outputImageBarriers.empty()) {
448 10 : buf.cmdPipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, outputImageBarriers);
449 : }
450 :
451 10 : return true;
452 20 : });
453 :
454 10 : buffers.emplace_back(buf);
455 :
456 10 : core::FrameSync frameSync;
457 10 : object->rearmSemaphores(*loop);
458 :
459 20 : frameSync.waitAttachments.emplace_back(core::FrameSyncAttachment{nullptr, object->getWaitSem(),
460 10 : object.get(), core::PipelineStage::Transfer});
461 20 : frameSync.waitAttachments.emplace_back(core::FrameSyncAttachment{nullptr, targetImage->getWaitSem(),
462 10 : targetImage.get(), core::PipelineStage::Transfer});
463 :
464 20 : frameSync.signalAttachments.emplace_back(core::FrameSyncAttachment{nullptr, targetImage->getSignalSem(),
465 10 : targetImage.get()});
466 :
467 : XL_VKAPI_LOG("[PresentImmediate] [writeBuffers] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
468 :
469 10 : if (presentFence) {
470 10 : presentFence->check(*(Loop *)_glLoop.get(), false);
471 : }
472 :
473 : XL_VKAPI_LOG("[PresentImmediate] [acquireFence] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
474 :
475 10 : queue = dev->tryAcquireQueueSync(ops, true);
476 10 : if (!queue) {
477 0 : return cleanup();
478 : }
479 :
480 : XL_VKAPI_LOG("[PresentImmediate] [acquireQueue] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
481 :
482 10 : if (!presentFence) {
483 0 : presentFence = loop->acquireFence(0, false);
484 : }
485 :
486 10 : if (!queue->submit(frameSync, *presentFence, *pool, buffers)) {
487 0 : return cleanup();
488 : }
489 :
490 : XL_VKAPI_LOG("[PresentImmediate] [submit] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
491 :
492 10 : auto result = _swapchain->present(*queue, targetImage);
493 10 : updateFrameInterval();
494 :
495 : XL_VKAPI_LOG("[PresentImmediate] [present] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
496 :
497 10 : if (result == VK_SUCCESS) {
498 10 : if (queue) {
499 10 : dev->releaseQueue(move(queue));
500 10 : queue = nullptr;
501 : }
502 10 : if (scheduleCb) {
503 0 : pool->autorelease(object);
504 0 : presentFence->addRelease([dev, pool = pool ? move(pool) : nullptr, scheduleCb = move(scheduleCb), object = move(object), loop] (bool success) mutable {
505 0 : if (pool) {
506 0 : dev->releaseCommandPoolUnsafe(move(pool));
507 : }
508 0 : loop->releaseImage(move(object));
509 0 : scheduleCb(success);
510 0 : }, this, "View::presentImmediate::releaseCommandPoolUnsafe");
511 0 : scheduleFence(move(presentFence));
512 : } else {
513 10 : presentFence->check(*((Loop *)_glLoop.get()), false);
514 10 : dev->releaseCommandPoolUnsafe(move(pool));
515 10 : loop->releaseImage(move(object));
516 : }
517 : XL_VKAPI_LOG("[PresentImmediate] [presentFence] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
518 10 : presentFence = nullptr;
519 : XL_VKAPI_LOG("[PresentImmediate] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
520 10 : return true;
521 : } else {
522 0 : if (queue) {
523 0 : queue->waitIdle();
524 0 : dev->releaseQueue(move(queue));
525 0 : queue = nullptr;
526 : }
527 0 : if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) {
528 0 : _swapchain->deprecate(false);
529 0 : presentFence->check(*loop, false);
530 : XL_VKAPI_LOG("[PresentImmediate] [presentFence] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
531 0 : presentFence = nullptr;
532 :
533 0 : dev->releaseCommandPoolUnsafe(move(pool));
534 0 : pool = nullptr;
535 : }
536 : XL_VKAPI_LOG("[PresentImmediate] [", xenolith::platform::clock(core::ClockType::Monotonic) - t, "]");
537 0 : return cleanup();
538 : }
539 10 : }
540 :
541 0 : void View::invalidateTarget(Rc<ImageStorage> &&object) {
542 : XL_VKVIEW_LOG("invalidateTarget");
543 0 : if (!object) {
544 0 : return;
545 : }
546 :
547 0 : if (object->isSwapchainImage()) {
548 0 : auto img = (SwapchainImage *)object.get();
549 0 : img->invalidateImage();
550 : } else {
551 :
552 : }
553 : }
554 :
555 0 : Rc<Ref> View::getSwapchainHandle() const {
556 0 : if (_swapchain) {
557 0 : return _swapchain.get();
558 : } else {
559 0 : return nullptr;
560 : }
561 : }
562 :
563 0 : void View::captureImage(StringView name, const Rc<core::ImageObject> &image, AttachmentLayout l) const {
564 0 : auto str = name.str<Interface>();
565 0 : _device->getTextureSetLayout()->readImage(*_device, *(Loop *)_glLoop.get(), (Image *)image.get(), l,
566 0 : [str] (const ImageInfoData &info, BytesView view) mutable {
567 0 : if (!StringView(str).ends_with(".png")) {
568 0 : str = str + String(".png");
569 : }
570 0 : if (!view.empty()) {
571 0 : auto fmt = core::getImagePixelFormat(info.format);
572 0 : bitmap::PixelFormat pixelFormat = bitmap::PixelFormat::Auto;
573 0 : switch (fmt) {
574 0 : case core::PixelFormat::A: pixelFormat = bitmap::PixelFormat::A8; break;
575 0 : case core::PixelFormat::IA: pixelFormat = bitmap::PixelFormat::IA88; break;
576 0 : case core::PixelFormat::RGB: pixelFormat = bitmap::PixelFormat::RGB888; break;
577 0 : case core::PixelFormat::RGBA: pixelFormat = bitmap::PixelFormat::RGBA8888; break;
578 0 : default: break;
579 : }
580 0 : if (pixelFormat != bitmap::PixelFormat::Auto) {
581 0 : Bitmap bmp(view.data(), info.extent.width, info.extent.height, pixelFormat);
582 0 : bmp.save(str);
583 0 : }
584 : }
585 0 : });
586 0 : }
587 :
588 0 : void View::captureImage(Function<void(const ImageInfoData &info, BytesView view)> &&cb, const Rc<core::ImageObject> &image, AttachmentLayout l) const {
589 0 : _device->getTextureSetLayout()->readImage(*_device, *(Loop *)_glLoop.get(), (Image *)image.get(), l, move(cb));
590 0 : }
591 :
592 0 : void View::scheduleFence(Rc<Fence> &&fence) {
593 : XL_VKVIEW_LOG("scheduleFence");
594 0 : if (_running.load()) {
595 0 : performOnThread([this, fence = move(fence)] () mutable {
596 0 : auto loop = (Loop *)_glLoop.get();
597 0 : if (!fence->check(*loop, true)) {
598 0 : auto frame = fence->getFrame();
599 0 : if (frame != 0 && (_fenceOrder == 0 || _fenceOrder > frame)) {
600 0 : _fenceOrder = frame;
601 : }
602 0 : _fences.emplace_back(move(fence));
603 : }
604 0 : }, this, true);
605 : } else {
606 0 : auto loop = (Loop *)_glLoop.get();
607 0 : fence->check(*loop, false);
608 : }
609 0 : }
610 :
611 10 : void View::mapWindow() {
612 10 : if (_options.renderOnDemand) {
613 10 : setReadyForNextFrame();
614 : } else {
615 0 : _scheduledTime = 0;
616 0 : scheduleNextImage(0, true);
617 : }
618 10 : }
619 :
620 7051 : void View::setReadyForNextFrame() {
621 7051 : performOnThread([this] {
622 7041 : if (!_readyForNextFrame) {
623 4640 : _scheduledTime = 0;
624 4640 : if (_swapchain && _options.renderOnDemand && _framesInProgress == 0 && _swapchain->getAcquiredImagesCount() == 0) {
625 : XL_VKVIEW_LOG("setReadyForNextFrame - scheduleNextImage");
626 10 : scheduleNextImage(0, true);
627 : } else {
628 4630 : _readyForNextFrame = true;
629 : }
630 : }
631 7041 : }, this, true);
632 7051 : }
633 :
634 0 : void View::setRenderOnDemand(bool value) {
635 0 : performOnThread([this, value] {
636 0 : _options.renderOnDemand = value;
637 0 : }, this, true);
638 0 : }
639 :
640 0 : bool View::isRenderOnDemand() const {
641 0 : return _options.renderOnDemand;
642 : }
643 :
644 0 : bool View::pollInput(bool frameReady) {
645 0 : return false;
646 : }
647 :
648 20 : core::SurfaceInfo View::getSurfaceOptions() const {
649 20 : return _instance->getSurfaceOptions(_surface->getSurface(), _device->getPhysicalDevice());
650 : }
651 :
652 0 : void View::invalidate() {
653 :
654 0 : }
655 :
656 4640 : void View::scheduleNextImage(uint64_t windowOffset, bool immediately) {
657 : XL_VKVIEW_LOG("scheduleNextImage");
658 4640 : performOnThread([this, windowOffset, immediately] {
659 4640 : _scheduledTime = xenolith::platform::clock(core::ClockType::Monotonic) + _info.frameInterval + config::OnDemandFrameInterval;
660 4640 : if (!_options.renderOnDemand || _readyForNextFrame || immediately) {
661 4640 : _frameEmitter->setEnableBarrier(_options.enableFrameEmitterBarrier);
662 :
663 4640 : if (_options.renderImageOffscreen) {
664 0 : scheduleSwapchainImage(windowOffset, AcquireOffscreenImage);
665 4640 : } else if (_options.acquireImageImmediately || immediately) {
666 20 : scheduleSwapchainImage(windowOffset, AcquireSwapchainImageImmediate);
667 : } else {
668 4620 : scheduleSwapchainImage(windowOffset, AcquireSwapchainImageAsync);
669 : }
670 :
671 4640 : _readyForNextFrame = false;
672 : }
673 4640 : }, this, true);
674 4640 : }
675 :
676 4640 : void View::scheduleSwapchainImage(uint64_t windowOffset, ScheduleImageMode mode) {
677 : XL_VKVIEW_LOG("scheduleSwapchainImage");
678 4640 : Rc<SwapchainImage> swapchainImage;
679 4640 : Rc<FrameRequest> newFrameRequest;
680 4640 : auto constraints = _constraints;
681 :
682 4640 : if (mode != ScheduleImageMode::AcquireOffscreenImage) {
683 4640 : if (!_swapchain) {
684 0 : return;
685 : }
686 :
687 4640 : auto fullOffset = getUpdateInterval() + windowOffset;
688 4640 : if (fullOffset > _info.frameInterval) {
689 0 : swapchainImage = Rc<SwapchainImage>::create(Rc<SwapchainHandle>(_swapchain), _frameOrder, 0);
690 : } else {
691 4640 : swapchainImage = Rc<SwapchainImage>::create(Rc<SwapchainHandle>(_swapchain), _frameOrder, _nextPresentWindow);
692 : }
693 :
694 4640 : swapchainImage->setReady(false);
695 4640 : constraints.extent = Extent2(swapchainImage->getInfo().extent.width, swapchainImage->getInfo().extent.height);
696 : }
697 :
698 4640 : ++ _framesInProgress;
699 4640 : if (_framesInProgress > _swapchain->getConfig().imageCount - 1 && _framesInProgress > 1) {
700 : XL_VKVIEW_LOG("scheduleSwapchainImage: extra frame: ", _framesInProgress);
701 : }
702 :
703 4640 : newFrameRequest = _frameEmitter->makeRequest(constraints);
704 :
705 : // make new frame request immediately
706 4640 : _mainLoop->performOnMainThread([this, req = move(newFrameRequest), swapchainImage, swapchain = _swapchain] () mutable {
707 : XL_VKVIEW_LOG("scheduleSwapchainImage: _director->acquireFrame");
708 4640 : if (_director->acquireFrame(req)) {
709 : XL_VKVIEW_LOG("scheduleSwapchainImage: frame acquired");
710 4640 : _glLoop->performOnGlThread([this, req = move(req), swapchainImage = move(swapchainImage), swapchain] () mutable {
711 4640 : if (_glLoop->isRunning() && swapchain) {
712 : XL_VKVIEW_LOG("scheduleSwapchainImage: setup frame request");
713 4640 : auto &queue = req->getQueue();
714 4640 : auto a = queue->getPresentImageOutput();
715 4640 : if (!a) {
716 0 : a = queue->getTransferImageOutput();
717 : }
718 4640 : if (!a) {
719 0 : -- _framesInProgress;
720 0 : log::error("vk::View", "Fail to run view with queue '", queue->getName(), "': no usable output attachments found");
721 0 : return;
722 : }
723 :
724 4640 : req->autorelease(swapchain);
725 4640 : req->setRenderTarget(a, Rc<core::ImageStorage>(swapchainImage));
726 9280 : req->setOutput(a, [this, swapchain] (core::FrameAttachmentData &data, bool success, Ref *) {
727 : XL_VKVIEW_LOG("scheduleSwapchainImage: output on frame");
728 4640 : if (data.image) {
729 4630 : if (success) {
730 4630 : return present(move(data.image));
731 : } else {
732 0 : invalidateTarget(move(data.image));
733 0 : performOnThread([this] {
734 0 : -- _framesInProgress;
735 0 : }, this);
736 : }
737 : }
738 10 : return true;
739 4640 : }, this);
740 : XL_VKVIEW_LOG("scheduleSwapchainImage: submit frame");
741 4640 : auto nextFrame = _frameEmitter->submitNextFrame(move(req));
742 4640 : if (nextFrame) {
743 4640 : auto order = nextFrame->getOrder();
744 4640 : swapchainImage->setFrameIndex(order);
745 :
746 4640 : performOnThread([this, order] {
747 4639 : _frameOrder = order;
748 4639 : }, this);
749 : }
750 4640 : }
751 : }, this);
752 : }
753 4640 : }, this);
754 :
755 : // we should wait until all current fences become signaled
756 : // then acquire image and wait for fence
757 4640 : if (swapchainImage) {
758 4640 : if (mode == AcquireSwapchainImageAsync && _options.waitOnSwapchainPassFence && _fenceOrder != 0) {
759 0 : updateFences();
760 0 : if (_fenceOrder < swapchainImage->getOrder()) {
761 0 : scheduleImage(move(swapchainImage));
762 : } else {
763 0 : _fenceImages.emplace_back(move(swapchainImage));
764 : }
765 : } else {
766 4640 : if (!acquireScheduledImageImmediate(swapchainImage)) {
767 0 : scheduleImage(move(swapchainImage));
768 : }
769 : }
770 : }
771 4640 : }
772 :
773 4640 : bool View::acquireScheduledImageImmediate(const Rc<SwapchainImage> &image) {
774 : XL_VKVIEW_LOG("acquireScheduledImageImmediate");
775 4640 : if (image->getSwapchain() != _swapchain) {
776 0 : image->invalidate();
777 0 : return true;
778 : }
779 :
780 4640 : if (!_swapchainImages.empty()) {
781 0 : auto acquiredImage = _swapchainImages.front();
782 0 : _swapchainImages.pop_front();
783 0 : _glLoop->performOnGlThread([tmp = image.get(), acquiredImage = move(acquiredImage)] () mutable {
784 0 : tmp->setAcquisitionTime(xenolith::platform::clock(core::ClockType::Monotonic));
785 0 : tmp->setImage(move(acquiredImage->swapchain), *acquiredImage->data, move(acquiredImage->sem));
786 0 : tmp->setReady(true);
787 0 : }, image, true);
788 0 : return true;
789 0 : }
790 :
791 4640 : if (!_requestedSwapchainImage.empty()) {
792 0 : return false;
793 : }
794 :
795 4640 : if (!_scheduledImages.empty() && _requestedSwapchainImage.empty()) {
796 0 : acquireScheduledImage();
797 0 : return false;
798 : }
799 :
800 4640 : auto nimages = _swapchain->getConfig().imageCount - _swapchain->getSurfaceInfo().minImageCount;
801 4640 : if (_swapchain->getAcquiredImagesCount() > nimages) {
802 0 : return false;
803 : }
804 :
805 4640 : auto loop = (Loop *)_glLoop.get();
806 4640 : auto fence = loop->acquireFence(0);
807 4640 : if (auto acquiredImage = _swapchain->acquire(false, fence)) {
808 4640 : fence->check(*loop, false);
809 4640 : fence = nullptr;
810 4640 : loop->performOnGlThread([tmp = image.get(), acquiredImage = move(acquiredImage)] () mutable {
811 4640 : tmp->setAcquisitionTime(xenolith::platform::clock(core::ClockType::Monotonic));
812 4640 : tmp->setImage(move(acquiredImage->swapchain), *acquiredImage->data, move(acquiredImage->sem));
813 4640 : tmp->setReady(true);
814 4640 : }, image, true);
815 4640 : return true;
816 : } else {
817 0 : fence->schedule(*loop);
818 0 : return false;
819 4640 : }
820 :
821 : return false;
822 4640 : }
823 :
824 217166 : bool View::acquireScheduledImage() {
825 217166 : if (!_requestedSwapchainImage.empty() || _scheduledImages.empty()) {
826 217166 : return false;
827 : }
828 :
829 : XL_VKVIEW_LOG("acquireScheduledImage");
830 0 : auto loop = (Loop *)_glLoop.get();
831 0 : auto fence = loop->acquireFence(0);
832 0 : if (auto acquiredImage = _swapchain->acquire(true, fence)) {
833 0 : _requestedSwapchainImage.emplace(acquiredImage);
834 0 : fence->addRelease([this, f = fence.get(), acquiredImage] (bool success) mutable {
835 0 : performOnThread([this, acquiredImage = move(acquiredImage), success] () mutable {
836 0 : if (success) {
837 0 : onSwapchainImageReady(move(acquiredImage));
838 : } else {
839 0 : _requestedSwapchainImage.erase(acquiredImage);
840 : }
841 0 : }, this, true);
842 : #if XL_VKAPI_DEBUG
843 : XL_VKAPI_LOG("[", f->getFrame(), "] vkAcquireNextImageKHR [complete]",
844 : " [", xenolith::platform::clock(core::ClockType::Monotonic) - f->getArmedTime(), "]");
845 : #endif
846 0 : }, this, "View::acquireScheduledImage");
847 0 : scheduleFence(move(fence));
848 0 : return true;
849 : } else {
850 0 : fence->schedule(*loop);
851 0 : return false;
852 0 : }
853 0 : }
854 :
855 0 : void View::scheduleImage(Rc<SwapchainImage> &&swapchainImage) {
856 : XL_VKVIEW_LOG("scheduleImage");
857 0 : if (!_swapchainImages.empty()) {
858 : // pop one of the previously acquired images
859 0 : auto acquiredImage = _swapchainImages.front();
860 0 : _swapchainImages.pop_front();
861 0 : _glLoop->performOnGlThread([tmp = swapchainImage.get(), acquiredImage = move(acquiredImage)] () mutable {
862 0 : tmp->setAcquisitionTime(xenolith::platform::clock(core::ClockType::Monotonic));
863 0 : tmp->setImage(move(acquiredImage->swapchain), *acquiredImage->data, move(acquiredImage->sem));
864 0 : tmp->setReady(true);
865 0 : }, swapchainImage, true);
866 0 : } else {
867 0 : _scheduledImages.emplace_back(move(swapchainImage));
868 0 : acquireScheduledImage();
869 : }
870 0 : }
871 :
872 0 : void View::onSwapchainImageReady(Rc<SwapchainHandle::SwapchainAcquiredImage> &&image) {
873 : XL_VKVIEW_LOG("onSwapchainImageReady");
874 0 : auto ptr = image.get();
875 :
876 0 : if (!_scheduledImages.empty()) {
877 : // send new swapchain image to framebuffer
878 0 : auto target = _scheduledImages.front();
879 0 : _scheduledImages.pop_front();
880 :
881 0 : _glLoop->performOnGlThread([image = move(image), target = move(target)] () mutable {
882 0 : target->setAcquisitionTime(xenolith::platform::clock(core::ClockType::Monotonic));
883 0 : target->setImage(move(image->swapchain), *image->data, move(image->sem));
884 0 : target->setReady(true);
885 0 : }, this, true);
886 0 : } else {
887 : // hold image until next framebuffer request, if not active queries
888 0 : _swapchainImages.emplace_back(move(image));
889 : }
890 :
891 0 : _requestedSwapchainImage.erase(ptr);
892 :
893 0 : if (!_scheduledImages.empty()) {
894 : // run next image query if someone waits for it
895 0 : acquireScheduledImage();
896 : }
897 0 : }
898 :
899 10 : bool View::recreateSwapchain(core::PresentMode mode) {
900 : XL_VKVIEW_LOG("recreateSwapchain");
901 : struct ResetData : public Ref {
902 : Vector<Rc<SwapchainImage>> fenceImages;
903 : std::deque<Rc<SwapchainImage>> scheduledImages;
904 : Rc<core::FrameEmitter> frameEmitter;
905 : };
906 :
907 10 : auto data = Rc<ResetData>::alloc();
908 10 : data->fenceImages = move(_fenceImages);
909 10 : data->scheduledImages = move(_scheduledImages);
910 10 : data->frameEmitter = _frameEmitter;
911 :
912 10 : _scheduledTime = 0;
913 10 : _framesInProgress -= data->fenceImages.size();
914 10 : _framesInProgress -= data->scheduledImages.size();
915 :
916 10 : _glLoop->performOnGlThread([data] {
917 10 : for (auto &it : data->fenceImages) {
918 0 : it->invalidate();
919 : }
920 10 : for (auto &it : data->scheduledImages) {
921 0 : it->invalidate();
922 : }
923 10 : data->frameEmitter->dropFrames();
924 10 : }, this);
925 :
926 10 : _fenceImages.clear();
927 10 : _scheduledImages.clear();
928 10 : _requestedSwapchainImage.clear();
929 10 : _swapchainImages.clear();
930 :
931 10 : if (!_surface || mode == core::PresentMode::Unsupported) {
932 0 : _swapchainInvalidated = true;
933 0 : return false;
934 : }
935 :
936 : #if DEBUG
937 10 : if (core::FrameHandle::GetActiveFramesCount() > 1) {
938 0 : core::FrameHandle::DescribeActiveFrames();
939 : }
940 : #endif
941 :
942 10 : auto info = getSurfaceOptions();
943 10 : auto cfg = _info.selectConfig(*this, info);
944 :
945 10 : if (!info.isSupported(cfg)) {
946 0 : log::error("Vk-Error", "Presentation with config ", cfg.description(), " is not supported for ", info.description());
947 0 : _swapchainInvalidated = true;
948 0 : return false;
949 : }
950 :
951 10 : if (cfg.extent.width == 0 || cfg.extent.height == 0) {
952 0 : _swapchainInvalidated = true;
953 0 : return false;
954 : }
955 :
956 10 : bool ret = false;
957 10 : if (mode == core::PresentMode::Unsupported) {
958 0 : ret = createSwapchain(info, move(cfg), cfg.presentMode);
959 : } else {
960 10 : ret = createSwapchain(info, move(cfg), mode);
961 : }
962 10 : if (ret) {
963 : XL_VKVIEW_LOG("recreateSwapchain - scheduleNextImage");
964 10 : _swapchainInvalidated = false;
965 : // run frame as, no present window, no wait on fences
966 10 : scheduleNextImage(0, true);
967 : }
968 10 : return ret;
969 10 : }
970 :
971 20 : bool View::createSwapchain(const core::SurfaceInfo &info, core::SwapchainConfig &&cfg, core::PresentMode presentMode) {
972 20 : auto devInfo = _device->getInfo();
973 :
974 20 : auto swapchainImageInfo = getSwapchainImageInfo(cfg);
975 20 : uint32_t queueFamilyIndices[] = { devInfo.graphicsFamily.index, devInfo.presentFamily.index };
976 :
977 : do {
978 20 : auto oldSwapchain = move(_swapchain);
979 :
980 40 : _swapchain = Rc<SwapchainHandle>::create(*_device, info, cfg, move(swapchainImageInfo), presentMode,
981 40 : _surface, queueFamilyIndices, oldSwapchain ? oldSwapchain.get() : nullptr);
982 :
983 20 : if (_swapchain) {
984 20 : _constraints.extent = cfg.extent;
985 20 : _constraints.transform = cfg.transform;
986 :
987 20 : Vector<uint64_t> ids;
988 20 : auto &cache = _glLoop->getFrameCache();
989 70 : for (auto &it : _swapchain->getImages()) {
990 100 : for (auto &iit : it.views) {
991 50 : auto id = iit.second->getIndex();
992 50 : ids.emplace_back(iit.second->getIndex());
993 50 : iit.second->setReleaseCallback([loop = _glLoop, cache, id] {
994 50 : loop->performOnGlThread([cache, id] {
995 50 : cache->removeImageView(id);
996 50 : });
997 50 : });
998 : }
999 : }
1000 :
1001 20 : _glLoop->performOnGlThread([loop = _glLoop, ids] {
1002 20 : auto &cache = loop->getFrameCache();
1003 70 : for (auto &id : ids) {
1004 50 : cache->addImageView(id);
1005 : }
1006 20 : });
1007 20 : }
1008 :
1009 20 : _config = move(cfg);
1010 :
1011 20 : log::verbose("vk::View", "Swapchain: ", _config.description());
1012 :
1013 20 : ++ _gen;
1014 20 : } while (0);
1015 :
1016 40 : return _swapchain != nullptr;
1017 20 : }
1018 :
1019 10 : bool View::isImagePresentable(const core::ImageObject &image, VkFilter &filter) const {
1020 10 : auto dev = (Device *)_device;
1021 :
1022 10 : auto &sourceImageInfo = image.getInfo();
1023 10 : if (sourceImageInfo.extent.depth != 1 || sourceImageInfo.format != _config.imageFormat
1024 20 : || (sourceImageInfo.usage & core::ImageUsage::TransferSrc) == core::ImageUsage::None) {
1025 0 : log::error("Swapchain", "Image can not be presented on swapchain");
1026 0 : return false;
1027 : }
1028 :
1029 : VkFormatProperties sourceProps;
1030 : VkFormatProperties targetProps;
1031 :
1032 20 : dev->getInstance()->vkGetPhysicalDeviceFormatProperties(dev->getInfo().device,
1033 10 : VkFormat(sourceImageInfo.format), &sourceProps);
1034 20 : dev->getInstance()->vkGetPhysicalDeviceFormatProperties(dev->getInfo().device,
1035 10 : VkFormat(_config.imageFormat), &targetProps);
1036 :
1037 10 : if (_config.extent.width == sourceImageInfo.extent.width && _config.extent.height == sourceImageInfo.extent.height) {
1038 10 : if ((targetProps.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_DST_BIT) == 0) {
1039 0 : return false;
1040 : }
1041 :
1042 10 : if (sourceImageInfo.tiling == core::ImageTiling::Optimal) {
1043 10 : if ((sourceProps.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT) == 0) {
1044 0 : return false;
1045 : }
1046 : } else {
1047 0 : if ((sourceProps.linearTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT) == 0) {
1048 0 : return false;
1049 : }
1050 : }
1051 : } else {
1052 0 : if ((targetProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0) {
1053 0 : return false;
1054 : }
1055 :
1056 0 : if (sourceImageInfo.tiling == core::ImageTiling::Optimal) {
1057 0 : if ((sourceProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0) {
1058 0 : return false;
1059 : }
1060 :
1061 0 : if ((sourceProps.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0) {
1062 0 : filter = VK_FILTER_LINEAR;
1063 : }
1064 : } else {
1065 0 : if ((sourceProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0) {
1066 0 : return false;
1067 : }
1068 :
1069 0 : if ((sourceProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0) {
1070 0 : filter = VK_FILTER_LINEAR;
1071 : }
1072 : }
1073 : }
1074 :
1075 10 : return true;
1076 : }
1077 :
1078 4019 : void View::runScheduledPresent(Rc<SwapchainImage> &&object) {
1079 : XL_VKVIEW_LOG("runScheduledPresent");
1080 4019 : if (_options.presentImmediate) {
1081 0 : auto queue = _device->tryAcquireQueueSync(QueueOperations::Present, true);
1082 0 : if (object->getSwapchain() == _swapchain && object->isSubmitted()) {
1083 0 : presentWithQueue(*queue, move(object));
1084 : }
1085 0 : _glLoop->performOnGlThread([this, queue = move(queue)] () mutable {
1086 0 : _device->releaseQueue(move(queue));
1087 0 : }, this);
1088 0 : } else {
1089 4019 : _glLoop->performOnGlThread([this, object = move(object)] () mutable {
1090 4019 : if (!_glLoop->isRunning()) {
1091 0 : return;
1092 : }
1093 :
1094 12057 : _device->acquireQueue(QueueOperations::Present, *(Loop*) _glLoop.get(),
1095 4019 : [this, object = move(object)](Loop&, const Rc<DeviceQueue> &queue) mutable {
1096 4019 : performOnThread([this, queue, object = move(object)]() mutable {
1097 4019 : if (object->getSwapchain() == _swapchain && object->isSubmitted()) {
1098 4019 : presentWithQueue(*queue, move(object));
1099 : }
1100 4019 : _glLoop->performOnGlThread([this, queue = move(queue)]() mutable {
1101 4019 : _device->releaseQueue(move(queue));
1102 4019 : }, this);
1103 4019 : }, this);
1104 4019 : }, [this](Loop&) {
1105 0 : invalidate();
1106 8038 : }, this);
1107 : }, this);
1108 : }
1109 4019 : }
1110 :
1111 4630 : void View::presentWithQueue(DeviceQueue &queue, Rc<ImageStorage> &&image) {
1112 : XL_VKVIEW_LOG("presentWithQueue: ", _framesInProgress);
1113 4630 : auto res = _swapchain->present(queue, move(image));
1114 4630 : auto dt = updateFrameInterval();
1115 4630 : if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) {
1116 : XL_VKVIEW_LOG("presentWithQueue - deprecate swapchain");
1117 0 : _swapchain->deprecate(false);
1118 4630 : } else if (res != VK_SUCCESS) {
1119 0 : log::error("vk::View", "presentWithQueue: error:", getVkResultName(res));
1120 : }
1121 : XL_VKVIEW_LOG("presentWithQueue - presented");
1122 4630 : _blockSwapchainRecreation = true;
1123 :
1124 : // DO NOT decrement active frame counter before poll input
1125 : // Input can call setReadyForNextFrame, that spawns new frame when _framesInProgress is 0
1126 : // so, scheduleNextImage will starts separate frame stream, that can cause deadlock on resources
1127 :
1128 4630 : if (!pollInput(true)) {
1129 0 : _blockSwapchainRecreation = false;
1130 0 : -- _framesInProgress;
1131 : XL_VKVIEW_LOG("presentWithQueue - pollInputExit");
1132 0 : return;
1133 : }
1134 :
1135 4630 : _blockSwapchainRecreation = false;
1136 4630 : -- _framesInProgress;
1137 :
1138 4630 : if (_swapchain->isDeprecated() && _swapchain->getAcquiredImagesCount() == 0) {
1139 10 : waitForFences(_frameOrder);
1140 10 : queue.waitIdle();
1141 :
1142 10 : recreateSwapchain(_swapchain->getRebuildMode());
1143 : } else {
1144 4620 : if (!_options.renderOnDemand || _readyForNextFrame) {
1145 4620 : if (_options.followDisplayLink) {
1146 : XL_VKVIEW_LOG("presentWithQueue - scheduleNextImage - followDisplayLink");
1147 0 : scheduleNextImage(0, true);
1148 : XL_VKVIEW_LOG("presentWithQueue - end");
1149 0 : return;
1150 : }
1151 4620 : _nextPresentWindow = dt.clock + _info.frameInterval - getUpdateInterval();
1152 :
1153 : // if current or average framerate below preferred - reduce present window to release new frame early
1154 4620 : if (dt.dt > _info.frameInterval || dt.avg > _info.frameInterval) {
1155 2294 : _nextPresentWindow -= (std::max(dt.dt, dt.avg) - _info.frameInterval);
1156 : }
1157 :
1158 : XL_VKVIEW_LOG("presentWithQueue - scheduleNextImage");
1159 4620 : scheduleNextImage(0, false);
1160 : }
1161 : }
1162 :
1163 : XL_VKVIEW_LOG("presentWithQueue - end");
1164 : }
1165 :
1166 0 : void View::invalidateSwapchainImage(Rc<ImageStorage> &&image) {
1167 : XL_VKVIEW_LOG("invalidateSwapchainImage");
1168 0 : _swapchain->invalidateImage(move(image));
1169 :
1170 0 : if (_swapchain->isDeprecated() && _swapchain->getAcquiredImagesCount() == 0) {
1171 : // log::debug("View", "recreateSwapchain - View::invalidateSwapchainImage (", renderqueue::FrameHandle::GetActiveFramesCount(), ")");
1172 0 : recreateSwapchain(_swapchain->getRebuildMode());
1173 : } else {
1174 : XL_VKVIEW_LOG("invalidateSwapchainImage - scheduleNextImage");
1175 0 : scheduleNextImage(_info.frameInterval, false);
1176 : }
1177 0 : }
1178 :
1179 4640 : View::FrameTimeInfo View::updateFrameInterval() {
1180 : FrameTimeInfo ret;
1181 4640 : ret.clock = xenolith::platform::clock(core::ClockType::Monotonic);
1182 4640 : ret.dt = ret.clock - _lastFrameStart;
1183 4640 : _lastFrameInterval = ret.dt;
1184 4640 : _avgFrameInterval.addValue(ret.dt);
1185 4640 : _avgFrameIntervalValue = _avgFrameInterval.getAverage();
1186 4640 : _lastFrameStart = ret.clock;
1187 4640 : ret.avg = _avgFrameIntervalValue.load();
1188 4640 : return ret;
1189 : }
1190 :
1191 10 : void View::waitForFences(uint64_t min) {
1192 10 : auto loop = (Loop *)_glLoop.get();
1193 10 : auto it = _fences.begin();
1194 10 : while (it != _fences.end()) {
1195 0 : if ((*it)->getFrame() <= min) {
1196 : // log::debug("View", "waitForFences: ", (*it)->getTag());
1197 0 : if ((*it)->check(*loop, false)) {
1198 0 : it = _fences.erase(it);
1199 : } else {
1200 0 : ++ it;
1201 : }
1202 : } else {
1203 0 : ++ it;
1204 : }
1205 : }
1206 10 : }
1207 :
1208 10 : void View::finalize() {
1209 10 : _glLoop->performOnGlThread([this] {
1210 10 : end();
1211 10 : }, this);
1212 :
1213 10 : std::unique_lock<Mutex> lock(_mutex);
1214 10 : _callbacks.clear();
1215 10 : }
1216 :
1217 217166 : void View::updateFences() {
1218 217166 : uint64_t fenceOrder = 0;
1219 : do {
1220 217166 : auto loop = (Loop *)_glLoop.get();
1221 217166 : auto it = _fences.begin();
1222 217166 : while (it != _fences.end()) {
1223 0 : if ((*it)->check(*loop, true)) {
1224 0 : it = _fences.erase(it);
1225 : } else {
1226 0 : auto frame = (*it)->getFrame();
1227 0 : if (frame != 0 && (fenceOrder == 0 || fenceOrder > frame)) {
1228 0 : fenceOrder = frame;
1229 : }
1230 0 : ++ it;
1231 : }
1232 : }
1233 : } while (0);
1234 :
1235 217166 : _fenceOrder = fenceOrder;
1236 217166 : }
1237 :
1238 10 : void View::clearImages() {
1239 10 : _mutex.lock();
1240 :
1241 10 : auto loop = (Loop *)_glLoop.get();
1242 10 : for (auto &it : _fences) {
1243 0 : it->check(*loop, false);
1244 : }
1245 10 : _fences.clear();
1246 10 : _mutex.unlock();
1247 :
1248 10 : for (auto &it :_fenceImages) {
1249 0 : it->invalidateSwapchain();
1250 : }
1251 10 : _fenceImages.clear();
1252 :
1253 10 : for (auto &it :_scheduledImages) {
1254 0 : it->invalidateSwapchain();
1255 : }
1256 10 : _scheduledImages.clear();
1257 :
1258 10 : for (auto &it :_scheduledPresent) {
1259 0 : it->invalidateSwapchain();
1260 : }
1261 10 : _scheduledPresent.clear();
1262 10 : }
1263 :
1264 4019 : void View::schedulePresent(SwapchainImage *img, uint64_t) {
1265 4019 : _scheduledPresent.emplace_back(img);
1266 4019 : }
1267 :
1268 : }
|