Skip to content

Commit 0c91901

Browse files
Correct the big shortcomings of the initial IQueue GC design
1 parent edcdd09 commit 0c91901

File tree

7 files changed

+34
-24
lines changed

7 files changed

+34
-24
lines changed

include/nbl/video/ILogicalDevice.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe
151151
bool validateMemoryBarrier(const uint32_t queueFamilyIndex, const IGPUCommandBuffer::SImageMemoryBarrier<ResourceBarrier>& barrier) const;
152152

153153

154-
//! Sync
155-
virtual IQueue::RESULT waitIdle() const = 0;
154+
//! Very important function, without it being called at the end of when you use a device, it will leak due to circular reference from resources used in the very last submit to a queue
155+
//! Alternatively to get rid of circular refs, you can call `waitIdle` individually on every queue you've ever used
156+
IQueue::RESULT waitIdle();
156157

157158
//! Semaphore Stuff
158159
virtual core::smart_refctd_ptr<ISemaphore> createSemaphore(const uint64_t initialValue) = 0;
@@ -767,10 +768,16 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe
767768
ILogicalDevice(core::smart_refctd_ptr<const IAPIConnection>&& api, const IPhysicalDevice* const physicalDevice, const SCreationParams& params, const bool runningInRenderdoc);
768769
inline virtual ~ILogicalDevice()
769770
{
771+
// There's no point calling `waitIdle` here for two reasons:
772+
// - vtable already destroyed, you'll then call undefined function pointer for `waitIdle_impl`
773+
// - `waitIdle` must have been called already or a similar operation performed, otherwise you'll have circular references from the per-queue GC and you'll never enter this destructor
770774
if (m_queues)
771775
for (uint32_t i=0u; i<m_queues->size(); ++i)
772776
delete (*m_queues)[i];
773777
}
778+
779+
virtual IQueue::RESULT waitIdle_impl() const = 0;
780+
774781
virtual bool flushMappedMemoryRanges_impl(const std::span<const MappedMemoryRange> ranges) = 0;
775782
virtual bool invalidateMappedMemoryRanges_impl(const std::span<const MappedMemoryRange> ranges) = 0;
776783

include/nbl/video/IQueue.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class IQueue : public core::Interface, public core::Unmovable
147147
inline DeferredSubmitResourceDrop& operator=(DeferredSubmitResourceDrop&& other)
148148
{
149149
m_resources = std::move(other.m_resources);
150-
m_resources = nullptr;
150+
other.m_resources = nullptr;
151151
return *this;
152152
}
153153

@@ -160,8 +160,11 @@ class IQueue : public core::Interface, public core::Unmovable
160160

161161
protected:
162162
NBL_API2 IQueue(ILogicalDevice* originDevice, const uint32_t _famIx, const core::bitflag<CREATE_FLAGS> _flags, const float _priority);
163-
// WARNING: Due to ordering of destructors, its the Base Class that implements `waitIdle_impl` that needs to call `waitIdle`!
164-
NBL_API2 ~IQueue();
163+
// As IQueue is logically an integral part of the `ILogicalDevice` the destructor will only run during `~ILogicalDevice` which means `waitIdle` already been called
164+
inline ~IQueue()
165+
{
166+
//while (cullResources()) {} // deleter of `m_submittedResources` calls dtor which will do this
167+
}
165168

166169
friend class CThreadSafeQueueAdapter;
167170
virtual RESULT submit_impl(const std::span<const SSubmitInfo> _submits) = 0;

src/nbl/video/CVulkanLogicalDevice.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,7 @@ class CVulkanLogicalDevice final : public ILogicalDevice
4646

4747
CVulkanLogicalDevice(core::smart_refctd_ptr<const IAPIConnection>&& api, renderdoc_api_t* const rdoc, const IPhysicalDevice* const physicalDevice, const VkDevice vkdev, const SCreationParams& params);
4848

49-
// sync sutff
50-
inline IQueue::RESULT waitIdle() const override
51-
{
52-
return CVulkanQueue::getResultFrom(m_devf.vk.vkDeviceWaitIdle(m_vkdev));
53-
}
54-
49+
// sync stuff
5550
core::smart_refctd_ptr<ISemaphore> createSemaphore(const uint64_t initialValue) override;
5651
ISemaphore::WAIT_RESULT waitForSemaphores(const std::span<const ISemaphore::SWaitInfo> infos, const bool waitAll, const uint64_t timeout) override;
5752

@@ -94,6 +89,12 @@ class CVulkanLogicalDevice final : public ILogicalDevice
9489
m_devf.vk.vkDestroyDescriptorSetLayout(m_vkdev,m_dummyDSLayout,nullptr);
9590
m_devf.vk.vkDestroyDevice(m_vkdev,nullptr);
9691
}
92+
93+
// sync stuff
94+
inline IQueue::RESULT waitIdle_impl() const override
95+
{
96+
return CVulkanQueue::getResultFrom(m_devf.vk.vkDeviceWaitIdle(m_vkdev));
97+
}
9798

9899
// memory stuff
99100
bool flushMappedMemoryRanges_impl(const std::span<const MappedMemoryRange> ranges) override;

src/nbl/video/CVulkanQueue.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ class CVulkanQueue final : public IQueue
4444
inline VkQueue getInternalObject() const {return m_vkQueue;}
4545

4646
private:
47-
inline ~CVulkanQueue()
48-
{
49-
// need to ensure all submits end to stop spinning in the Base Class destructor
50-
waitIdle_impl();
51-
}
52-
5347
RESULT submit_impl(const std::span<const SSubmitInfo> _submits) override;
5448
RESULT waitIdle_impl() const override;
5549

src/nbl/video/ILogicalDevice.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,17 @@ bool ILogicalDevice::validateMemoryBarrier(const uint32_t queueFamilyIndex, asse
237237
return true;
238238
}
239239

240+
241+
IQueue::RESULT ILogicalDevice::waitIdle()
242+
{
243+
const auto retval = waitIdle_impl();
244+
// Since this is equivalent to calling waitIdle on all queues, just proceed to releasing tracked resources
245+
for (auto queue : *m_queues)
246+
queue->cullResources();
247+
return retval;
248+
}
249+
250+
240251
core::smart_refctd_ptr<IGPUBufferView> ILogicalDevice::createBufferView(const asset::SBufferRange<const IGPUBuffer>& underlying, const asset::E_FORMAT _fmt)
241252
{
242253
if (!underlying.isValid() || !underlying.buffer->wasCreatedBy(this))

src/nbl/video/IQueue.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,4 @@ uint32_t IQueue::cullResources()
115115
return m_submittedResources->poll().eventsLeft;
116116
}
117117

118-
IQueue::~IQueue()
119-
{
120-
// if you find yourself spinning a lot here, its because the base class hasn't called waitIdle_impl()
121-
//while (cullResources()) {} // deleter of `m_submittedResources` calls dtor which will do this
122-
}
123-
124118
}

0 commit comments

Comments
 (0)