Skip to content

Commit 0d808a2

Browse files
Implement the immediateBlit and fix some errors in the handling of OUT_OF_DATE on acquireNextImage
1 parent 286b3b5 commit 0d808a2

File tree

2 files changed

+114
-45
lines changed

2 files changed

+114
-45
lines changed

include/nbl/video/utilities/ISimpleManagedSurface.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
180180
inline uint64_t getAcquireCount() {return m_acquireCount;}
181181
inline ISemaphore* getAcquireSemaphore() {return m_acquireSemaphore.get();}
182182

183+
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
184+
inline ISimpleManagedSurface(core::smart_refctd_ptr<ISurface>&& _surface, ICallback* _cb) : m_surface(std::move(_surface)), m_cb(_cb) {}
185+
virtual inline ~ISimpleManagedSurface() = default;
186+
183187
// RETURNS: Negative on failure, otherwise its the acquired image's index.
184188
inline int8_t acquireNextImage()
185189
{
@@ -224,6 +228,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
224228
assert(false); // shouldn't happen though cause we use uint64_t::max() as the timeout
225229
break;
226230
case ISwapchain::ACQUIRE_IMAGE_RESULT::OUT_OF_DATE:
231+
getSwapchainResources().invalidate();
227232
// try again, will re-create swapchain
228233
{
229234
const int8_t retval = handleOutOfDate();
@@ -241,7 +246,7 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
241246
// Frame Resources are not optional, shouldn't be null!
242247
inline bool present(const uint8_t imageIndex, const std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> waitSemaphores, core::smart_refctd_ptr<core::IReferenceCounted>&& frameResources)
243248
{
244-
if (getSwapchainResources().getStatus()!=ISwapchainResources::STATUS::USABLE || waitSemaphores.empty() || !frameResources)
249+
if (getSwapchainResources().getStatus()!=ISwapchainResources::STATUS::USABLE || !frameResources)
245250
return false;
246251

247252
const ISwapchain::SPresentInfo info = {
@@ -264,18 +269,14 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted
264269
return false;
265270
}
266271

267-
// Utility function for more complex Managed Surfaces, it does not increase the `m_acquireCount` but does acquire and present immediately
268272
using image_barrier_t = IGPUCommandBuffer::SImageMemoryBarrier<IGPUCommandBuffer::SOwnershipTransferBarrier>;
269-
bool immediateBlit(const image_barrier_t& contents, IQueue* blitQueue=nullptr);
270-
271-
protected:
272-
inline ISimpleManagedSurface(core::smart_refctd_ptr<ISurface>&& _surface, ICallback* _cb) : m_surface(std::move(_surface)), m_cb(_cb) {}
273-
virtual inline ~ISimpleManagedSurface() = default;
273+
// Utility function for more complex Managed Surfaces, it does not increase the `m_acquireCount` but does acquire and present immediately
274+
bool immediateBlit(const image_barrier_t& contents, const IQueue::SSubmitInfo::SSemaphoreInfo& waitBeforeBlit, CThreadSafeQueueAdapter* blitAndPresentQueue);
274275

275276
virtual ISwapchainResources& getSwapchainResources() = 0;
276277

277278
// generally used to check that per-swapchain resources can be created (including the swapchain itself)
278-
virtual bool init_impl(IQueue* queue, const ISwapchain::SSharedCreationParams& sharedParams) = 0;
279+
virtual bool init_impl(CThreadSafeQueueAdapter* queue, const ISwapchain::SSharedCreationParams& sharedParams) = 0;
279280

280281
// handlers for acquisition exceptions
281282
virtual bool handleNotReady() = 0;

src/nbl/video/utilities/ISimpleManagedSurface.cpp

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,43 @@ using namespace nbl;
44
using namespace video;
55

66

7-
bool ISimpleManagedSurface::immediateBlit(const image_barrier_t& contents, IQueue* blitQueue)
7+
bool ISimpleManagedSurface::immediateBlit(const image_barrier_t& contents, const IQueue::SSubmitInfo::SSemaphoreInfo& waitBeforeBlit, CThreadSafeQueueAdapter* blitAndPresentQueue)
88
{
9-
if (!contents.image || !m_queue)
9+
auto& swapchainResources = getSwapchainResources();
10+
if (!contents.image || swapchainResources.getStatus()!=ISwapchainResources::STATUS::USABLE)
1011
return false;
1112

12-
auto device = const_cast<ILogicalDevice*>(m_queue->getOriginDevice());
13-
const auto qFamProps = device->getPhysicalDevice()->getQueueFamilyProperties();
14-
if (!blitQueue)
13+
auto* swapchain = swapchainResources.getSwapchain();
14+
assert(swapchain); // because status is usable
15+
auto device = const_cast<ILogicalDevice*>(swapchain->getOriginDevice());
16+
17+
// check queue provided
1518
{
16-
// default to using the presentation queue if it can blit
17-
if (qFamProps[m_queue->getFamilyIndex()].queueFlags.hasFlags(IQueue::FAMILY_FLAGS::GRAPHICS_BIT))
18-
blitQueue = m_queue;
19-
else // just pick first compatible
20-
for (uint8_t qFam=0; qFam<ILogicalDevice::MaxQueueFamilies; qFam++)
21-
if (device->getQueueCount(qFam) && qFamProps[qFam].queueFlags.hasFlags(IQueue::FAMILY_FLAGS::GRAPHICS_BIT))
19+
const auto qFamProps = device->getPhysicalDevice()->getQueueFamilyProperties();
20+
auto compatibleQueue = [&](const uint8_t qFam)->bool
2221
{
23-
blitQueue = device->getThreadSafeQueue(qFam,0);
24-
break;
22+
return qFamProps[qFam].queueFlags.hasFlags(IQueue::FAMILY_FLAGS::GRAPHICS_BIT) && m_surface->isSupportedForPhysicalDevice(device->getPhysicalDevice(),qFam);
23+
};
24+
// pick if default wanted
25+
if (!blitAndPresentQueue)
26+
{
27+
for (uint8_t qFam=0; qFam<ILogicalDevice::MaxQueueFamilies; qFam++)
28+
{
29+
const auto qCount = device->getQueueCount(qFam);
30+
if (qCount && qFamProps[qFam].queueFlags.hasFlags(IQueue::FAMILY_FLAGS::GRAPHICS_BIT))
31+
{
32+
// pick a different queue than we'd pick for a regular present
33+
blitAndPresentQueue = device->getThreadSafeQueue(qFam,0);
34+
if (blitAndPresentQueue==m_queue)
35+
blitAndPresentQueue = device->getThreadSafeQueue(qFam,qCount-1);
36+
break;
37+
}
38+
}
2539
}
26-
}
2740

28-
if (!blitQueue || qFamProps[blitQueue->getFamilyIndex()].queueFlags.hasFlags(IQueue::FAMILY_FLAGS::GRAPHICS_BIT))
29-
return false;
41+
if (!blitAndPresentQueue || compatibleQueue(blitAndPresentQueue->getFamilyIndex()))
42+
return false;
43+
}
3044

3145
// create a different semaphore so we don't increase the acquire counter in `this`
3246
auto semaphore = device->createSemaphore(0);
@@ -36,26 +50,42 @@ bool ISimpleManagedSurface::immediateBlit(const image_barrier_t& contents, IQueu
3650
// transient commandbuffer and pool to perform the blit
3751
core::smart_refctd_ptr<IGPUCommandBuffer> cmdbuf;
3852
{
39-
auto pool = device->createCommandPool(blitQueue->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::TRANSIENT_BIT);
53+
auto pool = device->createCommandPool(blitAndPresentQueue->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::TRANSIENT_BIT);
4054
if (!pool || !pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{&cmdbuf,1}) || !cmdbuf)
4155
return false;
56+
57+
if (!cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT))
58+
return false;
4259
}
4360

44-
const IQueue::SSubmitInfo::SSemaphoreInfo acquired[1] = {
45-
{
46-
.semaphore=semaphore.get(),
47-
.value=1
48-
}
61+
const IQueue::SSubmitInfo::SSemaphoreInfo acquired = {
62+
.semaphore=semaphore.get(),
63+
.value=1
4964
};
5065
// acquire
51-
;
52-
53-
// now record the blit commands
54-
auto acquiredImage = getSwapchainResources().getImage(0xffu);
66+
uint32_t imageIndex;
67+
switch (swapchain->acquireNextImage({.queue=blitAndPresentQueue,.signalSemaphores={&acquired,1}},&imageIndex))
5568
{
56-
if (!cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT))
69+
case ISwapchain::ACQUIRE_IMAGE_RESULT::SUBOPTIMAL: [[fallthrough]];
70+
case ISwapchain::ACQUIRE_IMAGE_RESULT::SUCCESS:
71+
break;
72+
case ISwapchain::ACQUIRE_IMAGE_RESULT::TIMEOUT: [[fallthrough]];
73+
case ISwapchain::ACQUIRE_IMAGE_RESULT::NOT_READY: // don't throw our swapchain away just because of a timeout XD
74+
assert(false); // shouldn't happen though cause we use uint64_t::max() as the timeout
75+
return false;
76+
case ISwapchain::ACQUIRE_IMAGE_RESULT::OUT_OF_DATE:
77+
swapchainResources.invalidate();
78+
return false;
79+
default:
80+
swapchainResources.becomeIrrecoverable();
5781
return false;
82+
}
83+
// once image is acquired, WE HAVE TO present it
84+
bool retval = true;
5885

86+
// now record the blit commands
87+
auto acquiredImage = swapchainResources.getImage(imageIndex);
88+
{
5989
const auto blitSrcLayout = IGPUImage::LAYOUT::TRANSFER_SRC_OPTIMAL;
6090
const auto blitDstLayout = IGPUImage::LAYOUT::TRANSFER_DST_OPTIMAL;
6191
IGPUCommandBuffer::SPipelineBarrierDependencyInfo depInfo = {};
@@ -100,8 +130,7 @@ bool ISimpleManagedSurface::immediateBlit(const image_barrier_t& contents, IQueu
100130
}
101131
};
102132
depInfo.imgBarriers = preBarriers;
103-
if (!cmdbuf->pipelineBarrier(asset::EDF_NONE,depInfo))
104-
return false;
133+
retval &= cmdbuf->pipelineBarrier(asset::EDF_NONE,depInfo);
105134

106135
// TODO: Implement scaling modes other than plain STRETCH, and allow for using subrectangles of the initial contents
107136
{
@@ -117,8 +146,7 @@ bool ISimpleManagedSurface::immediateBlit(const image_barrier_t& contents, IQueu
117146
.dstBaseLayer = 0,
118147
.srcMipLevel = 0 // TODO
119148
}};
120-
if (!cmdbuf->blitImage(contents.image,blitSrcLayout,acquiredImage,blitDstLayout,regions,IGPUSampler::ETF_LINEAR))
121-
return false;
149+
retval &= cmdbuf->blitImage(contents.image,blitSrcLayout,acquiredImage,blitDstLayout,regions,IGPUSampler::ETF_LINEAR);
122150
}
123151

124152
// barrier after
@@ -148,14 +176,54 @@ bool ISimpleManagedSurface::immediateBlit(const image_barrier_t& contents, IQueu
148176
}
149177
};
150178
depInfo.imgBarriers = postBarriers;
151-
if (!cmdbuf->pipelineBarrier(asset::EDF_NONE,depInfo))
152-
return false;
179+
retval &= cmdbuf->pipelineBarrier(asset::EDF_NONE,depInfo);
180+
181+
retval &= cmdbuf->end();
153182
}
154183

184+
const IQueue::SSubmitInfo::SSemaphoreInfo blitted[1] = {
185+
{
186+
.semaphore = semaphore.get(),
187+
.value = 2,
188+
.stageMask = asset::PIPELINE_STAGE_FLAGS::BLIT_BIT
189+
}
190+
};
155191
// submit
192+
{
193+
const IQueue::SSubmitInfo::SSemaphoreInfo wait[2] = {acquired,waitBeforeBlit};
194+
const IQueue::SSubmitInfo::SCommandBufferInfo cmdbufs[1] = {{.cmdbuf=cmdbuf.get()}};
195+
IQueue::SSubmitInfo infos[1] = {
196+
{
197+
.waitSemaphores = wait,
198+
.commandBuffers = cmdbufs,
199+
.signalSemaphores = blitted
200+
}
201+
};
202+
retval &= blitAndPresentQueue->submit(infos)==IQueue::RESULT::SUCCESS;
203+
}
156204

157-
// present
158-
;
159-
160-
return true;
205+
// present
206+
switch (swapchainResources.swapchain->present({.queue=blitAndPresentQueue,.imgIndex=imageIndex,.waitSemaphores=blitted},std::move(cmdbuf)))
207+
{
208+
case ISwapchain::PRESENT_RESULT::SUBOPTIMAL: [[fallthrough]];
209+
case ISwapchain::PRESENT_RESULT::SUCCESS:
210+
// all resources can be dropped, the swapchain will hold onto them
211+
return retval;
212+
case ISwapchain::PRESENT_RESULT::OUT_OF_DATE:
213+
swapchainResources.invalidate();
214+
break;
215+
default:
216+
swapchainResources.becomeIrrecoverable();
217+
break;
218+
}
219+
// swapchain won't hold onto anything, so just block till resources not used anymore
220+
if (retval) // only if queue has submitted you have anything to wait on
221+
{
222+
ISemaphore::SWaitInfo infos[1] = {{
223+
.semaphore = blitted[0].semaphore,
224+
.value = blitted[0].value
225+
}};
226+
device->blockForSemaphores(infos);
227+
}
228+
return false;
161229
}

0 commit comments

Comments
 (0)