Skip to content

Commit c5a3ccc

Browse files
committed
Merge branch 'master' into ngfx-sdk-integration
2 parents 7df72fe + 123a17b commit c5a3ccc

File tree

6 files changed

+88
-52
lines changed

6 files changed

+88
-52
lines changed

include/nbl/video/CVulkanSwapchain.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@ class CVulkanSwapchain final : public ISwapchain
2020

2121
void setObjectDebugName(const char* label) const override;
2222

23-
inline const void* getNativeHandle() const {return &m_vkSwapchainKHR;}
23+
inline const void* getNativeHandle() const override {return &m_vkSwapchainKHR;}
2424
inline VkSwapchainKHR getInternalObject() const {return m_vkSwapchainKHR;}
25+
26+
// returns the maximum number of time acquire can be called without releasing the image index through present.
27+
inline uint8_t getMaxBlockingAcquiresBeforePresent() const override { return m_maxBlockingAcquiresBeforePresent; }
28+
29+
// returns the maximum number of acquires you can request without waiting for previous acquire semaphores to signal.
30+
uint8_t getMaxAcquiresInFlight() const override { return getImageCount(); }
2531

2632
private:
2733
CVulkanSwapchain(
@@ -32,7 +38,8 @@ class CVulkanSwapchain final : public ISwapchain
3238
const VkSwapchainKHR swapchain,
3339
const VkSemaphore* const _acquireAdaptorSemaphores,
3440
const VkSemaphore* const _prePresentSemaphores,
35-
const VkSemaphore* const _presentAdaptorSemaphores
41+
const VkSemaphore* const _presentAdaptorSemaphores,
42+
const uint8_t maxAcquiresBeforePresent
3643
);
3744
~CVulkanSwapchain();
3845

@@ -51,6 +58,7 @@ class CVulkanSwapchain final : public ISwapchain
5158
uint64_t m_perImageAcquireCount[ISwapchain::MaxImages] = { 0 };
5259
// nasty way to fight UB of the Vulkan spec
5360
bool m_needToWaitIdle = true;
61+
uint8_t m_maxBlockingAcquiresBeforePresent = 0u;
5462
};
5563

5664
}

include/nbl/video/ISwapchain.h

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -464,15 +464,21 @@ class ISwapchain : public IBackendObject
464464
// Vulkan: const VkSwapchainKHR*
465465
virtual const void* getNativeHandle() const = 0;
466466

467-
// only public because MultiTimelineEventHandlerST needs to know about it
468-
class DeferredFrameSemaphoreDrop final
469-
{
467+
// returns the maximum number of time acquires with infinite timeout which can be called before releasing the image index through present.
468+
virtual uint8_t getMaxBlockingAcquiresBeforePresent() const = 0u;
469+
470+
// returns the maximum number of acquires you can request without waiting for previous acquire semaphores to signal.
471+
virtual uint8_t getMaxAcquiresInFlight() const = 0u;
472+
473+
// only public because MultiTimelineEventHandlerST needs to know about it
474+
class DeferredFrameSemaphoreDrop final
475+
{
470476
using sema_refctd_ptr = core::smart_refctd_ptr<ISemaphore>;
471-
core::smart_refctd_dynamic_array<sema_refctd_ptr> m_otherSemaphores = nullptr;
477+
core::smart_refctd_dynamic_array<sema_refctd_ptr> m_otherSemaphores = nullptr;
472478

473-
public:
474-
inline DeferredFrameSemaphoreDrop(const std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> _semaphores)
475-
{
479+
public:
480+
inline DeferredFrameSemaphoreDrop(const std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> _semaphores)
481+
{
476482
const auto otherCount = _semaphores.size()-1;
477483
// first semaphore always serves as the timeline and will be refcounted in the event handler
478484
if (otherCount==0)
@@ -481,34 +487,34 @@ class ISwapchain : public IBackendObject
481487

482488
for (auto i=0ull; i<otherCount; i++)
483489
m_otherSemaphores->operator[](i) = sema_refctd_ptr(_semaphores[i].semaphore);
484-
}
490+
}
485491
DeferredFrameSemaphoreDrop(const DeferredFrameSemaphoreDrop& other) = delete;
486-
inline DeferredFrameSemaphoreDrop(DeferredFrameSemaphoreDrop&& other) : m_otherSemaphores(nullptr)
487-
{
488-
this->operator=(std::move(other));
489-
}
492+
inline DeferredFrameSemaphoreDrop(DeferredFrameSemaphoreDrop&& other) : m_otherSemaphores(nullptr)
493+
{
494+
this->operator=(std::move(other));
495+
}
490496

491497
DeferredFrameSemaphoreDrop& operator=(const DeferredFrameSemaphoreDrop& other) = delete;
492-
inline DeferredFrameSemaphoreDrop& operator=(DeferredFrameSemaphoreDrop&& other)
493-
{
498+
inline DeferredFrameSemaphoreDrop& operator=(DeferredFrameSemaphoreDrop&& other)
499+
{
494500
m_otherSemaphores = std::move(other.m_otherSemaphores);
495-
other.m_otherSemaphores = nullptr;
496-
return *this;
497-
}
498-
499-
struct single_poll_t {};
500-
static inline single_poll_t single_poll;
501-
inline bool operator()(single_poll_t _single_poll)
502-
{
503-
operator()();
504-
return true;
505-
}
501+
other.m_otherSemaphores = nullptr;
502+
return *this;
503+
}
504+
505+
struct single_poll_t {};
506+
static inline single_poll_t single_poll;
507+
inline bool operator()(single_poll_t _single_poll)
508+
{
509+
operator()();
510+
return true;
511+
}
506512

507513
inline void operator()()
508514
{
509515
m_otherSemaphores = nullptr;
510516
}
511-
};
517+
};
512518

513519
protected:
514520
ISwapchain(core::smart_refctd_ptr<const ILogicalDevice>&& dev, SCreationParams&& params, const uint8_t imageCount, core::smart_refctd_ptr<ISwapchain>&& oldSwapchain);

include/nbl/video/utilities/CSmoothResizeSurface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ class NBL_API2 ISmoothResizeSurface : public ISimpleManagedSurface
336336
auto device = const_cast<ILogicalDevice*>(queue->getOriginDevice());
337337

338338
// need to wait before resetting a commandbuffer
339-
const auto maxFramesInFlight = getMaxFramesInFlight();
339+
const auto maxFramesInFlight = getMaxAcquiresInFlight();
340340
if (acquireCount>maxFramesInFlight)
341341
{
342342
const ISemaphore::SWaitInfo cmdbufDonePending[1] = {
@@ -454,7 +454,7 @@ class CSmoothResizeSurface final : public ISmoothResizeSurface
454454

455455
// transient commandbuffer and pool to perform the blits or other copies to SC images
456456
auto pool = device->createCommandPool(queue->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT);
457-
if (!pool || !pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{m_cmdbufs.data(),getMaxFramesInFlight()}))
457+
if (!pool || !pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{m_cmdbufs.data(),getMaxAcquiresInFlightUpperLimit()}))
458458
return init_fail();
459459

460460
m_swapchainResources = std::move(scResources);

include/nbl/video/utilities/ISimpleManagedSurface.h

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,22 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
3939
//
4040
inline ISurface* getSurface() {return m_surface.get();}
4141
inline const ISurface* getSurface() const {return m_surface.get();}
42+
4243

43-
//
44-
inline uint8_t getMaxFramesInFlight() const {return m_maxFramesInFlight;}
44+
// returns the maximum number of acquires you can request without waiting for previous acquire semaphores to signal.
45+
// this value is dynamic, so don't think about caching this value, this might change on swapchain recreation.
46+
inline uint8_t getMaxAcquiresInFlight()
47+
{
48+
auto swapchainResources = getSwapchainResources();
49+
if (swapchainResources && swapchainResources->swapchain)
50+
return core::min(swapchainResources->swapchain->getMaxAcquiresInFlight(), m_maxImageCount);
51+
return m_maxImageCount;
52+
}
53+
54+
// returns upper limit of maximum acquires in flight returned in `getMaxAcquiresInFlight()`.
55+
// use this to allocate your command buffers or per frame in flight resources
56+
// call this function after initilization.
57+
inline uint8_t getMaxAcquiresInFlightUpperLimit() const { assert(m_maxImageCount > 0); return m_maxImageCount; }
4558

4659
// A small utility for the boilerplate
4760
inline uint8_t pickQueueFamily(ILogicalDevice* device) const
@@ -166,7 +179,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
166179
m_queue->waitIdle();
167180

168181
m_queue = nullptr;
169-
m_maxFramesInFlight = 0;
182+
m_maxImageCount = 0;
170183
m_acquireCount = 0;
171184
std::fill(m_acquireSemaphores.begin(),m_acquireSemaphores.end(),nullptr);
172185
}
@@ -179,7 +192,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
179192

180193
// 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.
181194
inline uint64_t getAcquireCount() {return m_acquireCount;}
182-
inline ISemaphore* getAcquireSemaphore(const uint8_t ix) {return ix<m_maxFramesInFlight ? m_acquireSemaphores[ix].get():nullptr;}
195+
inline ISemaphore* getAcquireSemaphore(const uint8_t ix) {return ix<m_maxImageCount ? m_acquireSemaphores[ix].get():nullptr;}
183196

184197
// What `acquireNextImage` returns
185198
struct SAcquireResult
@@ -213,11 +226,10 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
213226
{
214227
ISurface::SCapabilities caps = {};
215228
m_surface->getSurfaceCapabilitiesForPhysicalDevice(physDev,caps);
216-
// vkAcquireNextImageKHR should not be called if the number of images that the application has currently acquired is greater than SwapchainImages-MinimumImages
217-
m_maxFramesInFlight = core::min(caps.maxImageCount+1-caps.minImageCount,ISwapchain::MaxImages);
229+
m_maxImageCount = core::min(caps.maxImageCount,ISwapchain::MaxImages);
218230
}
219231

220-
for (uint8_t i=0u; i<m_maxFramesInFlight; i++)
232+
for (uint8_t i=0u; i<m_maxImageCount; i++)
221233
{
222234
m_acquireSemaphores[i] = device->createSemaphore(0u);
223235
if (!m_acquireSemaphores[i])
@@ -251,7 +263,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
251263

252264
const auto nextAcquireSignal = m_acquireCount+1;
253265
SAcquireResult retval = {
254-
.semaphore = m_acquireSemaphores[nextAcquireSignal%m_maxFramesInFlight].get(),
266+
.semaphore = m_acquireSemaphores[nextAcquireSignal%m_maxImageCount].get(),
255267
.acquireCount = nextAcquireSignal
256268
};
257269
const IQueue::SSubmitInfo::SSemaphoreInfo signalInfos[1] = {
@@ -344,12 +356,13 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
344356

345357
// Use a Threadsafe queue to make sure we can do smooth resize in derived class, might get re-assigned
346358
CThreadSafeQueueAdapter* m_queue = nullptr;
347-
//
348-
uint8_t m_maxFramesInFlight = 0;
359+
360+
uint8_t m_maxImageCount = 0;
361+
349362
// Created and persistent after first initialization, Note that we need one swapchain per Frame In Fligth because Acquires can't wait or synchronize with anything
350-
// The only rule is that you can only have `m_maxFramesInFlight` pending acquires to wait with an infinte timeout, so thats about as far as they synchronize.
351363
std::array<core::smart_refctd_ptr<ISemaphore>,ISwapchain::MaxImages> m_acquireSemaphores;
352-
// You don't want to use `m_swapchainResources.swapchain->getAcquireCount()` because it resets when swapchain gets recreated
364+
365+
// We shouldn't use `m_swapchainResources.swapchain->getAcquireCount()` because it resets when swapchain gets recreated
353366
uint64_t m_acquireCount = 0;
354367
};
355368

src/nbl/video/CVulkanSwapchain.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ core::smart_refctd_ptr<CVulkanSwapchain> CVulkanSwapchain::create(core::smart_re
1313
return nullptr;
1414
if (!params.surface)
1515
return nullptr;
16+
17+
auto physDev = logicalDevice->getPhysicalDevice();
1618

1719
// now check if any queue family of the physical device supports this surface
1820
{
19-
auto physDev = logicalDevice->getPhysicalDevice();
20-
2121
bool noSupport = true;
2222
for (auto familyIx=0u; familyIx<ILogicalDevice::MaxQueueFamilies; familyIx++)
2323
if (logicalDevice->getQueueCount(familyIx) && params.surface->isSupportedForPhysicalDevice(physDev,familyIx))
@@ -148,7 +148,12 @@ core::smart_refctd_ptr<CVulkanSwapchain> CVulkanSwapchain::create(core::smart_re
148148
return nullptr;
149149
}
150150

151-
return core::smart_refctd_ptr<CVulkanSwapchain>(new CVulkanSwapchain(std::move(device),std::move(params),imageCount,std::move(oldSwapchain),vk_swapchain,semaphores+imageCount,semaphores,semaphores+2*imageCount),core::dont_grab);
151+
ISurface::SCapabilities caps = {};
152+
params.surface->getSurfaceCapabilitiesForPhysicalDevice(physDev, caps);
153+
// read https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#swapchain-acquire-forward-progress
154+
const uint8_t maxAcquiresWithoutPresent = core::min(imageCount,ISwapchain::MaxImages) - caps.minImageCount + 1u;
155+
156+
return core::smart_refctd_ptr<CVulkanSwapchain>(new CVulkanSwapchain(std::move(device),std::move(params),imageCount,std::move(oldSwapchain),vk_swapchain,semaphores+imageCount,semaphores,semaphores+2*imageCount, maxAcquiresWithoutPresent),core::dont_grab);
152157
}
153158

154159
CVulkanSwapchain::CVulkanSwapchain(
@@ -159,9 +164,10 @@ CVulkanSwapchain::CVulkanSwapchain(
159164
const VkSwapchainKHR swapchain,
160165
const VkSemaphore* const _acquireAdaptorSemaphores,
161166
const VkSemaphore* const _prePresentSemaphores,
162-
const VkSemaphore* const _presentAdaptorSemaphores
163-
) : ISwapchain(std::move(logicalDevice),std::move(params),imageCount,std::move(oldSwapchain)),
164-
m_imgMemRequirements{.size=0,.memoryTypeBits=0x0u,.alignmentLog2=63,.prefersDedicatedAllocation=true,.requiresDedicatedAllocation=true}, m_vkSwapchainKHR(swapchain)
167+
const VkSemaphore* const _presentAdaptorSemaphores,
168+
const uint8_t maxAcquiresBeforePresent)
169+
: ISwapchain(std::move(logicalDevice),std::move(params),imageCount,std::move(oldSwapchain)),
170+
m_imgMemRequirements{.size=0,.memoryTypeBits=0x0u,.alignmentLog2=63,.prefersDedicatedAllocation=true,.requiresDedicatedAllocation=true}, m_vkSwapchainKHR(swapchain), m_maxBlockingAcquiresBeforePresent(maxAcquiresBeforePresent)
165171
{
166172
// we've got it from here!
167173
if (m_oldSwapchain)
@@ -173,10 +179,13 @@ CVulkanSwapchain::CVulkanSwapchain(
173179
auto retval = vulkanDevice->getFunctionTable()->vk.vkGetSwapchainImagesKHR(vulkanDevice->getInternalObject(),m_vkSwapchainKHR,&dummy,m_images);
174180
assert(retval==VK_SUCCESS && dummy==getImageCount());
175181
}
182+
const uint8_t maxAcquiresInFlight = getMaxAcquiresInFlight();
183+
assert(maxAcquiresInFlight == imageCount);
184+
assert(getMaxBlockingAcquiresBeforePresent() <= imageCount);
176185

177-
std::copy_n(_acquireAdaptorSemaphores,imageCount,m_acquireAdaptorSemaphores);
178-
std::copy_n(_prePresentSemaphores,imageCount,m_prePresentSemaphores);
179-
std::copy_n(_presentAdaptorSemaphores,imageCount,m_presentAdaptorSemaphores);
186+
std::copy_n(_acquireAdaptorSemaphores,maxAcquiresInFlight,m_acquireAdaptorSemaphores);
187+
std::copy_n(_prePresentSemaphores,maxAcquiresInFlight,m_prePresentSemaphores);
188+
std::copy_n(_presentAdaptorSemaphores,maxAcquiresInFlight,m_presentAdaptorSemaphores);
180189
}
181190

182191
CVulkanSwapchain::~CVulkanSwapchain()

0 commit comments

Comments
 (0)