@@ -32,16 +32,25 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
3232 IResizableSurface* m_recreator = nullptr ;
3333 };
3434
35+ //
36+ struct SPresentSource
37+ {
38+ IGPUImage* image;
39+ VkRect2D rect;
40+ };
41+
3542 //
3643 class NBL_API2 ISwapchainResources : public core::IReferenceCounted, public ISimpleManagedSurface::ISwapchainResources
3744 {
3845 protected:
3946 friend class IResizableSurface ;
4047
41- // The `cmdbuf` is already begun when given to the callback, and will be ended outside.
4248 // Returns what stage the submit signal semaphore should signal from for the presentation to wait on.
43- // User is responsible for transitioning both images' layouts, acquiring ownership etc.
44- virtual asset::PIPELINE_STAGE_FLAGS tripleBufferPresent (IGPUCommandBuffer* cmdbuf, IGPUImage* source, const uint8_t dstIx) = 0;
49+ // The `cmdbuf` is already begun when given to the callback, and will be ended outside.
50+ // User is responsible for transitioning the image layouts (most notably the swapchain), acquiring ownership etc.
51+ // Performance Tip: DO NOT transition the layout of `source` inside this callback, have it already in the correct Layout you need!
52+ // However, if `qFamToAcquireSrcFrom!=IQueue::FamilyIgnored`, you need to acquire the ownership of the `source.image`
53+ virtual asset::PIPELINE_STAGE_FLAGS tripleBufferPresent (IGPUCommandBuffer* cmdbuf, const SPresentSource& source, const uint8_t dstIx, const uint32_t qFamToAcquireSrcFrom) = 0;
4554 };
4655
4756 //
@@ -51,22 +60,49 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
5160 return device->getThreadSafeQueue (fam,device->getQueueCount (fam)-1 );
5261 }
5362
63+ // This is basically a poll, the extent CAN change between a call to this and `present`
64+ inline VkExtent2D getCurrentExtent ()
65+ {
66+ std::unique_lock guard (m_swapchainResourcesMutex);
67+ // if got some weird invalid extent, try to recreate and retry once
68+ while (true )
69+ {
70+ auto resources = getSwapchainResources ();
71+ if (resources)
72+ {
73+ auto swapchain = resources->getSwapchain ();
74+ if (swapchain)
75+ {
76+ const auto & params = swapchain->getCreationParameters ().sharedParams ;
77+ if (params.width >0 && params.height >0 )
78+ return {params.width ,params.height };
79+ }
80+ }
81+ if (!recreateSwapchain ())
82+ break ;
83+ }
84+ return {0 ,0 };
85+ }
86+
5487 struct SPresentInfo
5588 {
56- inline operator bool () const {return source;}
89+ inline operator bool () const {return source. image ;}
5790
58- IGPUImage* source = nullptr ;
59- // TODO: add sourceRegion
91+ SPresentSource source;
92+ uint8_t mostRecentFamilyOwningSource;
6093 // only allow waiting for one semaphore, because there's only one source to present!
6194 IQueue::SSubmitInfo::SSemaphoreInfo wait;
6295 core::IReferenceCounted* frameResources;
6396 };
64- // TODO: explanations
97+ // This is a present that you should regularly use from the main rendering thread or something.
98+ // Due to the constraints and mutexes on everything, its impossible to split this into a separate acquire and present call so this does both.
99+ // So DON'T USE `acquireNextImage` for frame pacing, it was bad Vulkan practice anyway!
65100 inline bool present (const SPresentInfo& presentInfo)
66101 {
67102 std::unique_lock guard (m_swapchainResourcesMutex);
68- // The only thing we want to do under the mutex, is just enqueue a blit and a present, its not a lot
69- return present_impl (presentInfo);
103+ // The only thing we want to do under the mutex, is just enqueue a blit and a present, its not a lot.
104+ // Only acquire ownership if the Blit&Present queue is different to the current one.
105+ return present_impl (presentInfo,getAssignedQueue ()->getFamilyIndex ()!=presentInfo.mostRecentFamilyOwningSource );
70106 }
71107
72108 // Call this when you want to recreate the swapchain with new extents
@@ -90,9 +126,9 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
90126 if (current->getSwapchain ()==oldSwapchain.get ())
91127 return true ;
92128
93- // The blit enqueue operations are fast enough to be done under a mutex, this is safer on some platforms
94- // You need to "race to present" to avoid a flicker
95- return present_impl ({.source =m_lastPresentSource,.wait =m_lastPresentWait,.frameResources =nullptr });
129+ // The blit enqueue operations are fast enough to be done under a mutex, this is safer on some platforms. You need to "race to present" to avoid a flicker.
130+ // Queue family ownership acquire not needed, done by the the very first present when `m_lastPresentSource` wasset.
131+ return present_impl ({.source =m_lastPresentSource,.wait =m_lastPresentWait,.frameResources =nullptr }, false );
96132 }
97133
98134 protected:
@@ -221,7 +257,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
221257 }
222258
223259 //
224- inline bool present_impl (const SPresentInfo& presentInfo)
260+ inline bool present_impl (const SPresentInfo& presentInfo, const bool acquireOwnership )
225261 {
226262 // irrecoverable or bad input
227263 if (!presentInfo || !getSwapchainResources ())
@@ -250,7 +286,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
250286 .stageMask = asset::PIPELINE_STAGE_FLAGS::NONE // presentation engine usage isn't a stage
251287 }
252288 };
253- m_lastPresentSourceImage = core::smart_refctd_ptr<IGPUImage>(presentInfo.source );
289+ m_lastPresentSourceImage = core::smart_refctd_ptr<IGPUImage>(presentInfo.source . image );
254290 m_lastPresentSemaphore = core::smart_refctd_ptr<ISemaphore>(presentInfo.wait .semaphore );
255291 m_lastPresentSource = presentInfo.source ;
256292 m_lastPresentWait = presentInfo.wait ;
@@ -284,7 +320,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
284320 .semaphore = m_blitSemaphore.get (),
285321 .value = acquireCount,
286322 // don't need to predicate with `willBlit` because if `willBlit==false` cmdbuf not properly begun and validation will fail
287- .stageMask = swapchainResources->tripleBufferPresent (cmdbuf,presentInfo.source ,imageIx)
323+ .stageMask = swapchainResources->tripleBufferPresent (cmdbuf,presentInfo.source ,imageIx,acquireOwnership ? queue-> getFamilyIndex ():IQueue::FamilyIgnored )
288324 }
289325 };
290326 willBlit &= bool (blitted[1 ].stageMask .value );
@@ -328,7 +364,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
328364 // No because there can be presents enqueued whose wait semaphores have not signalled yet, meaning there could be images presented in the future.
329365 // Unless you like your frames to go backwards in time in a special "rewind glitch" you need to blit the frame that has not been presented yet or is the same as most recently enqueued.
330366 IQueue::SSubmitInfo::SSemaphoreInfo m_lastPresentWait = {};
331- decltype (SPresentInfo::source) m_lastPresentSource = {};
367+ SPresentSource m_lastPresentSource = {};
332368 core::smart_refctd_ptr<ISemaphore> m_lastPresentSemaphore = {};
333369 core::smart_refctd_ptr<IGPUImage> m_lastPresentSourceImage = {};
334370};
0 commit comments