SpiecsEngine
 
Loading...
Searching...
No Matches
VulkanCmdThreadPool.cpp
Go to the documentation of this file.
1/**
2* @file VulkanCmdThreadPool.cpp.
3* @brief The VulkanCmdThreadPool Class Implementation.
4* @author Spices.
5*/
6
7#include "Pchheader.h"
9#include "Render/FrameInfo.h"
11
12namespace Spices {
13
14 const int nCmdThreads = glm::min(4, static_cast<int>(0.5 * std::thread::hardware_concurrency()));
15
16 VulkanCmdThreadPool::VulkanCmdThreadPool(VulkanState& vulkanState, const std::string& name)
17 : VulkanObject(vulkanState)
19 {
21
22 for (int i = 0; i < MaxFrameInFlight; i++)
23 {
24 m_CmdBuffers[i].resize(nCmdThreads);
25 }
26
27 m_CmdPools.resize(nCmdThreads);
28
29 /**
30 * @brief Init ThreadPool.
31 */
32 {
33 SetMode(PoolMode::MODE_FIXED);
35 }
36 }
37
39 {
41
42 vkDeviceWaitIdle(m_VulkanState.m_Device);
43
44 for (int i = 0; i < MaxFrameInFlight; i++)
45 {
47 }
48 }
49
50 void VulkanCmdThreadPool::Start(int initThreadSize)
51 {
53
54 m_IsPoolRunning = true;
55 m_InitThreadSize = initThreadSize;
56 m_IdleThreadSize = initThreadSize;
57 m_NThreads = initThreadSize;
58
59 for (uint32_t i = 0; i < m_InitThreadSize; i++)
60 {
61 auto ptr = std::make_unique<Thread<VkCommandBuffer>>(std::bind(&VulkanCmdThreadPool::ThreadFunc, this, std::placeholders::_1), i);
62 uint32_t threadId = ptr->GetId();
63
64 m_Threads.emplace(threadId, std::move(ptr));
65 m_Threads[threadId]->Start();
66 }
67 }
68
69 void VulkanCmdThreadPool::ThreadFunc(Thread<VkCommandBuffer>* thread)
70 {
72
73 /**
74 * @brief Name thread.
75 */
76 std::stringstream ss;
77 ss << m_PoolName << thread->GetId();
78
80
81 auto lastTime = std::chrono::high_resolution_clock::now();
82
83 for (;;)
84 {
85 Task task;
86 {
87 std::unique_lock<std::mutex> lock(m_Mutex);
88
89 while ((m_Tasks.load() == 0 && thread->GetThreadTasksCount() == 0) || m_IsSuspend)
90 {
91 /**
92 * @brief Exit.
93 */
94 if (!m_IsPoolRunning)
95 {
96 m_Threads.erase(thread->GetId());
97 --m_IdleThreadSize;
98 m_ExitCond.notify_all();
99 return;
100 }
101
102 if (m_PoolMode == PoolMode::MODE_CACHED)
103 {
104 if (m_NotEmpty.wait_for(lock, std::chrono::seconds(1)) == std::cv_status::timeout)
105 {
106 auto now = std::chrono::high_resolution_clock::now();
107 auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
108
109 /**
110 * @brief Try recovery unused threads.
111 */
112 if (dur.count() >= m_ThreadIdleTimeOut && m_Threads.size() > m_InitThreadSize)
113 {
114 m_Threads.erase(thread->GetId());
115 --m_IdleThreadSize;
116 --m_NThreads;
117 return;
118 }
119 }
120 }
121 else
122 {
123 m_NotEmpty.wait(lock);
124 }
125 }
126
127 /**
128 * @brief Rink First, Thread Tasks.
129 */
130 task = thread->RequireTask();
131
132 /**
133 * @brief Rink Second, Pool Tasks.
134 */
135 if (!task)
136 {
137 task = m_TaskQueue.front();
138 m_TaskQueue.pop();
139 thread->SetThreadInTask(true);
140 --m_Tasks;
141 }
142
143 --m_IdleThreadSize;
144 }
145
146 if (m_Tasks > 0)
147 {
148 m_NotEmpty.notify_all();
149 }
150
151 /**
152 * @brief execute task.
153 */
154 if (task != nullptr)
155 {
156 VkCommandBuffer cmdBuffer = CreateParallelCommandBuffers(thread->GetId());
157 task(cmdBuffer);
158 thread->SetThreadInTask(false);
159 ++m_IdleThreadSize;
160 m_IdleCond.notify_all();
161 }
162 else
163 {
164 thread->SetThreadInTask(false);
165 ++m_IdleThreadSize;
166 m_IdleCond.notify_all();
167 }
168
169 lastTime = std::chrono::high_resolution_clock::now();
170 }
171 }
172
174 {
176
177 if (!m_CmdPools[threadId])
178 {
179 m_CmdPools[threadId] = VulkanCommandPool::GetThreadGraphicCommandPool();
180 }
181
182 VkCommandBufferAllocateInfo allocInfo{};
183 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
184 allocInfo.commandPool = m_CmdPools[threadId];
185 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
186 allocInfo.commandBufferCount = 1;
187
188 VkCommandBuffer cmdBuffer;
189
190 VK_CHECK(vkAllocateCommandBuffers(m_VulkanState.m_Device, &allocInfo, &cmdBuffer))
191 DEBUGUTILS_SETOBJECTNAME(VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)cmdBuffer, m_VulkanState.m_Device, "ParallelGraphicCommandBuffer")
193
194 m_CmdBuffers[FrameInfo::Get().m_FrameIndex][threadId].push_back(cmdBuffer);
195
196 return cmdBuffer;
197 }
198
200 {
202
203 auto& commandBuffers = m_CmdBuffers[frameIndex];
204
205 for (int i = 0; i < m_NThreads.load(); i++)
206 {
207 if (commandBuffers[i].empty()) continue;
208
209 vkFreeCommandBuffers(m_VulkanState.m_Device, m_CmdPools[i], commandBuffers[i].size(), commandBuffers[i].data());
210 commandBuffers[i].clear();
211 }
212 }
213}
#define SPICES_PROFILE_ZONE
#define SPICES_PROFILE_VK_COLLECT(cmdbuf)
#define VK_CHECK(expr)
Vulkan Check macro. Verify Vulkan API Effectiveness.
Definition VulkanUtils.h:68
static bool SetThreadName(const std::string &name)
Set Thread name.
Thread Static Function Library.
virtual ~VulkanCmdThreadPool() override
Destructor Function.
void Start(int initThreadSize=0.5 *std::thread::hardware_concurrency())
Start Run this thread pool.
void FreeParallelCommandBuffers(uint32_t frameIndex)
Free all second CommandBuffer in rhi threadPool thread.
void ThreadFunc(Thread< VkCommandBuffer > *thread)
Thread Function.
VkCommandBuffer CreateParallelCommandBuffers(uint32_t threadId)
Create second CommandBuffer in rhi threadPool thread.
VulkanCmdThreadPool(VulkanState &vulkanState, const std::string &name)
Constructor Function. Create Specific ThreadPool.
Wrappers of RHI Thread Pool.
VulkanState & m_VulkanState
The global VulkanState Referenced from VulkanRenderBackend.
VulkanObject(VulkanState &vulkanState)
Constructor Function. Init member variables.
VulkanObject Class. This class defines the basic behaves of VulkanObject. When we create an new Vulka...
PoolMode
ThreadPool Run Mode.
const int nCmdThreads
constexpr uint32_t MaxFrameInFlight
Max In Flight Frame. 2 buffers are enough in this program.
Definition VulkanUtils.h:22
This struct contains all Vulkan object in used global.
Definition VulkanUtils.h:74