Skip to content

Commit 3d28442

Browse files
committed
Merge branch 'intended_submit_info_changes' into cad_large_texture_streaming
2 parents 2f43bba + 650b981 commit 3d28442

File tree

1 file changed

+64
-37
lines changed

1 file changed

+64
-37
lines changed

include/nbl/video/utilities/SIntendedSubmitInfo.h

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ namespace nbl::video
1111
//! Struct meant to be used with any Utility (not just `IUtilities`) which exhibits "submit on overflow" behaviour.
1212
//! Such functions are non-blocking (unless overflow) and take `SIntendedSubmitInfo` by reference and patch it accordingly.
1313
//! for example: in the case the `waitSemaphores` were already waited upon, the struct will be modified to have it's `waitSemaphores` emptied.
14-
//! MAKE SURE to do a submit to `queue` by yourself with the result of `popSubmit(...)` implicitly converted to `std::span<const IQueue::SSubmitInfo>` !
14+
//! User is supposed to begin EXACTLY ONE of the `scratchCommandBuffer` before first ever submit.
15+
//! Never `begin` or `end` scratch commandBuffers ever again.
16+
//! Always assume the current recording commandBuffer is in RECORDING state (except between `submit` and `beginNextCommandBuffer` call intended for ADVANCED users)
17+
//! The "current recording commandBuffer" is returned by `getCurrentRecordingCommandBufferInfo` or updated via the pointer ref in beginNextCommandBuffer or overflowSubmit)
1518
struct SIntendedSubmitInfo final : core::Uncopyable
1619
{
1720
public:
@@ -27,14 +30,12 @@ struct SIntendedSubmitInfo final : core::Uncopyable
2730
// EXACTLY ONE commandbuffer must be in recording state! This is the one the utility will use immediately to record commands.
2831
// But remember that even though its scratch, you can record some of your own preceeding commands into it as well.
2932
std::span<IQueue::SSubmitInfo::SCommandBufferInfo> scratchCommandBuffers = {};
30-
// This parameter is required but may be unused if there is no need (no overflow) to do submit.
31-
// This semaphore is needed to "stitch together" additional submits if they occur so they occur before and after the original intended waits and signals.
33+
// This semaphore is needed to ensure correct and valid usage of the command buffer used to record the next submit by ensuring they are not currently pending.
3234
// The initial `scratchSemaphore.value` gets incremented and signalled on each submit, can start at 0 because an overflow will signal `value+1`.
33-
// NOTE: You should signal this semaphore as well when doing your last/tail submit! Why?
34-
// Utilities might use the `[Multi]TimelineEventHandler` to latch cleanups on `scratchSemaphore` where this value
35+
// `[Multi]TimelineEventHandler` to latch cleanups on `scratchSemaphore` where this value, so make sure you're submitting by yourself manually and use the functions provided here.
3536
// is a value that they expect to actually get signalled in the future.
36-
// NOTE: Do not choose the values for waits and signals yourself, as the overflows may increment the counter by an arbitrary amount!
37-
// You can actually examine the change in `scratchSemaphore.value` to figure out how many overflows occurred.
37+
// NOTE: To ensure deterministic behavior, do not attempt to signal this semaphore yourself.
38+
// You can actually examine the change in `scratchSemaphore.value` to figure out how many submits occurred.
3839
IQueue::SSubmitInfo::SSemaphoreInfo scratchSemaphore = {};
3940
// Optional: If you had a semaphore whose highest pending signal is 45 but gave the scratch a value of 68 (causing 69 to get signalled in a popped submit),
4041
// but only used 4 scratch command buffers, we'd wait for the semaphore to reach 66 before resetting the next scratch command buffer.
@@ -48,22 +49,20 @@ struct SIntendedSubmitInfo final : core::Uncopyable
4849
//
4950
inline ISemaphore::SWaitInfo getFutureScratchSemaphore() const {return {scratchSemaphore.semaphore,scratchSemaphore.value+1};}
5051

51-
// Returns the scratch to use if valid, nullptr otherwise
52-
inline const IQueue::SSubmitInfo::SCommandBufferInfo* getCommandBufferForRecording() const
52+
// Returns the command buffer to use for recording if valid, nullptr otherwise
53+
inline const IQueue::SSubmitInfo::SCommandBufferInfo* getCurrentRecordingCommandBufferInfo() const
5354
{
54-
if (!queue || scratchCommandBuffers.empty() || !scratchSemaphore.semaphore)
55+
if (scratchCommandBuffers.empty())
5556
return nullptr;
57+
5658
// All commandbuffers must be compatible with the queue we're about to submit to
5759
auto cmdbufNotSubmittableToQueue = [this](const IGPUCommandBuffer* cmdbuf)->bool
5860
{
5961
return !cmdbuf || cmdbuf->getPool()->getQueueFamilyIndex()!=queue->getFamilyIndex();
6062
};
61-
// All commandbuffers before the scratch must be executable (ready to be submitted)
62-
for (const auto& info : prevCommandBuffers)
63-
if (cmdbufNotSubmittableToQueue(info.cmdbuf) || info.cmdbuf->getState()!=IGPUCommandBuffer::STATE::EXECUTABLE)
64-
return nullptr;
65-
// the found scratch
66-
const IQueue::SSubmitInfo::SCommandBufferInfo* scratch = nullptr;
63+
64+
// finding command buffer toRecord
65+
const IQueue::SSubmitInfo::SCommandBufferInfo* toRecord = nullptr;
6766
core::unordered_set<const IGPUCommandBuffer*> uniqueCmdBufs;
6867
uniqueCmdBufs.reserve(scratchCommandBuffers.size());
6968
for (auto& info : scratchCommandBuffers)
@@ -72,43 +71,46 @@ struct SIntendedSubmitInfo final : core::Uncopyable
7271
if (cmdbufNotSubmittableToQueue(info.cmdbuf) || !info.cmdbuf->isResettable())
7372
return nullptr;
7473
uniqueCmdBufs.insert(info.cmdbuf);
75-
// not our scratch
74+
// not our toRecord
7675
if (info.cmdbuf->getState()!=IGPUCommandBuffer::STATE::RECORDING)
7776
continue;
78-
// there can only be one scratch!
79-
if (scratch)
77+
// there can only be one toRecord!
78+
if (toRecord)
8079
return nullptr;
81-
scratch = &info;
80+
toRecord = &info;
8281
}
8382
// a commandbuffer repeats itself
8483
if (uniqueCmdBufs.size()!=scratchCommandBuffers.size())
8584
return nullptr;
86-
// there is no scratch cmdbuf at all!
87-
if (!scratch)
85+
// there is no toRecord cmdbuf at all!
86+
if (!toRecord)
8887
return nullptr;
8988
// It makes no sense to reuse the same commands for a second submission.
9089
// Moreover its dangerous because the utilities record their own internal commands which might use subresources for which
9190
// frees have already been latched on the scratch semaphore you must signal anyway.
92-
if (!scratch->cmdbuf->getRecordingFlags().hasFlags(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT))
91+
if (!toRecord->cmdbuf->getRecordingFlags().hasFlags(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT))
9392
return nullptr;
94-
return scratch;
93+
return toRecord;
9594
}
96-
97-
// Returns the scratch to use if valid, nullptr otherwise
95+
96+
// Returns the command buffer to use for recording if valid, nullptr otherwise
9897
inline const IQueue::SSubmitInfo::SCommandBufferInfo* valid() const
9998
{
100-
if (!queue || scratchCommandBuffers.empty() || !scratchSemaphore.semaphore)
99+
if (!queue || !scratchSemaphore.semaphore)
101100
return nullptr;
101+
102102
// All commandbuffers must be compatible with the queue we're about to submit to
103103
auto cmdbufNotSubmittableToQueue = [this](const IGPUCommandBuffer* cmdbuf)->bool
104104
{
105105
return !cmdbuf || cmdbuf->getPool()->getQueueFamilyIndex()!=queue->getFamilyIndex();
106106
};
107-
// All commandbuffers before the scratch must be executable (ready to be submitted)
107+
108+
// All prevCommandBuffers must be executable (ready to be submitted)
108109
for (const auto& info : prevCommandBuffers)
109110
if (cmdbufNotSubmittableToQueue(info.cmdbuf) || info.cmdbuf->getState()!=IGPUCommandBuffer::STATE::EXECUTABLE)
110111
return nullptr;
111-
return getCommandBufferForRecording();
112+
113+
return getCurrentRecordingCommandBufferInfo();
112114
}
113115

114116
//! xxxx
@@ -175,22 +177,47 @@ struct SIntendedSubmitInfo final : core::Uncopyable
175177
return retval;
176178
}
177179

178-
// recordingCmdBuf pointer may change so some other command buffer info in `scratchCommandBuffers` when applicable
180+
// ! [inout] recordingCmdBuf: current recording SCommandBufferInfo pointing to an element of `scratchCommandBuffers`.
181+
// To achieve multiple submits in flight, the pointer may change and point to another element of `scratchCommandBuffers` when applicable.
179182
inline IQueue::RESULT overflowSubmit(const IQueue::SSubmitInfo::SCommandBufferInfo* &recordingCmdBuf)
183+
{
184+
IQueue::RESULT res = submit(recordingCmdBuf, {});
185+
if (res != IQueue::RESULT::SUCCESS)
186+
return res;
187+
if (!beginNextCommandBuffer(recordingCmdBuf))
188+
return IQueue::RESULT::OTHER_ERROR;
189+
return IQueue::RESULT::SUCCESS;
190+
}
191+
192+
// Submits via the current recording command buffer
193+
// ! recordingCmdBuf: current recording SCommandBufferInfo pointing to an element of `scratchCommandBuffers`.
194+
// ! Optional: signalSemaphores: your signal semaphores that indicate the job is finished.
195+
// ! Don't attempt to use the `recordingCmdBuf` after calling this function and before calling `beginNextCommandBuffer` because it will be in PENDING state
196+
inline IQueue::RESULT submit(const IQueue::SSubmitInfo::SCommandBufferInfo* recordingCmdBuf, const std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> signalSemaphores)
180197
{
181198
const auto pLast = &scratchCommandBuffers.back();
182199
if (recordingCmdBuf<scratchCommandBuffers.data() || recordingCmdBuf>pLast)
183200
return IQueue::RESULT::OTHER_ERROR;
201+
184202
// First, submit the already buffered up work
185203
recordingCmdBuf->cmdbuf->end();
186204

187205
// we only signal the scratch semaphore when overflowing
188-
const auto submit = popSubmit(recordingCmdBuf->cmdbuf,{});
206+
const auto submit = popSubmit(recordingCmdBuf->cmdbuf,signalSemaphores);
189207
IQueue::RESULT res = queue->submit(submit);
190-
if (res!=IQueue::RESULT::SUCCESS)
191-
return res;
208+
return res;
209+
}
210+
211+
// If next command buffer is not PENDING and safe to use, it will reset and begin it, otherwise it will block on the scratch semaphore
212+
// ! recordingCmdBuf: previous recording SCommandBufferInfo pointing to an element of `scratchCommandBuffers`
213+
// To achieve multiple submits in flight, the pointer may change and point to another element of `scratchCommandBuffers` when applicable.
214+
inline bool beginNextCommandBuffer(const IQueue::SSubmitInfo::SCommandBufferInfo* &recordingCmdBuf)
215+
{
216+
const auto pLast = &scratchCommandBuffers.back();
217+
if (recordingCmdBuf<scratchCommandBuffers.data() || recordingCmdBuf>pLast)
218+
return false;
192219

193-
// Get the next scratch buffer
220+
// Get the next command buffer to record
194221
if (recordingCmdBuf!=pLast)
195222
recordingCmdBuf++;
196223
else
@@ -209,17 +236,17 @@ struct SIntendedSubmitInfo final : core::Uncopyable
209236
if (waitResult!=ISemaphore::WAIT_RESULT::SUCCESS)
210237
{
211238
assert(false);
212-
return IQueue::RESULT::OTHER_ERROR;
239+
return false;
213240
}
214241
}
215242
// could have just called begin to reset but also need to reset with the release resources flag
216243
recordingCmdBuf->cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT);
217244
recordingCmdBuf->cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT);
218245

219-
return res;
246+
return true;
220247
}
221248

222-
// useful overload if you forgot what was scratch
249+
// useful overload if you forgot what was command buffer to record.
223250
inline IQueue::RESULT overflowSubmit()
224251
{
225252
auto recordingCmdBuf = valid();

0 commit comments

Comments
 (0)