Beatmup
context.cpp
Go to the documentation of this file.
1 /*
2  Beatmup image and signal processing library
3  Copyright (C) 2019, lnstadrum
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "context.h"
20 #include "parallelism.h"
21 #include "gpu/pipeline.h"
22 #include "gpu/recycle_bin.h"
23 #include "gpu/gpu_task.h"
24 #include "exception.h"
25 #include "bitmap/abstract_bitmap.h"
26 #include "thread_pool.hpp"
27 #include <algorithm>
28 #include <vector>
29 #include <map>
30 #include <mutex>
31 #include <iostream>
32 
33 #ifdef BEATMUP_PLATFORM_ANDROID
34 #include <android/log.h>
35 #endif
36 
37 using namespace Beatmup;
38 
39 
40 /**
41  Context class implementation (pimpl)
42 */
44 private:
45  /**
46  Thread pool event listener
47  */
49  private:
51 
52  public:
54 
55  inline void threadCreated(PoolIndex pool) {
56  if (ctx.eventListener)
58  };
59 
60  inline void threadTerminating(PoolIndex pool) {
61  if (ctx.eventListener)
63  };
64 
65  inline bool taskDone(PoolIndex pool, AbstractTask& task, bool aborted) {
66  if (ctx.eventListener)
67  return ctx.eventListener->taskDone(pool, task, aborted);
68  return false;
69  };
70 
71  inline void taskFail(PoolIndex pool, AbstractTask& task, std::exception_ptr exPtr) {
72  if (ctx.eventListener)
73  try {
74  std::rethrow_exception(exPtr);
75  }
76  catch (const std::exception & ex) {
77 #if BEATMUP_PLATFORM_ANDROID
78  __android_log_print(ANDROID_LOG_ERROR, "Beatmup",
79  "\n********************************************\n"
80  "Beatmup engine raises exception:\n%s"
81  "\n********************************************\n", ex.what());
82 #elif BEATMUP_DEBUG
83  std::cout << ex.what() << std::endl;
84 #endif
85  ctx.eventListener->taskFail(pool, task, ex);
86  }
87  }
88 
89  inline void gpuInitFail(PoolIndex pool, std::exception_ptr exPtr) {
90  if (ctx.eventListener)
91  try {
92  std::rethrow_exception(exPtr);
93  }
94  catch (const std::exception & ex) {
95 #if BEATMUP_PLATFORM_ANDROID
96  __android_log_print(ANDROID_LOG_ERROR, "Beatmup",
97  "\n********************************************\n"
98  "Beatmup engine was unable to init GPU:\n%s"
99  "\n********************************************\n", ex.what());
100 #elif BEATMUP_DEBUG
101  std::cout << ex.what() << std::endl;
102 #endif
103  ctx.eventListener->gpuInitFail(pool, ex);
104  }
105  }
106  };
107 
108  ThreadIndex optimalThreadCount; //!< optimal default number of worker threads per task in each pool
109 
110  ThreadPool** threadPools; //!< thread pools of task workers
113 
114 
115 public:
116  Context::EventListener* eventListener; //!< an event listener
117 
119  optimalThreadCount(std::max<ThreadIndex>(1, ThreadPool::hardwareConcurrency() / numThreadPools)),
122  eventListener(nullptr)
123  {
125  for (PoolIndex pool = 0; pool < numThreadPools; pool++)
127  }
128 
129 
130  ~Impl() {
131  for (PoolIndex i = 0; i < numThreadPools; i++)
132  delete threadPools[i];
133  delete[] threadPools;
134  }
135 
136 
139  auto startTime = std::chrono::high_resolution_clock::now();
141  threadPools[pool]->waitForJob(job);
142  auto endTime = std::chrono::high_resolution_clock::now();
143  threadPools[pool]->check();
144  return std::chrono::duration<float, std::milli>(endTime - startTime).count();
145  }
146 
147 
148  void repeatTask(PoolIndex pool, AbstractTask& task, bool abortCurrent) {
150  threadPools[pool]->repeatTask(task, abortCurrent);
151  }
152 
153 
157  }
158 
159 
163  }
164 
165 
166  void waitForJob(const PoolIndex pool, Job job) {
168  threadPools[pool]->waitForJob(job);
169  }
170 
171 
172  bool abortJob(const PoolIndex pool, Job job) {
174  return threadPools[pool]->abortJob(job);
175  }
176 
177 
178  void wait(const PoolIndex pool) {
180  threadPools[pool]->wait();
181  }
182 
183 
184  bool busy(const PoolIndex pool) {
186  return threadPools[pool]->busy();
187  }
188 
189 
190  void check(const PoolIndex pool) {
191  threadPools[pool]->check();
192  }
193 
194 
195  const ThreadIndex maxAllowedWorkerCount(const PoolIndex pool) const {
197  return threadPools[pool]->getThreadCount();
198  }
199 
200 
201  void limitWorkerCount(const PoolIndex pool, ThreadIndex maxValue) {
203  threadPools[pool]->resize(maxValue);
204  }
205 
206 
207  inline bool isGpuQueried() const {
208  return threadPools[0]->isGpuQueried();
209  }
210 
211 
212  inline bool isGpuReady() const {
213  return threadPools[0]->isGpuReady();
214  }
215 
216 
217  inline bool isManagingThread() const {
218  for (PoolIndex pool = 0; pool < numThreadPools; ++pool)
219  if (threadPools[pool]->isManagingThread())
220  return true;
221  return false;
222  }
223 };
224 
225 
227 Context::Context(const PoolIndex numThreadPools) {
228  impl = new Impl(numThreadPools);
229  recycleBin = new GL::RecycleBin(*this);
230 }
231 
232 
234  if (recycleBin)
235  recycleBin->emptyBin();
236  delete recycleBin;
237  delete impl;
238 }
239 
241  return impl->performTask(pool, task);
242 }
243 
244 void Context::repeatTask(AbstractTask& task, bool abortCurrent, const PoolIndex pool) {
245  return impl->repeatTask(pool, task, abortCurrent);
246 }
247 
249  return impl->submitTask(pool, task);
250 }
251 
253  return impl->submitPersistentTask(pool, task);
254 }
255 
257  impl->waitForJob(pool, job);
258 }
259 
260 bool Context::abortJob(Job job, const PoolIndex pool) {
261  return impl->abortJob(pool, job);
262 }
263 
264 void Context::wait(const PoolIndex pool) {
265  impl->wait(pool);
266 }
267 
268 bool Context::busy(const PoolIndex pool) {
269  return impl->busy(pool);
270 }
271 
272 void Context::check(const PoolIndex pool) {
273  impl->check(pool);
274 }
275 
277  return impl->maxAllowedWorkerCount(pool);
278 }
279 
280 void Context::limitWorkerCount(ThreadIndex maxValue, const PoolIndex pool) {
281  impl->limitWorkerCount(pool, maxValue);
282 }
283 
285  impl->eventListener = eventListener;
286 }
287 
289  return impl->eventListener;
290 }
291 
292 bool Context::isGpuQueried() const {
293  return impl->isGpuQueried();
294 }
295 
296 bool Context::isGpuReady() const {
297  return impl->isGpuReady();
298 }
299 
301  if (!isGpuReady()) {
302  GpuTask task;
303  performTask(task);
304  }
305 }
306 
307 bool Context::queryGpuInfo(std::string& vendor, std::string& renderer) {
308  class GpuQueryingTask : public AbstractTask {
309  public:
310  bool gpuFound;
311  std::string &vendor, &renderer;
312 
313  GpuQueryingTask(std::string& vendor, std::string& renderer) :
314  vendor(vendor), renderer(renderer) {}
315 
316  TaskDeviceRequirement getUsedDevices() const { return TaskDeviceRequirement::GPU_OR_CPU; }
317 
318  bool process(TaskThread& thread) {
319  gpuFound = false;
320  return true;
321  }
322 
323  bool processOnGPU(GraphicPipeline& gpu, TaskThread& thread) {
324  vendor = gpu.getGpuVendorString();
326  gpuFound = true;
327  return true;
328  }
329  } task(vendor, renderer);
330 
331  performTask(task);
332 
333  return task.gpuFound;
334 }
335 
337  return impl->isManagingThread();
338 }
339 
341  return recycleBin;
342 }
Task: an operation that can be executed by multiple threads in parallel.
Definition: parallelism.h:90
An event listener (bunch of callbacks)
Definition: context.h:72
virtual void taskFail(PoolIndex pool, AbstractTask &task, const std::exception &ex)=0
Called when a task fails.
virtual void gpuInitFail(PoolIndex pool, const std::exception &ex)=0
Called when GPU initialization failed.
virtual void threadCreated(PoolIndex pool)=0
Called when a new worker thread is created.
virtual void threadTerminating(PoolIndex pool)=0
Called when a worker thread finished.
virtual bool taskDone(PoolIndex pool, AbstractTask &task, bool aborted)=0
Called when a task is successfully finished.
Basic class: task and memory management, any kind of static data.
Definition: context.h:59
const ThreadIndex maxAllowedWorkerCount(const PoolIndex pool=DEFAULT_POOL) const
Definition: context.cpp:276
GL::RecycleBin * recycleBin
stores GPU garbage: resources managed by GPU and might be freed in the managing thread only
Definition: context.h:64
bool isGpuReady() const
Definition: context.cpp:296
float performTask(AbstractTask &task, const PoolIndex pool=DEFAULT_POOL)
Performs a given task.
Definition: context.cpp:240
void wait(const PoolIndex pool=DEFAULT_POOL)
Blocks until all the submitted jobs are executed.
Definition: context.cpp:264
Job submitTask(AbstractTask &task, const PoolIndex pool=DEFAULT_POOL)
Adds a new task to the jobs queue.
Definition: context.cpp:248
bool abortJob(Job job, const PoolIndex pool=DEFAULT_POOL)
Aborts a given submitted job.
Definition: context.cpp:260
GL::RecycleBin * getGpuRecycleBin() const
Definition: context.cpp:340
bool isManagingThread() const
Definition: context.cpp:336
Job submitPersistentTask(AbstractTask &task, const PoolIndex pool=DEFAULT_POOL)
Adds a new persistent task to the jobs queue.
Definition: context.cpp:252
bool queryGpuInfo(std::string &vendor, std::string &renderer)
Initializes the GPU if not yet and queries information about it.
Definition: context.cpp:307
void check(const PoolIndex pool=DEFAULT_POOL)
Checks if a specific thread pool is doing great: rethrows exceptions occurred during tasks execution,...
Definition: context.cpp:272
EventListener * getEventListener() const
Returns current event listener (or NULL)
Definition: context.cpp:288
bool busy(const PoolIndex pool=DEFAULT_POOL)
Queries whether a given thread pool is busy with a task.
Definition: context.cpp:268
void repeatTask(AbstractTask &task, bool abortCurrent, const PoolIndex pool=DEFAULT_POOL)
Ensures a given task executed at least once.
Definition: context.cpp:244
void warmUpGpu()
Initializes GPU within a given Context if not yet (takes no effect if it already is).
Definition: context.cpp:300
bool isGpuQueried() const
Definition: context.cpp:292
Impl * impl
Definition: context.h:62
void limitWorkerCount(ThreadIndex maxValue, const PoolIndex pool=DEFAULT_POOL)
Limits maximum number of threads (workers) when performing tasks in a given pool.
Definition: context.cpp:280
void setEventListener(EventListener *eventListener)
Installs new event listener.
Definition: context.cpp:284
void waitForJob(Job job, const PoolIndex pool=DEFAULT_POOL)
Waits until a given job finishes.
Definition: context.cpp:256
Stores references to GPU resources that will not be used anymore and needed to be recycled in a threa...
Definition: recycle_bin.h:34
void emptyBin()
Empty the bin destroying all the items in a GPU-aware thread.
Definition: recycle_bin.cpp:91
Template of a task using GPU.
Definition: gpu_task.h:27
Internal low-level GPU control API.
Definition: pipeline.h:33
const char * getGpuRendererString() const
Definition: pipeline.cpp:951
const char * getGpuVendorString() const
Definition: pipeline.cpp:946
Thread executing tasks.
Definition: parallelism.h:154
A pool of threads running tasks ThreadPool runs AbstractTasks, possibly in multiple threads.
Definition: thread_pool.hpp:36
Job submitTask(AbstractTask &task, const TaskExecutionMode mode)
Adds a new task to the jobs queue.
void resize(ThreadIndex newThreadCount)
Resizes the pool.
@ PERSISTENT
persistent task, run process() until it returns false
@ NORMAL
normal task, should be run it once
void waitForJob(Job job)
Blocks until a given job finishes if not yet.
Job repeatTask(AbstractTask &task, bool abortCurrent)
Ensures a given task executed at least once.
ThreadIndex getThreadCount() const
bool abortJob(Job job)
Aborts a given submitted job.
bool busy()
Checks whether the pool has jobs.
bool isGpuReady() const
void check()
Checks if the thread pool is doing great.
bool isGpuQueried() const
void wait()
Blocks until all the submitted jobs are executed.
void threadCreated(PoolIndex pool)
Callback function called when a new worker thread is created.
Definition: context.cpp:55
void threadTerminating(PoolIndex pool)
Callback function called when a worker thread is terminating.
Definition: context.cpp:60
ThreadPoolEventListener(const Context::Impl &ctx)
Definition: context.cpp:53
void taskFail(PoolIndex pool, AbstractTask &task, std::exception_ptr exPtr)
Callback function called when an exception is thrown.
Definition: context.cpp:71
bool taskDone(PoolIndex pool, AbstractTask &task, bool aborted)
Callback function called when a task is done or cancelled; if returns true, the task will be repeated...
Definition: context.cpp:65
void gpuInitFail(PoolIndex pool, std::exception_ptr exPtr)
Callback function called when the GPU cannot start.
Definition: context.cpp:89
Context class implementation (pimpl)
Definition: context.cpp:43
bool abortJob(const PoolIndex pool, Job job)
Definition: context.cpp:172
PoolIndex numThreadPools
Definition: context.cpp:111
Impl(const PoolIndex numThreadPools)
Definition: context.cpp:118
bool isGpuReady() const
Definition: context.cpp:212
bool busy(const PoolIndex pool)
Definition: context.cpp:184
void waitForJob(const PoolIndex pool, Job job)
Definition: context.cpp:166
void repeatTask(PoolIndex pool, AbstractTask &task, bool abortCurrent)
Definition: context.cpp:148
void limitWorkerCount(const PoolIndex pool, ThreadIndex maxValue)
Definition: context.cpp:201
Job submitTask(const PoolIndex pool, AbstractTask &task)
Definition: context.cpp:154
Context::EventListener * eventListener
an event listener
Definition: context.cpp:116
ThreadPool ** threadPools
thread pools of task workers
Definition: context.cpp:110
void check(const PoolIndex pool)
Definition: context.cpp:190
bool isManagingThread() const
Definition: context.cpp:217
bool isGpuQueried() const
Definition: context.cpp:207
ThreadIndex optimalThreadCount
optimal default number of worker threads per task in each pool
Definition: context.cpp:108
void wait(const PoolIndex pool)
Definition: context.cpp:178
ThreadPoolEventListener threadPoolEventListener
Definition: context.cpp:112
const ThreadIndex maxAllowedWorkerCount(const PoolIndex pool) const
Definition: context.cpp:195
Job submitPersistentTask(const PoolIndex pool, AbstractTask &task)
Definition: context.cpp:160
float performTask(PoolIndex pool, AbstractTask &task)
Definition: context.cpp:137
#define BEATMUP_ASSERT_DEBUG(C)
Definition: exception.h:163
unsigned char ThreadIndex
number of threads / thread index
Definition: parallelism.h:68
unsigned char PoolIndex
number of tread pools or a pool index
Definition: parallelism.h:67
int Job
Definition: parallelism.h:69
Definition: geometry.h:721
CustomPoint< numeric > max(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:728
JNIEnv jlong jint jint job
Beatmup::SceneRenderer * renderer
Beatmup::NNets::InferenceTask * task