@@ -32,16 +32,25 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
32
32
IResizableSurface* m_recreator = nullptr ;
33
33
};
34
34
35
+ //
36
+ struct SPresentSource
37
+ {
38
+ IGPUImage* image;
39
+ VkRect2D rect;
40
+ };
41
+
35
42
//
36
43
class NBL_API2 ISwapchainResources : public core::IReferenceCounted, public ISimpleManagedSurface::ISwapchainResources
37
44
{
38
45
protected:
39
46
friend class IResizableSurface ;
40
47
41
- // The `cmdbuf` is already begun when given to the callback, and will be ended outside.
42
48
// 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;
45
54
};
46
55
47
56
//
@@ -51,22 +60,49 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
51
60
return device->getThreadSafeQueue (fam,device->getQueueCount (fam)-1 );
52
61
}
53
62
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
+
54
87
struct SPresentInfo
55
88
{
56
- inline operator bool () const {return source;}
89
+ inline operator bool () const {return source. image ;}
57
90
58
- IGPUImage* source = nullptr ;
59
- // TODO: add sourceRegion
91
+ SPresentSource source;
92
+ uint8_t mostRecentFamilyOwningSource;
60
93
// only allow waiting for one semaphore, because there's only one source to present!
61
94
IQueue::SSubmitInfo::SSemaphoreInfo wait;
62
95
core::IReferenceCounted* frameResources;
63
96
};
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!
65
100
inline bool present (const SPresentInfo& presentInfo)
66
101
{
67
102
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 );
70
106
}
71
107
72
108
// Call this when you want to recreate the swapchain with new extents
@@ -90,9 +126,9 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
90
126
if (current->getSwapchain ()==oldSwapchain.get ())
91
127
return true ;
92
128
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 );
96
132
}
97
133
98
134
protected:
@@ -221,7 +257,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
221
257
}
222
258
223
259
//
224
- inline bool present_impl (const SPresentInfo& presentInfo)
260
+ inline bool present_impl (const SPresentInfo& presentInfo, const bool acquireOwnership )
225
261
{
226
262
// irrecoverable or bad input
227
263
if (!presentInfo || !getSwapchainResources ())
@@ -250,7 +286,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
250
286
.stageMask = asset::PIPELINE_STAGE_FLAGS::NONE // presentation engine usage isn't a stage
251
287
}
252
288
};
253
- m_lastPresentSourceImage = core::smart_refctd_ptr<IGPUImage>(presentInfo.source );
289
+ m_lastPresentSourceImage = core::smart_refctd_ptr<IGPUImage>(presentInfo.source . image );
254
290
m_lastPresentSemaphore = core::smart_refctd_ptr<ISemaphore>(presentInfo.wait .semaphore );
255
291
m_lastPresentSource = presentInfo.source ;
256
292
m_lastPresentWait = presentInfo.wait ;
@@ -284,7 +320,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
284
320
.semaphore = m_blitSemaphore.get (),
285
321
.value = acquireCount,
286
322
// 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 )
288
324
}
289
325
};
290
326
willBlit &= bool (blitted[1 ].stageMask .value );
@@ -328,7 +364,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
328
364
// No because there can be presents enqueued whose wait semaphores have not signalled yet, meaning there could be images presented in the future.
329
365
// 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.
330
366
IQueue::SSubmitInfo::SSemaphoreInfo m_lastPresentWait = {};
331
- decltype (SPresentInfo::source) m_lastPresentSource = {};
367
+ SPresentSource m_lastPresentSource = {};
332
368
core::smart_refctd_ptr<ISemaphore> m_lastPresentSemaphore = {};
333
369
core::smart_refctd_ptr<IGPUImage> m_lastPresentSourceImage = {};
334
370
};
0 commit comments