Skip to content

Commit 8e2709e

Browse files
committed
Corrected and Documented CommandBuffer State Management and Checking.
+ Unified return types of begin/end/reset function + fixed compiler errors
1 parent aa8ac6e commit 8e2709e

File tree

11 files changed

+171
-31
lines changed

11 files changed

+171
-31
lines changed

include/nbl/asset/ICommandBuffer.h

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,23 @@ class ICommandBuffer
118118
ERF_RELEASE_RESOURCES_BIT = 0x01
119119
};
120120

121+
/*
122+
CommandBuffer Lifecycle Tracking in Nabla:
123+
* We say a command buffer is "resettable" If it was allocated from a command pool which was created with `ECF_RESET_COMMAND_BUFFER_BIT` flag.
124+
- ES_INITIAL
125+
-> When a command buffer is allocated, it is in the ES_INITIAL state.
126+
-> If a command buffer is "resettable", Calling `reset()` on a command buffer will change it's state to ES_INITIAL If it's not PENDING
127+
- ES_RECORDING
128+
-> Calling `begin()` on a command buffer will change it's state to `ES_RECORDING` If It's not already RECORDING, and should be INITIAL for non-resettable command buffers.
129+
- ES_EXECUTABLE
130+
-> Calling `end()` on a command buffer will change it's state to `ES_EXECUTABLE` If it's RECORDING
131+
-> After submission for non-resettable command buffers.
132+
- ES_PENDING
133+
* ES_PENDING Is impossible to track correctly without a fence. So `ES_PENDING` actually means the engine is in the process of SUBMITTING and It will be changed to either `ES_EXECUTABLE` or `ES_INVALID` after SUBMISSION.
134+
* So the convention here is different than Vulkan's command buffer lifecycle and therefore contains false negatives (It is not PENDING but actually is PENDING and working on GPU)
135+
- ES_INVALID
136+
-> After submission for resettable command buffers.
137+
*/
121138
enum E_STATE : uint32_t
122139
{
123140
ES_INITIAL,
@@ -193,27 +210,34 @@ class ICommandBuffer
193210
// hm now i think having begin(), reset() and end() as command buffer API is a little weird
194211

195212
//! `_flags` takes bits from E_USAGE
196-
virtual void begin(uint32_t _flags)
213+
virtual bool begin(uint32_t _flags)
197214
{
198-
assert(m_state != ES_PENDING);
215+
if(m_state == ES_RECORDING)
216+
{
217+
assert(false);
218+
return false;
219+
}
199220
m_state = ES_RECORDING;
200221
m_recordingFlags = _flags;
222+
return true;
201223
}
202-
224+
203225
//! `_flags` takes bits from E_RESET_FLAGS
204226
virtual bool reset(uint32_t _flags)
205227
{
206-
if (m_state==ES_PENDING);
207-
return false;
208228
m_state = ES_INITIAL;
209-
210229
return true;
211230
}
212231

213-
virtual void end()
232+
virtual bool end()
214233
{
215-
assert(m_state!=ES_PENDING);
234+
if(m_state!=ES_RECORDING)
235+
{
236+
assert(false);
237+
return false;
238+
}
216239
m_state = ES_EXECUTABLE;
240+
return true;
217241
}
218242

219243
virtual bool bindIndexBuffer(const buffer_t* buffer, size_t offset, E_INDEX_TYPE indexType) = 0;

include/nbl/video/IGPUCommandBuffer.h

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,47 @@ class IGPUCommandBuffer :
5454
>;
5555

5656
public:
57+
58+
inline bool isResettable() const
59+
{
60+
return m_cmdpool->getCreationFlags().hasValue(IGPUCommandPool::ECF_RESET_COMMAND_BUFFER_BIT);
61+
}
62+
63+
inline bool canReset() const
64+
{
65+
if(isResettable())
66+
return m_state != ES_PENDING;
67+
return false;
68+
}
69+
5770
virtual bool begin(uint32_t _flags, const SInheritanceInfo* inheritanceInfo = nullptr)
5871
{
59-
base_t::begin(_flags);
60-
if ((m_cmdpool->getCreationFlags()&IGPUCommandPool::ECF_RESET_COMMAND_BUFFER_BIT)==0u)
72+
if (!isResettable())
6173
{
62-
assert(m_state != ES_INITIAL);
74+
if(m_state != ES_INITIAL)
75+
{
76+
assert(false);
77+
return false;
78+
}
6379
}
64-
return true;
80+
81+
if(m_state == ES_PENDING)
82+
{
83+
assert(false);
84+
return false;
85+
}
86+
87+
return base_t::begin(_flags);
88+
}
89+
90+
virtual bool reset(uint32_t _flags)
91+
{
92+
if (!canReset())
93+
{
94+
assert(false);
95+
return false;
96+
}
97+
return base_t::reset(_flags);
6598
}
6699

67100
uint32_t getQueueFamilyIndex() const { return m_cmdpool->getQueueFamilyIndex(); }
@@ -135,6 +168,8 @@ class IGPUCommandBuffer :
135168
}
136169

137170
protected:
171+
friend class IGPUQueue;
172+
138173
IGPUCommandBuffer(core::smart_refctd_ptr<const ILogicalDevice>&& dev, E_LEVEL lvl, IGPUCommandPool* _cmdpool) : base_t(lvl), IBackendObject(std::move(dev)), m_cmdpool(_cmdpool)
139174
{
140175
}

include/nbl/video/IGPUCommandPool.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
#include "nbl/core/IReferenceCounted.h"
6+
#include "nbl/core/util/bitflag.h"
67

78
#include "nbl/video/decl/IBackendObject.h"
89

@@ -21,15 +22,15 @@ class IGPUCommandPool : public core::IReferenceCounted, public IBackendObject
2122
ECF_PROTECTED_BIT = 0x04
2223
};
2324

24-
IGPUCommandPool(core::smart_refctd_ptr<const ILogicalDevice>&& dev, E_CREATE_FLAGS _flags, uint32_t _familyIx) : IBackendObject(std::move(dev)), m_flags(_flags), m_familyIx(_familyIx) {}
25+
IGPUCommandPool(core::smart_refctd_ptr<const ILogicalDevice>&& dev, core::bitflag<E_CREATE_FLAGS> _flags, uint32_t _familyIx) : IBackendObject(std::move(dev)), m_flags(_flags), m_familyIx(_familyIx) {}
2526

26-
E_CREATE_FLAGS getCreationFlags() const { return m_flags; }
27+
core::bitflag<E_CREATE_FLAGS> getCreationFlags() const { return m_flags; }
2728
uint32_t getQueueFamilyIndex() const { return m_familyIx; }
2829

2930
protected:
3031
virtual ~IGPUCommandPool() = default;
3132

32-
E_CREATE_FLAGS m_flags;
33+
core::bitflag<E_CREATE_FLAGS> m_flags;
3334
uint32_t m_familyIx;
3435
};
3536

include/nbl/video/IGPUQueue.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,47 @@ class IGPUQueue : public core::Interface, public core::Unmovable
6363

6464
inline constexpr static float DEFAULT_QUEUE_PRIORITY = 1.f;
6565

66+
protected:
67+
68+
inline bool markCommandBuffersAsPending(uint32_t _count, const SSubmitInfo* _submits)
69+
{
70+
if(_submits == nullptr)
71+
return false;
72+
for (uint32_t i = 0u; i < _count; ++i)
73+
{
74+
auto& submit = _submits[i];
75+
for (uint32_t j = 0u; j < submit.commandBufferCount; ++j)
76+
{
77+
auto& cmdbuf = submit.commandBuffers[j];
78+
if(cmdbuf == nullptr)
79+
return false;
80+
submit.commandBuffers[j]->setState(IGPUCommandBuffer::ES_PENDING);
81+
}
82+
}
83+
}
84+
85+
inline bool markCommandBuffersAsDone(uint32_t _count, const SSubmitInfo* _submits)
86+
{
87+
if(_submits == nullptr)
88+
return false;
89+
for (uint32_t i = 0u; i < _count; ++i)
90+
{
91+
auto& submit = _submits[i];
92+
for (uint32_t j = 0u; j < submit.commandBufferCount; ++j)
93+
{
94+
auto& cmdbuf = submit.commandBuffers[j];
95+
if(cmdbuf == nullptr)
96+
return false;
97+
98+
if(cmdbuf->isResettable())
99+
cmdbuf->setState(IGPUCommandBuffer::ES_INVALID);
100+
else
101+
cmdbuf->setState(IGPUCommandBuffer::ES_EXECUTABLE);
102+
}
103+
}
104+
return true;
105+
}
106+
66107
protected:
67108
const uint32_t m_familyIndex;
68109
const E_CREATE_FLAGS m_flags;
@@ -72,11 +113,17 @@ class IGPUQueue : public core::Interface, public core::Unmovable
72113

73114
inline bool IGPUQueue::submit(uint32_t _count, const SSubmitInfo* _submits, IGPUFence* _fence)
74115
{
116+
if(_submits == nullptr)
117+
return false;
118+
75119
for (uint32_t i = 0u; i < _count; ++i)
76120
{
77121
auto& submit = _submits[i];
78122
for (uint32_t j = 0u; j < submit.commandBufferCount; ++j)
79123
{
124+
if(submit.commandBuffers[j] == nullptr)
125+
return false;
126+
80127
assert(submit.commandBuffers[j]->getLevel() == IGPUCommandBuffer::EL_PRIMARY);
81128
assert(submit.commandBuffers[j]->getState() == IGPUCommandBuffer::ES_EXECUTABLE);
82129

include/nbl/video/utilities/IUtilities.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ namespace nbl::video
295295
const uint32_t alignment = static_cast<uint32_t>(limits.nonCoherentAtomSize);
296296

297297
auto* cmdpool = cmdbuf->getPool();
298-
assert(cmdpool->getCreationFlags() & IGPUCommandPool::ECF_RESET_COMMAND_BUFFER_BIT);
298+
assert(cmdbuf->isResettable());
299299
assert(cmdpool->getQueueFamilyIndex() == queue->getFamilyIndex());
300300

301301
// no pipeline barriers necessary because write and optional flush happens before submit, and memory allocation is reclaimed after fence signal

src/nbl/video/COpenGLCommandBuffer.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,12 @@ COpenGLCommandBuffer::~COpenGLCommandBuffer()
112112

113113
bool COpenGLCommandBuffer::reset(uint32_t _flags)
114114
{
115-
if (!(m_cmdpool->getCreationFlags() & IGPUCommandPool::ECF_RESET_COMMAND_BUFFER_BIT))
115+
if (!IGPUCommandBuffer::canReset())
116116
return false;
117117

118118
freeSpaceInCmdPool();
119119
m_commands.clear();
120-
IGPUCommandBuffer::reset(_flags);
121-
122-
return true;
120+
return IGPUCommandBuffer::reset(_flags);
123121
}
124122

125123
void COpenGLCommandBuffer::copyBufferToImage(const SCmd<impl::ECT_COPY_BUFFER_TO_IMAGE>& c, IOpenGL_FunctionTable* gl, SOpenGLContextLocalCache* ctxlocal, uint32_t ctxid)

src/nbl/video/COpenGLCommandBuffer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,11 +555,12 @@ class COpenGLCommandBuffer final : public IGPUCommandBuffer
555555

556556
COpenGLCommandBuffer(core::smart_refctd_ptr<const ILogicalDevice>&& dev, E_LEVEL lvl, IGPUCommandPool* _cmdpool, system::logger_opt_smart_ptr&& logger, const COpenGLFeatureMap* _features);
557557

558-
inline void begin(uint32_t _flags) override final
558+
inline bool begin(uint32_t _flags) override final
559559
{
560-
IGPUCommandBuffer::begin(_flags);
561560
reset(_flags);
561+
return IGPUCommandBuffer::begin(_flags);
562562
}
563+
563564
bool reset(uint32_t _flags) override final;
564565

565566

src/nbl/video/COpenGL_Queue.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ class COpenGL_Queue final : public IGPUQueue
321321
{
322322
if (!IGPUQueue::submit(_count, _submits, _fence))
323323
return false;
324+
if(!IGPUQueue::markCommandBuffersAsPending(_count, _submits))
325+
return false;
324326

325327
core::smart_refctd_ptr<COpenGLSync> sync;
326328
for (uint32_t i = 0u; i < _count; ++i)
@@ -383,7 +385,9 @@ class COpenGL_Queue final : public IGPUQueue
383385
COpenGLFence* glfence = static_cast<COpenGLFence*>(_fence);
384386
glfence->associateGLSync(std::move(sync)); // associate sync used for signal semaphores in last submit
385387
}
386-
388+
389+
if(!IGPUQueue::markCommandBuffersAsDone(_count, _submits))
390+
return false;
387391
return true;
388392
}
389393

src/nbl/video/CVulkanCommandBuffer.h

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,32 +76,52 @@ class CVulkanCommandBuffer : public IGPUCommandBuffer
7676

7777
const auto* vk = static_cast<const CVulkanLogicalDevice*>(getOriginDevice())->getFunctionTable();
7878
VkResult retval = vk->vk.vkBeginCommandBuffer(m_cmdbuf, &beginInfo);
79-
assert(retval == VK_SUCCESS);
80-
IGPUCommandBuffer::begin(recordingFlags);
79+
if(retval == VK_SUCCESS)
80+
{
81+
return IGPUCommandBuffer::begin(recordingFlags);
82+
}
83+
else
84+
{
85+
assert(false);
86+
return false;
87+
}
88+
8189
}
8290

8391
// API needs to changed, vkEndCommandBuffer can fail
84-
void end() override
92+
bool end() override
8593
{
8694
const auto* vk = static_cast<const CVulkanLogicalDevice*>(getOriginDevice())->getFunctionTable();
8795
VkResult retval = vk->vk.vkEndCommandBuffer(m_cmdbuf);
88-
assert(retval == VK_SUCCESS);
89-
IGPUCommandBuffer::end();
96+
if(retval == VK_SUCCESS)
97+
{
98+
return IGPUCommandBuffer::end();
99+
}
100+
else
101+
{
102+
assert(false);
103+
return false;
104+
}
90105
}
91106

92107
bool reset(uint32_t _flags) override
93108
{
109+
if(!IGPUCommandBuffer::canReset())
110+
return false;
111+
94112
freeSpaceInCmdPool();
95113

96114
const auto* vk = static_cast<const CVulkanLogicalDevice*>(getOriginDevice())->getFunctionTable();
97115

98116
if (vk->vk.vkResetCommandBuffer(m_cmdbuf, static_cast<VkCommandBufferResetFlags>(_flags)) == VK_SUCCESS)
99117
{
100-
IGPUCommandBuffer::reset(_flags);
101-
return true;
118+
return IGPUCommandBuffer::reset(_flags);
102119
}
103120
else
121+
{
122+
assert(false);
104123
return false;
124+
}
105125
}
106126

107127
virtual bool bindIndexBuffer(const buffer_t* buffer, size_t offset, asset::E_INDEX_TYPE indexType) override

src/nbl/video/CVulkanQueue.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ namespace nbl::video
1010

1111
bool CVulkanQueue::submit(uint32_t _count, const SSubmitInfo* _submits, IGPUFence* _fence)
1212
{
13+
if (!IGPUQueue::submit(_count, _submits, _fence))
14+
return false;
15+
16+
if(!IGPUQueue::markCommandBuffersAsPending(_count, _submits))
17+
return false;
18+
1319
auto* vk = static_cast<const CVulkanLogicalDevice*>(m_originDevice)->getFunctionTable();
1420

1521
uint32_t waitSemCnt = 0u;
@@ -98,7 +104,11 @@ bool CVulkanQueue::submit(uint32_t _count, const SSubmitInfo* _submits, IGPUFenc
98104

99105
VkFence fence = _fence ? static_cast<CVulkanFence*>(_fence)->getInternalObject() : VK_NULL_HANDLE;
100106
if (vk->vk.vkQueueSubmit(m_vkQueue, _count, submits, fence) == VK_SUCCESS)
107+
{
108+
if(!IGPUQueue::markCommandBuffersAsDone(_count, _submits))
109+
return false;
101110
return true;
111+
}
102112

103113
return false;
104114
}

0 commit comments

Comments
 (0)