@@ -50,15 +50,16 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
50
50
// A small utility for the boilerplate
51
51
inline uint8_t pickQueueFamily (ILogicalDevice* device) const
52
52
{
53
+ auto physDev = device->getPhysicalDevice ();
53
54
uint8_t qFam = 0u ;
54
55
for (; qFam<ILogicalDevice::MaxQueueFamilies; qFam++)
55
- if (device->getQueueCount (qFam) && m_surface->isSupportedForPhysicalDevice (device-> getPhysicalDevice (), qFam))
56
+ if (device->getQueueCount (qFam) && m_surface->isSupportedForPhysicalDevice (physDev,qFam) && queueFamilyOk (physDev-> getQueueFamilyProperties ()[ qFam] ))
56
57
break ;
57
58
return qFam;
58
59
}
59
60
60
61
// Just pick the first queue within the first compatible family
61
- inline CThreadSafeQueueAdapter* pickQueue (ILogicalDevice* device) const
62
+ virtual inline CThreadSafeQueueAdapter* pickQueue (ILogicalDevice* device) const
62
63
{
63
64
return device->getThreadSafeQueue (pickQueueFamily (device),0 );
64
65
}
@@ -70,18 +71,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
70
71
friend class ISimpleManagedSurface ;
71
72
72
73
public:
73
- // If window gets minimized on some platforms or more rarely if it gets resized weirdly, the render area becomes 0 so its impossible to recreate a swapchain.
74
- // So we need to defer the swapchain re-creation until we can resize to a valid extent.
75
- enum class STATUS : int8_t
76
- {
77
- IRRECOVERABLE = -1 ,
78
- USABLE,
79
- NOT_READY = 1
80
- };
81
- // If `getStatus()==STATUS::IRRECOVERABLE` the Managed Surface Object is useless and can't be recovered into a functioning state.
82
- inline STATUS getStatus () const {return status;}
83
-
84
- // If status is `NOT_READY` this might either return nullptr or the old stale swapchain that should be retired
74
+ // If `init` not called yet this might return nullptr, or the old stale swapchain that should be retired
85
75
inline ISwapchain* getSwapchain () const {return swapchain.get ();}
86
76
87
77
//
@@ -95,25 +85,13 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
95
85
protected:
96
86
virtual ~ISwapchainResources ()
97
87
{
98
- // just to avoid deadlocks due to circular refcounting
88
+ // just to get rid of circular refs
99
89
becomeIrrecoverable ();
100
90
}
101
91
102
- // just drop the per-swapchain resources, e.g. Framebuffers with each of the swapchain images, or even pre-recorded commandbuffers
103
- inline void invalidate ()
104
- {
105
- if (status==STATUS::NOT_READY)
106
- return ;
107
- invalidate_impl ();
108
- std::fill (images.begin (),images.end (),nullptr );
109
- status = STATUS::NOT_READY;
110
- }
111
- // mark the surface & swapchain as hopeless and ready for deletion due to errors
92
+ //
112
93
inline void becomeIrrecoverable ()
113
94
{
114
- if (status==STATUS::IRRECOVERABLE)
115
- return ;
116
-
117
95
// Want to nullify things in an order that leads to fastest drops (if possible) and shallowest callstacks when refcounting
118
96
invalidate ();
119
97
@@ -124,8 +102,15 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
124
102
if (swapchain)
125
103
while (swapchain->acquiredImagesAwaitingPresent ()) {}
126
104
swapchain = nullptr ;
105
+ }
127
106
128
- status = STATUS::IRRECOVERABLE;
107
+ // just drop the per-swapchain resources, e.g. Framebuffers with each of the swapchain images, or even pre-recorded commandbuffers
108
+ inline void invalidate ()
109
+ {
110
+ if (!images.front ())
111
+ return ;
112
+ invalidate_impl ();
113
+ std::fill (images.begin (),images.end (),nullptr );
129
114
}
130
115
131
116
// here you drop your own resources of the base class
@@ -152,19 +137,15 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
152
137
// extra things you might need
153
138
virtual bool onCreateSwapchain_impl () = 0;
154
139
155
- // As per the above, the swapchain might not be possible to create or recreate right away, so this might be
156
- // either nullptr before the first successful acquire or the old to-be-retired swapchain.
140
+ // We start with a `nullptr` swapchain because some implementations might defer its creation
157
141
core::smart_refctd_ptr<ISwapchain> swapchain = {};
158
142
// Useful for everyone
159
143
std::array<core::smart_refctd_ptr<IGPUImage>,ISwapchain::MaxImages> images = {};
160
- // We start in not-ready, instead of irrecoverable, because we haven't tried to create a swapchain yet
161
- STATUS status = STATUS::NOT_READY;
162
144
};
163
145
164
146
// We need to defer the swapchain creation till the Physical Device is chosen and Queues are created together with the Logical Device
165
147
inline bool init (CThreadSafeQueueAdapter* queue, const ISwapchain::SSharedCreationParams& sharedParams={})
166
148
{
167
- getSwapchainResources ().becomeIrrecoverable ();
168
149
if (!queue)
169
150
return false ;
170
151
@@ -194,30 +175,23 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
194
175
protected: // some of the methods need to stay protected in this base class because they need to be performed under a Mutex for smooth resize variants
195
176
inline ISimpleManagedSurface (core::smart_refctd_ptr<ISurface>&& _surface, ICallback* _cb) : m_surface(std::move(_surface)), m_cb(_cb) {}
196
177
virtual inline ~ISimpleManagedSurface () = default ;
178
+
179
+ virtual inline bool checkQueueFamilyProps (const IPhysicalDevice::SQueueFamilyProperties& props) const {return true ;}
197
180
198
181
// RETURNS: `ISwapchain::MaxImages` on failure, otherwise its the acquired image's index.
199
182
inline uint8_t acquireNextImage ()
200
183
{
184
+ auto swapchainResources = getSwapchainResources ();
185
+ if (!swapchainResources)
186
+ return ISwapchain::MaxImages;
187
+
201
188
// Only check upon an acquire, previously acquired images MUST be presented
202
189
// Window/Surface got closed, but won't actually disappear UNTIL the swapchain gets dropped,
203
190
// which is outside of our control here as there is a nice chain of lifetimes of:
204
191
// `ExternalCmdBuf -via usage of-> Swapchain Image -memory provider-> Swapchain -created from-> Window/Surface`
205
192
// Only when the last user of the swapchain image drops it, will the window die.
206
- if (m_cb->isWindowOpen ())
193
+ if (m_cb->isWindowOpen () && swapchainResources-> swapchain )
207
194
{
208
- using status_t = ISwapchainResources::STATUS;
209
- switch (getSwapchainResources ().getStatus ())
210
- {
211
- case status_t ::NOT_READY:
212
- if (handleNotReady ())
213
- break ;
214
- [[fallthrough]];
215
- case status_t ::IRRECOVERABLE:
216
- return ISwapchain::MaxImages;
217
- default :
218
- break ;
219
- }
220
-
221
195
const IQueue::SSubmitInfo::SSemaphoreInfo signalInfos[1 ] = {
222
196
{
223
197
.semaphore =m_acquireSemaphore.get (),
@@ -227,7 +201,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
227
201
228
202
uint32_t imageIndex;
229
203
// We don't support resizing (swapchain recreation) in this example, so a failure to acquire is a failure to keep running
230
- switch (getSwapchainResources (). swapchain ->acquireNextImage ({.queue =m_queue,.signalSemaphores =signalInfos},&imageIndex))
204
+ switch (swapchainResources-> swapchain ->acquireNextImage ({.queue =m_queue,.signalSemaphores =signalInfos},&imageIndex))
231
205
{
232
206
case ISwapchain::ACQUIRE_IMAGE_RESULT::SUBOPTIMAL: [[fallthrough]];
233
207
case ISwapchain::ACQUIRE_IMAGE_RESULT::SUCCESS:
@@ -239,7 +213,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
239
213
assert (false ); // shouldn't happen though cause we use uint64_t::max() as the timeout
240
214
break ;
241
215
case ISwapchain::ACQUIRE_IMAGE_RESULT::OUT_OF_DATE:
242
- getSwapchainResources (). invalidate ();
216
+ swapchainResources-> invalidate ();
243
217
// try again, will re-create swapchain
244
218
{
245
219
const auto retval = handleOutOfDate ();
@@ -250,28 +224,32 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
250
224
break ;
251
225
}
252
226
}
253
- getSwapchainResources (). becomeIrrecoverable ();
227
+ swapchainResources-> becomeIrrecoverable ();
254
228
return ISwapchain::MaxImages;
255
229
}
230
+
231
+ // nice little callback
232
+ virtual uint8_t handleOutOfDate () = 0;
256
233
257
234
// Frame Resources are not optional, shouldn't be null!
258
235
inline bool present (const uint8_t imageIndex, const std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> waitSemaphores, core::IReferenceCounted* frameResources)
259
236
{
260
- if (getSwapchainResources ().getStatus ()!=ISwapchainResources::STATUS::USABLE || !frameResources)
237
+ auto swapchainResources = getSwapchainResources ();
238
+ if (!swapchainResources || !swapchainResources->swapchain || !frameResources)
261
239
return false ;
262
240
263
241
const ISwapchain::SPresentInfo info = {
264
242
.queue = m_queue,
265
243
.imgIndex = imageIndex,
266
244
.waitSemaphores = waitSemaphores
267
245
};
268
- switch (getSwapchainResources (). getSwapchain () ->present (info,core::smart_refctd_ptr<core::IReferenceCounted>(frameResources)))
246
+ switch (swapchainResources-> swapchain ->present (info,core::smart_refctd_ptr<core::IReferenceCounted>(frameResources)))
269
247
{
270
248
case ISwapchain::PRESENT_RESULT::SUBOPTIMAL: [[fallthrough]];
271
249
case ISwapchain::PRESENT_RESULT::SUCCESS:
272
250
return true ;
273
251
case ISwapchain::PRESENT_RESULT::OUT_OF_DATE:
274
- getSwapchainResources (). invalidate ();
252
+ swapchainResources-> invalidate ();
275
253
break ;
276
254
default :
277
255
// because we won't hold onto the `frameResources` we need to block for `waitSemaphores`
@@ -282,25 +260,18 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
282
260
*(outWait++) = {.semaphore =wait.semaphore ,.value =wait.value };
283
261
const_cast <ILogicalDevice*>(m_queue->getOriginDevice ())->blockForSemaphores (waitInfos);
284
262
}
285
- getSwapchainResources (). becomeIrrecoverable ();
263
+ swapchainResources-> becomeIrrecoverable ();
286
264
break ;
287
265
}
288
266
return false ;
289
267
}
290
268
291
- using image_barrier_t = IGPUCommandBuffer::SImageMemoryBarrier<IGPUCommandBuffer::SOwnershipTransferBarrier>;
292
- // Utility function for more complex Managed Surfaces, it does not increase the `m_acquireCount` but does acquire and present immediately
293
- bool immediateBlit (const image_barrier_t & contents, const IQueue::SSubmitInfo::SSemaphoreInfo& waitBeforeBlit, CThreadSafeQueueAdapter* blitAndPresentQueue);
294
-
295
- virtual ISwapchainResources& getSwapchainResources () = 0;
269
+ //
270
+ virtual ISwapchainResources* getSwapchainResources () = 0;
296
271
297
272
// Generally used to check that per-swapchain resources can be created (including the swapchain itself)
298
273
virtual bool init_impl (CThreadSafeQueueAdapter* queue, const ISwapchain::SSharedCreationParams& sharedParams) = 0;
299
274
300
- // Handlers for acquisition exceptions, by default we can't do anything about them
301
- virtual bool handleNotReady () {return false ;}
302
- virtual uint8_t handleOutOfDate () {return ISwapchain::MaxImages;}
303
-
304
275
//
305
276
ICallback* const m_cb = nullptr ;
306
277
0 commit comments