Skip to content

Commit 8c69d10

Browse files
Add source image rect and wrap it up in a SPresentSource struct, deal with and document queue family ownership transfer (untested though!)
1 parent 891e85b commit 8c69d10

File tree

2 files changed

+53
-17
lines changed

2 files changed

+53
-17
lines changed

include/nbl/video/utilities/CResizableSurface.h

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,25 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
3232
IResizableSurface* m_recreator = nullptr;
3333
};
3434

35+
//
36+
struct SPresentSource
37+
{
38+
IGPUImage* image;
39+
VkRect2D rect;
40+
};
41+
3542
//
3643
class NBL_API2 ISwapchainResources : public core::IReferenceCounted, public ISimpleManagedSurface::ISwapchainResources
3744
{
3845
protected:
3946
friend class IResizableSurface;
4047

41-
// The `cmdbuf` is already begun when given to the callback, and will be ended outside.
4248
// 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;
4554
};
4655

4756
//
@@ -51,22 +60,49 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
5160
return device->getThreadSafeQueue(fam,device->getQueueCount(fam)-1);
5261
}
5362

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+
5487
struct SPresentInfo
5588
{
56-
inline operator bool() const {return source;}
89+
inline operator bool() const {return source.image;}
5790

58-
IGPUImage* source = nullptr;
59-
// TODO: add sourceRegion
91+
SPresentSource source;
92+
uint8_t mostRecentFamilyOwningSource;
6093
// only allow waiting for one semaphore, because there's only one source to present!
6194
IQueue::SSubmitInfo::SSemaphoreInfo wait;
6295
core::IReferenceCounted* frameResources;
6396
};
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!
65100
inline bool present(const SPresentInfo& presentInfo)
66101
{
67102
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);
70106
}
71107

72108
// Call this when you want to recreate the swapchain with new extents
@@ -90,9 +126,9 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
90126
if (current->getSwapchain()==oldSwapchain.get())
91127
return true;
92128

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);
96132
}
97133

98134
protected:
@@ -221,7 +257,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
221257
}
222258

223259
//
224-
inline bool present_impl(const SPresentInfo& presentInfo)
260+
inline bool present_impl(const SPresentInfo& presentInfo, const bool acquireOwnership)
225261
{
226262
// irrecoverable or bad input
227263
if (!presentInfo || !getSwapchainResources())
@@ -250,7 +286,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
250286
.stageMask = asset::PIPELINE_STAGE_FLAGS::NONE // presentation engine usage isn't a stage
251287
}
252288
};
253-
m_lastPresentSourceImage = core::smart_refctd_ptr<IGPUImage>(presentInfo.source);
289+
m_lastPresentSourceImage = core::smart_refctd_ptr<IGPUImage>(presentInfo.source.image);
254290
m_lastPresentSemaphore = core::smart_refctd_ptr<ISemaphore>(presentInfo.wait.semaphore);
255291
m_lastPresentSource = presentInfo.source;
256292
m_lastPresentWait = presentInfo.wait;
@@ -284,7 +320,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
284320
.semaphore = m_blitSemaphore.get(),
285321
.value = acquireCount,
286322
// 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)
288324
}
289325
};
290326
willBlit &= bool(blitted[1].stageMask.value);
@@ -328,7 +364,7 @@ class NBL_API2 IResizableSurface : public ISimpleManagedSurface
328364
// No because there can be presents enqueued whose wait semaphores have not signalled yet, meaning there could be images presented in the future.
329365
// 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.
330366
IQueue::SSubmitInfo::SSemaphoreInfo m_lastPresentWait = {};
331-
decltype(SPresentInfo::source) m_lastPresentSource = {};
367+
SPresentSource m_lastPresentSource = {};
332368
core::smart_refctd_ptr<ISemaphore> m_lastPresentSemaphore = {};
333369
core::smart_refctd_ptr<IGPUImage> m_lastPresentSourceImage = {};
334370
};

0 commit comments

Comments
 (0)