Skip to content

Commit 6c48e48

Browse files
think about the other 3 utility functions
1 parent 00182c7 commit 6c48e48

File tree

1 file changed

+65
-66
lines changed

1 file changed

+65
-66
lines changed

include/nbl/video/utilities/IUtilities.h

Lines changed: 65 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -194,108 +194,98 @@ class NBL_API2 IUtilities : public core::IReferenceCounted
194194
));
195195
return allocationSize;
196196
}
197-
198-
#if 0 // TODO: port
199-
//! WARNING: This function blocks the CPU and stalls the GPU!
200-
inline core::smart_refctd_ptr<IGPUBuffer> createFilledDeviceLocalBufferOnDedMem(IQueue* queue, IGPUBuffer::SCreationParams&& params, const void* data)
197+
198+
struct SFrontHalfSubmitInfo final
201199
{
202-
if(!params.usage.hasFlags(IGPUBuffer::EUF_TRANSFER_DST_BIT))
203-
{
204-
assert(false);
205-
return nullptr;
206-
}
207-
auto buffer = m_device->createBuffer(std::move(params));
208-
auto mreqs = buffer->getMemoryReqs();
209-
mreqs.memoryTypeBits &= m_device->getPhysicalDevice()->getDeviceLocalMemoryTypeBits();
210-
auto mem = m_device->allocate(mreqs, buffer.get());
211-
updateBufferRangeViaStagingBufferAutoSubmit(asset::SBufferRange<IGPUBuffer>{0u, params.size, core::smart_refctd_ptr(buffer)}, data, queue);
212-
return buffer;
213-
}
214-
#endif
200+
inline bool valid() const {return queue;}
215201

202+
// Use the last command buffer in intendedNextSubmit, it should be in recording state
203+
inline IGPUCommandBuffer* getScratchCommandBuffer() {return commandBuffers.empty() ? nullptr:commandBuffers.back().cmdbuf;}
204+
inline const IGPUCommandBuffer* getScratchCommandBuffer() const {return commandBuffers.empty() ? nullptr:commandBuffers.back().cmdbuf;}
205+
206+
207+
IQueue* queue = {};
208+
std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> waitSemaphores = {};
209+
std::span<IQueue::SSubmitInfo::SCommandBufferInfo> commandBuffers = {};
210+
};
211+
//! Struct meant to be used with any utility (not just `IUtilities`) which exhibits "submit on overflow" behaviour.
212+
//! Such functions are non-blocking (unless overflow) and take `SIntendedSubmitInfo` by reference and patch it accordingly.
213+
//! MAKE SURE to do a submit to `queue` by yourself with a submit info obtained by casting `this` to `IQueue::SSubmitInfo` !
214+
//! for example: in the case the `frontHalf.waitSemaphores` were already waited upon, the struct will be modified to have it's `waitSemaphores` emptied.
216215
struct SIntendedSubmitInfo final
217216
{
218217
public:
219218
inline bool valid() const
220219
{
221-
if (!queue || commandBuffers.empty() || signalSemaphores.empty())
220+
if (!frontHalf.valid() || frontHalf.commandBuffers.empty() || signalSemaphores.empty())
222221
return false;
223-
if (!getScratchCommandBuffer()->isResettable())
222+
if (!frontHalf.getScratchCommandBuffer()->isResettable())
224223
return false;
225-
if (!getScratchCommandBuffer()->getRecordingFlags().hasFlags(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT))
226-
return false;
227-
for (const auto& info : commandBuffers)
228-
if (info.cmdbuf->getPool()->getQueueFamilyIndex()!=queue->getFamilyIndex())
224+
if (!frontHalf.getScratchCommandBuffer()->getRecordingFlags().hasFlags(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT))
229225
return false;
230226
return true;
231227
}
232228

233-
// Use the last command buffer in intendedNextSubmit, it should be in recording state
234-
inline IGPUCommandBuffer* getScratchCommandBuffer() {return commandBuffers.back().cmdbuf;}
235-
inline const IGPUCommandBuffer* getScratchCommandBuffer() const {return commandBuffers.back().cmdbuf;}
236-
237229
inline ISemaphore::SWaitInfo getScratchSemaphoreNextWait() const {return {signalSemaphores.front().semaphore,signalSemaphores.front().value};}
238230

239231
inline operator IQueue::SSubmitInfo() const
240232
{
241233
return {
242-
.waitSemaphores = waitSemaphores,
243-
.commandBuffers = commandBuffers,
234+
.waitSemaphores = frontHalf.waitSemaphores,
235+
.commandBuffers = frontHalf.commandBuffers,
244236
.signalSemaphores = signalSemaphores
245237
};
246238
}
247239

248240
inline void overflowSubmit()
249241
{
250-
auto cmdbuf = getScratchCommandBuffer();
242+
auto cmdbuf = frontHalf.getScratchCommandBuffer();
251243
auto& scratchSemaphore = signalSemaphores.front();
252244
// but first sumbit the already buffered up copies
253245
cmdbuf->end();
254246
IQueue::SSubmitInfo submit = *this;
255247
// we only signal the last semaphore which is used as scratch
256248
submit.signalSemaphores = {&scratchSemaphore,1};
257249
assert(submit.isValid());
258-
queue->submit({&submit,1});
250+
frontHalf.queue->submit({&submit,1});
259251
// We wait (stall) on the immediately preceeding submission timeline semaphore signal value and increase it for the next signaller
260252
{
261253
const ISemaphore::SWaitInfo info = {scratchSemaphore.semaphore,scratchSemaphore.value++};
262254
const_cast<ILogicalDevice*>(cmdbuf->getOriginDevice())->blockForSemaphores({&info,1});
263255
}
264256
// we've already waited on the Host for the semaphores, no use waiting twice
265-
waitSemaphores = {};
257+
frontHalf.waitSemaphores = {};
266258
// since all the commandbuffers have submitted already we only reuse the last one
267-
commandBuffers = {&commandBuffers.back(),1};
259+
frontHalf.commandBuffers = {&frontHalf.commandBuffers.back(),1};
268260
// we will still signal the same set in the future
269261
cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT);
270262
cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT);
271263
}
272264

273265

274-
IQueue* queue = {};
275-
std::span<const IQueue::SSubmitInfo::SSemaphoreInfo> waitSemaphores = {};
276-
std::span<IQueue::SSubmitInfo::SCommandBufferInfo> commandBuffers = {};
266+
//! The last CommandBuffer will be used to record the copy commands
267+
SFrontHalfSubmitInfo frontHalf = {};
268+
//! The first Semaphore will be used as a scratch, so don't use it yourself as we can advance the counter an arbitrary amount!
277269
std::span<IQueue::SSubmitInfo::SSemaphoreInfo> signalSemaphores = {};
278270

279271
private:
280272
friend class IUtilities;
281273
static const char* ErrorText;
282274
};
275+
283276
// --------------
284277
// updateBufferRangeViaStagingBuffer
285278
// --------------
286279

287280
//! Copies `data` to stagingBuffer and Records the commands needed to copy the data from stagingBuffer to `bufferRange.buffer`
288281
//! If the allocation from staging memory fails due to large buffer size or fragmentation then This function may need to submit the command buffer via the `submissionQueue`.
289282
//! Returns:
290-
//! IQueue::SSubmitInfo to use for command buffer submission instead of `intendedNextSubmit`.
291-
//! for example: in the case the `SSubmitInfo::waitSemaphores` were already signalled, the new SSubmitInfo will have it's waitSemaphores emptied from `intendedNextSubmit`.
292-
//! Make sure to submit with the new SSubmitInfo returned by this function
283+
//! the number of times we overflown and had to submit, <0 [negative] on failure
293284
//! Parameters:
285+
//! - nextSubmit:
286+
//! Is the SubmitInfo you intended to submit your command buffers with, it will be patched if overflow occurred @see SIntendedSubmitInfo
294287
//! - bufferRange: contains offset + size into bufferRange::buffer that will be copied from `data` (offset doesn't affect how `data` is accessed)
295288
//! - data: raw pointer to data that will be copied to bufferRange::buffer
296-
//! - intendedNextSubmit:
297-
//! Is the SubmitInfo you intended to submit your command buffers.
298-
//! ** The last command buffer will be used to record the copy commands
299289
//! - submissionQueue: IQueue used to submit, when needed.
300290
//! Note: This parameter is required but may not be used if there is no need to submit
301291
//! - scratchSemaphore:
@@ -320,24 +310,25 @@ class NBL_API2 IUtilities : public core::IReferenceCounted
320310
//! * submissionFence must point to a valid IGPUFence
321311
//! * submissionFence must be in `UNSIGNALED` state
322312
//! ** IUtility::getDefaultUpStreamingBuffer()->cull_frees() should be called before reseting the submissionFence and after fence is signaled.
323-
inline bool updateBufferRangeViaStagingBuffer(SIntendedSubmitInfo& nextSubmit, const asset::SBufferRange<IGPUBuffer>& bufferRange, const void* data)
313+
inline int64_t updateBufferRangeViaStagingBuffer(SIntendedSubmitInfo& nextSubmit, const asset::SBufferRange<IGPUBuffer>& bufferRange, const void* data)
324314
{
325315
if (!bufferRange.isValid() || !bufferRange.buffer->getCreationParams().usage.hasFlags(asset::IBuffer::EUF_TRANSFER_DST_BIT))
326316
{
327317
m_logger.log("Invalid `bufferRange` or buffer has no `EUF_TRANSFER_DST_BIT` usage flag, cannot `updateBufferRangeViaStagingBuffer`!", system::ILogger::ELL_ERROR);
328-
return false;
318+
return -1;
329319
}
330320

331321
if (!nextSubmit.valid())
332322
{
333323
m_logger.log(nextSubmit.ErrorText,system::ILogger::ELL_ERROR);
334-
return false;
324+
return -1;
335325
}
336326

337327
const auto& limits = m_device->getPhysicalDevice()->getLimits();
338328
const uint32_t optimalTransferAtom = limits.maxResidentInvocations * sizeof(uint32_t);
339329

340-
auto cmdbuf = nextSubmit.getScratchCommandBuffer();
330+
auto cmdbuf = nextSubmit.frontHalf.getScratchCommandBuffer();
331+
int64_t overflowCounter = 0;
341332
// no pipeline barriers necessary because write and optional flush happens before submit, and memory allocation is reclaimed after fence signal
342333
for (size_t uploadedSize=0ull; uploadedSize<bufferRange.size;)
343334
{
@@ -362,6 +353,7 @@ class NBL_API2 IUtilities : public core::IReferenceCounted
362353
if (localOffset == StreamingTransientDataBufferMT<>::invalid_value)
363354
{
364355
nextSubmit.overflowSubmit();
356+
overflowCounter++;
365357
continue;
366358
}
367359
// some platforms expose non-coherent host-visible GPU memory, so writes need to be flushed explicitly
@@ -380,9 +372,9 @@ class NBL_API2 IUtilities : public core::IReferenceCounted
380372
m_defaultUploadBuffer.get()->multi_deallocate(1u,&localOffset,&allocationSize,nextSubmit.getScratchSemaphoreNextWait(),&cmdbuf);
381373
uploadedSize += subSize;
382374
}
383-
return true;
375+
return overflowCounter;
384376
}
385-
#if 0
377+
386378
//! This function is an specialization of the `updateBufferRangeViaStagingBuffer` function above.
387379
//! Submission of the commandBuffer to submissionQueue happens automatically, no need for the user to handle submit
388380
//! WARNING: Don't use this function in hot loops or to do batch updates, its merely a convenience for one-off uploads
@@ -399,16 +391,12 @@ class NBL_API2 IUtilities : public core::IReferenceCounted
399391
//! Valid Usage:
400392
//! * If submitInfo::commandBufferCount > 0 and the last command buffer must be in one of these stages: `EXECUTABLE`, `INITIAL`, `RECORDING`
401393
//! For more info on command buffer states See `ICommandBuffer::E_STATE` comments.
402-
inline void updateBufferRangeViaStagingBufferAutoSubmit(
403-
const asset::SBufferRange<IGPUBuffer>& bufferRange, const void* data,
404-
IQueue* submissionQueue, IGPUFence* submissionFence, IQueue::SSubmitInfo submitInfo = {}
405-
)
394+
inline bool updateBufferRangeViaStagingBufferAutoSubmit(SIntendedSubmitInfo& nextSubmit, const asset::SBufferRange<IGPUBuffer>& bufferRange, const void* data)
406395
{
407-
if(!submitInfo.isValid())
396+
if(!nextSubmit.frontHalf.valid())
408397
{
409398
// TODO: log error
410-
assert(false);
411-
return;
399+
return false;
412400
}
413401

414402
CSubmitInfoPatcher submitInfoPatcher;
@@ -418,28 +406,39 @@ class NBL_API2 IUtilities : public core::IReferenceCounted
418406

419407
assert(submitInfo.isValid());
420408
submissionQueue->submit(1u,&submitInfo,submissionFence);
409+
return true;
421410
}
422411

423412
//! This function is an specialization of the `updateBufferRangeViaStagingBufferAutoSubmit` function above.
424413
//! Additionally waits for the upload right away
425414
//! WARNING: This function blocks CPU and stalls the GPU!
426-
inline void updateBufferRangeViaStagingBufferAutoSubmit(
427-
const asset::SBufferRange<IGPUBuffer>& bufferRange, const void* data,
428-
IQueue* submissionQueue, const IQueue::SSubmitInfo& submitInfo = {}
429-
)
415+
inline bool updateBufferRangeViaStagingBufferAutoSubmit(const SFrontHalfSubmitInfo& submit, const asset::SBufferRange<IGPUBuffer>& bufferRange, const void* data)
430416
{
431-
if(!submitInfo.isValid())
417+
if(!submit.valid())
432418
{
433419
// TODO: log error
434-
assert(false);
435-
return;
420+
return false;
436421
}
437422

438-
auto fence = m_device->createFence(static_cast<IGPUFence::E_CREATE_FLAGS>(0));
439-
updateBufferRangeViaStagingBufferAutoSubmit(bufferRange, data, submissionQueue, fence.get(), submitInfo);
440-
m_device->blockForFences(1u, &fence.get());
423+
auto semaphore = m_device->createSemaphore(0);
424+
if (!updateBufferRangeViaStagingBufferAutoSubmit(,bufferRange,data))
425+
return false;
426+
const ISemaphore::SWaitInfo info = {semaphore.get(),1};
427+
m_device->blockForSemaphores({&info,1});
428+
return true;
429+
}
430+
431+
//! WARNING: This function blocks the CPU and stalls the GPU!
432+
inline core::smart_refctd_ptr<IGPUBuffer> createFilledDeviceLocalBufferOnDedMem(const SFrontHalfSubmitInfo& submit, IGPUBuffer::SCreationParams&& params, const void* data)
433+
{
434+
auto buffer = m_device->createBuffer(std::move(params));
435+
auto mreqs = buffer->getMemoryReqs();
436+
mreqs.memoryTypeBits &= m_device->getPhysicalDevice()->getDeviceLocalMemoryTypeBits();
437+
auto mem = m_device->allocate(mreqs,buffer.get());
438+
if (!updateBufferRangeViaStagingBufferAutoSubmit(submit,asset::SBufferRange<IGPUBuffer>{0u,params.size,core::smart_refctd_ptr(buffer)},data))
439+
return nullptr;
440+
return buffer;
441441
}
442-
#endif
443442

444443
// pipelineBarrierAutoSubmit?
445444

0 commit comments

Comments
 (0)