Skip to content

Commit 50ec6b1

Browse files
Refactor the Managed Surfaces to be able to send a stateful ISwapchainResources more easily
1 parent 985b343 commit 50ec6b1

File tree

4 files changed

+91
-90
lines changed

4 files changed

+91
-90
lines changed

include/nbl/video/utilities/CSimpleResizeSurface.h

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ class CSimpleResizeSurface final : public ISimpleManagedSurface
3434
return core::smart_refctd_ptr<this_t>(new this_t(std::move(_surface),cb),core::dont_grab);
3535
}
3636

37+
//
38+
inline bool init(CThreadSafeQueueAdapter* queue, std::unique_ptr<SwapchainResources>&& scResources, const ISwapchain::SSharedCreationParams& sharedParams={})
39+
{
40+
if (!scResources || !base_init(queue))
41+
return init_fail();
42+
43+
m_sharedParams = sharedParams;
44+
if (!m_sharedParams.deduce(queue->getOriginDevice()->getPhysicalDevice(),getSurface()))
45+
return init_fail();
46+
47+
m_swapchainResources = std::move(scResources);
48+
return true;
49+
}
50+
51+
// Can be public because we don't need to worry about mutexes unlike the Smooth Resize class
52+
inline ISwapchainResources* getSwapchainResources() override {return m_swapchainResources.get();}
53+
3754
// need to see if the swapchain is invalidated (e.g. because we're starting from 0-area old Swapchain) and try to recreate the swapchain
3855
inline uint8_t acquireNextImage()
3956
{
@@ -56,25 +73,17 @@ class CSimpleResizeSurface final : public ISimpleManagedSurface
5673
{
5774
becomeIrrecoverable();
5875
}
59-
inline bool init_impl(const ISwapchain::SSharedCreationParams& sharedParams) override final
60-
{
61-
m_sharedParams = sharedParams;
62-
if (!m_sharedParams.deduce(getAssignedQueue()->getOriginDevice()->getPhysicalDevice(),getSurface()))
63-
return false;
64-
65-
m_swapchainResources = std::make_unique<SwapchainResources>();
66-
return getSwapchainResources();
67-
}
6876

6977
//
70-
inline ISwapchainResources* getSwapchainResources() override {return m_swapchainResources.get();}
7178
inline void becomeIrrecoverable() override {m_swapchainResources = nullptr;}
7279

7380
//
7481
inline bool recreateSwapchain()
7582
{
83+
assert(m_swapchainResources);
7684
// dont assign straight to `m_swapchainResources` because of complex refcounting and cycles
7785
core::smart_refctd_ptr<ISwapchain> newSwapchain;
86+
// TODO: This block of code could be rolled up into `ISimpleManagedSurface::ISwapchainResources` eventually
7887
{
7988
auto* surface = getSurface();
8089
auto device = const_cast<ILogicalDevice*>(getAssignedQueue()->getOriginDevice());
@@ -114,7 +123,6 @@ class CSimpleResizeSurface final : public ISimpleManagedSurface
114123
if (newSwapchain)
115124
{
116125
m_swapchainResources->invalidate();
117-
m_swapchainResources = std::make_unique<SwapchainResources>();
118126
return m_swapchainResources->onCreateSwapchain(getAssignedQueue()->getFamilyIndex(),std::move(newSwapchain));
119127
}
120128
else

include/nbl/video/utilities/CSmoothResizeSurface.h

Lines changed: 42 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ class NBL_API2 ISmoothResizeSurface : public ISimpleManagedSurface
2626

2727
private:
2828
friend class ISmoothResizeSurface;
29-
// `recreator` owns the `ISurface`, which refcounts the `IWindow` which refcounts the callback, so fumb pointer to avoid cycles
30-
inline void setSwapchainRecreator(ISmoothResizeSurface* recreator) { m_recreator = recreator; }
31-
29+
// `recreator` owns the `ISurface`, which refcounts the `IWindow` which refcounts the callback, so dumb pointer to avoid cycles
3230
ISmoothResizeSurface* m_recreator = nullptr;
3331
};
3432

@@ -173,20 +171,21 @@ class NBL_API2 ISmoothResizeSurface : public ISimpleManagedSurface
173171
}
174172

175173
protected:
174+
inline void setSwapchainRecreator() {static_cast<ICallback*>(m_cb)->m_recreator=this;}
175+
176176
using ISimpleManagedSurface::ISimpleManagedSurface;
177177
virtual inline ~ISmoothResizeSurface()
178178
{
179179
// stop any calls into explicit resizes
180-
std::unique_lock guard(m_swapchainResourcesMutex);
181-
static_cast<ICallback*>(m_cb)->setSwapchainRecreator(nullptr);
180+
deinit_impl();
182181
}
183182

184183
//
185184
inline void deinit_impl() override final
186185
{
187186
// stop any calls into explicit resizes
188187
std::unique_lock guard(m_swapchainResourcesMutex);
189-
static_cast<ICallback*>(m_cb)->setSwapchainRecreator(nullptr);
188+
static_cast<ICallback*>(m_cb)->m_recreator = nullptr;
190189

191190
m_lastPresent.source = {};
192191
m_lastPresentSourceImage = nullptr;
@@ -208,38 +207,6 @@ class NBL_API2 ISmoothResizeSurface : public ISimpleManagedSurface
208207
m_presentSemaphore = nullptr;
209208
}
210209

211-
//
212-
inline bool init_impl(const ISwapchain::SSharedCreationParams& sharedParams) override final
213-
{
214-
// swapchain callback already deinitialized, so no mutex needed here
215-
216-
auto queue = getAssignedQueue();
217-
auto device = const_cast<ILogicalDevice*>(queue->getOriginDevice());
218-
219-
m_sharedParams = sharedParams;
220-
if (!m_sharedParams.deduce(device->getPhysicalDevice(),getSurface()))
221-
return false;
222-
223-
// want to keep using the same semaphore throughout the lifetime to not run into sync issues
224-
if (!m_presentSemaphore)
225-
{
226-
m_presentSemaphore = device->createSemaphore(0u);
227-
if (!m_presentSemaphore)
228-
return false;
229-
}
230-
231-
// transient commandbuffer and pool to perform the blits or other copies to SC images
232-
auto pool = device->createCommandPool(queue->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT);
233-
if (!pool || !pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{m_cmdbufs.data(),getMaxFramesInFlight()}))
234-
return false;
235-
236-
if (!createSwapchainResources())
237-
return false;
238-
239-
static_cast<ICallback*>(m_cb)->setSwapchainRecreator(this);
240-
return true;
241-
}
242-
243210
//
244211
inline bool recreateSwapchain(const uint32_t w=0, const uint32_t h=0)
245212
{
@@ -285,7 +252,7 @@ class NBL_API2 ISmoothResizeSurface : public ISimpleManagedSurface
285252
if (newSwapchain)
286253
{
287254
swapchainResources->invalidate();
288-
return createSwapchainResources()->onCreateSwapchain(getAssignedQueue()->getFamilyIndex(),std::move(newSwapchain));
255+
return swapchainResources->onCreateSwapchain(getAssignedQueue()->getFamilyIndex(),std::move(newSwapchain));
289256
}
290257
else
291258
{
@@ -403,18 +370,16 @@ class NBL_API2 ISmoothResizeSurface : public ISimpleManagedSurface
403370
return ISimpleManagedSurface::present(imageIx,{waitSemaphores+1,1});
404371
}
405372

406-
// Assume it will execute under a mutex
407-
virtual ISwapchainResources* createSwapchainResources() = 0;
408-
409373
// Because the surface can start minimized (extent={0,0}) we might not be able to create the swapchain right away, so store creation parameters until we can create it.
410374
ISwapchain::SSharedCreationParams m_sharedParams = {};
411375

412-
private:
376+
protected:
413377
// Have to use a second semaphore to make acquire-present pairs independent of each other, also because there can be no ordering ensured between present->acquire
414378
core::smart_refctd_ptr<ISemaphore> m_presentSemaphore;
415379
// Command Buffers for blitting/copying the Triple Buffers to Swapchain Images
416380
std::array<core::smart_refctd_ptr<IGPUCommandBuffer>,ISwapchain::MaxImages> m_cmdbufs = {};
417381

382+
private:
418383
// used to protect access to swapchain resources during present and recreateExplicit
419384
std::mutex m_swapchainResourcesMutex;
420385
// Why do we delay the swapchain recreate present until the rendering of the most recent present source is done? Couldn't we present whatever latest Triple Buffer is done?
@@ -451,24 +416,51 @@ class CSmoothResizeSurface final : public ISmoothResizeSurface
451416
return core::smart_refctd_ptr<this_t>(new this_t(std::move(_surface),cb),core::dont_grab);
452417
}
453418

419+
//
420+
inline bool init(CThreadSafeQueueAdapter* queue, std::unique_ptr<SwapchainResources>&& scResources, const ISwapchain::SSharedCreationParams& sharedParams={})
421+
{
422+
// swapchain callback already deinitialized, so no mutex needed here
423+
if (!scResources || !base_init(queue))
424+
return init_fail();
425+
426+
auto device = const_cast<ILogicalDevice*>(queue->getOriginDevice());
427+
428+
m_sharedParams = sharedParams;
429+
if (!m_sharedParams.deduce(device->getPhysicalDevice(),getSurface()))
430+
return init_fail();
431+
432+
// want to keep using the same semaphore throughout the lifetime to not run into sync issues
433+
if (!m_presentSemaphore)
434+
{
435+
m_presentSemaphore = device->createSemaphore(0u);
436+
if (!m_presentSemaphore)
437+
return init_fail();
438+
}
439+
440+
// transient commandbuffer and pool to perform the blits or other copies to SC images
441+
auto pool = device->createCommandPool(queue->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT);
442+
if (!pool || !pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{m_cmdbufs.data(),getMaxFramesInFlight()}))
443+
return init_fail();
444+
445+
m_swapchainResources = std::move(scResources);
446+
447+
setSwapchainRecreator();
448+
return true;
449+
}
450+
454451
protected:
455452
using ISmoothResizeSurface::ISmoothResizeSurface;
456453

457454
inline bool checkQueueFamilyProps(const IPhysicalDevice::SQueueFamilyProperties& props) const override {return props.queueFlags.hasFlags(SwapchainResources::RequiredQueueFlags);}
458455

459-
// All of the below are called under a mutex
460-
inline ISwapchainResources* createSwapchainResources() override
461-
{
462-
m_swapchainResources = core::make_smart_refctd_ptr<SwapchainResources>();
463-
return m_swapchainResources.get();
464-
}
456+
// All of the below are called from under a mutex
465457
inline ISimpleManagedSurface::ISwapchainResources* getSwapchainResources() override {return m_swapchainResources.get();}
466458
inline void becomeIrrecoverable() override {m_swapchainResources = nullptr;}
467459

468460
private:
469461
// As per the above, the swapchain might not be possible to create or recreate right away, so this might be
470462
// either nullptr before the first successful acquire or the old to-be-retired swapchain.
471-
core::smart_refctd_ptr<SwapchainResources> m_swapchainResources = {};
463+
std::unique_ptr<SwapchainResources> m_swapchainResources = {};
472464
};
473465

474466
}

include/nbl/video/utilities/ISimpleManagedSurface.h

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,19 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
103103
// NOTE: Current class doesn't trigger it because it knows nothing about how and when to create or recreate a swapchain.
104104
inline bool onCreateSwapchain(const uint8_t qFam, core::smart_refctd_ptr<ISwapchain>&& _swapchain)
105105
{
106-
swapchain = std::move(_swapchain);
107-
auto device = const_cast<ILogicalDevice*>(swapchain->getOriginDevice());
106+
auto device = const_cast<ILogicalDevice*>(_swapchain->getOriginDevice());
108107
// create images
109-
for (auto i=0u; i<swapchain->getImageCount(); i++)
108+
for (auto i=0u; i<_swapchain->getImageCount(); i++)
110109
{
111-
images[i] = swapchain->createImage(i);
110+
images[i] = _swapchain->createImage(i);
112111
if (!images[i])
113112
{
114113
std::fill_n(images.begin(),i,nullptr);
115114
return false;
116115
}
117116
}
118117

118+
swapchain = std::move(_swapchain);
119119
if (!onCreateSwapchain_impl(qFam))
120120
{
121121
invalidate();
@@ -167,9 +167,26 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
167167
m_acquireSemaphore = 0;
168168
m_acquireCount = 0;
169169
}
170+
171+
//
172+
inline bool irrecoverable() const {return !const_cast<ISimpleManagedSurface*>(this)->getSwapchainResources();}
173+
174+
//
175+
inline CThreadSafeQueueAdapter* getAssignedQueue() const {return m_queue;}
176+
177+
// An interesting solution to the "Frames In Flight", our tiny wrapper class will have its own Timeline Semaphore incrementing with each acquire, and thats it.
178+
inline uint64_t getAcquireCount() {return m_acquireCount;}
179+
inline ISemaphore* getAcquireSemaphore() {return m_acquireSemaphore.get();}
180+
181+
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
182+
inline ISimpleManagedSurface(core::smart_refctd_ptr<ISurface>&& _surface, ICallback* _cb) : m_surface(std::move(_surface)), m_cb(_cb) {}
183+
virtual inline ~ISimpleManagedSurface() = default;
184+
185+
virtual inline bool checkQueueFamilyProps(const IPhysicalDevice::SQueueFamilyProperties& props) const {return true;}
170186

171187
// We need to defer the swapchain creation till the Physical Device is chosen and Queues are created together with the Logical Device
172-
inline bool init(CThreadSafeQueueAdapter* queue, const ISwapchain::SSharedCreationParams& sharedParams={})
188+
// Generally you should have a regular `init` in the final derived class to call this
189+
inline bool base_init(CThreadSafeQueueAdapter* queue)
173190
{
174191
deinit();
175192
if (queue)
@@ -191,33 +208,20 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
191208
{
192209
m_acquireSemaphore = device->createSemaphore(0u);
193210
if (m_acquireSemaphore)
194-
{
195-
if (init_impl(sharedParams))
196-
return true;
197-
}
211+
return true;
198212
}
199213
}
200214
}
215+
return init_fail();
216+
}
217+
218+
// just a simple convenience wrapper
219+
inline bool init_fail()
220+
{
201221
deinit();
202222
return false;
203223
}
204224

205-
//
206-
inline bool irrecoverable() const {return !const_cast<ISimpleManagedSurface*>(this)->getSwapchainResources();}
207-
208-
//
209-
inline CThreadSafeQueueAdapter* getAssignedQueue() const {return m_queue;}
210-
211-
// An interesting solution to the "Frames In Flight", our tiny wrapper class will have its own Timeline Semaphore incrementing with each acquire, and thats it.
212-
inline uint64_t getAcquireCount() {return m_acquireCount;}
213-
inline ISemaphore* getAcquireSemaphore() {return m_acquireSemaphore.get();}
214-
215-
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
216-
inline ISimpleManagedSurface(core::smart_refctd_ptr<ISurface>&& _surface, ICallback* _cb) : m_surface(std::move(_surface)), m_cb(_cb) {}
217-
virtual inline ~ISimpleManagedSurface() = default;
218-
219-
virtual inline bool checkQueueFamilyProps(const IPhysicalDevice::SQueueFamilyProperties& props) const {return true;}
220-
221225
// RETURNS: `ISwapchain::MaxImages` on failure, otherwise its the acquired image's index.
222226
inline uint8_t acquireNextImage()
223227
{
@@ -305,9 +309,6 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
305309
//
306310
virtual void deinit_impl() = 0;
307311

308-
// Generally used to check that per-swapchain resources can be created (including the swapchain itself)
309-
virtual bool init_impl(const ISwapchain::SSharedCreationParams& sharedParams) = 0;
310-
311312
//
312313
ICallback* const m_cb = nullptr;
313314

0 commit comments

Comments
 (0)