parent
df33e5e3a7
commit
23a83adb23
72 changed files with 1423 additions and 1220 deletions
After Width: | Height: | Size: 463 KiB |
@ -1,381 +0,0 @@ |
||||
// cxxpool is a header-only thread pool for C++
|
||||
// Repository: https://github.com/bloomen/cxxpool
|
||||
// Copyright: 2022 Christian Blume
|
||||
// License: http://www.opensource.org/licenses/mit-license.php
|
||||
#pragma once |
||||
#include <thread> |
||||
#include <mutex> |
||||
#include <future> |
||||
#include <stdexcept> |
||||
#include <queue> |
||||
#include <utility> |
||||
#include <functional> |
||||
#include <vector> |
||||
#include <chrono> |
||||
#include <cstddef> |
||||
|
||||
|
||||
namespace cxxpool { |
||||
namespace detail { |
||||
|
||||
|
||||
template<typename Iterator> |
||||
struct future_info { |
||||
typedef typename std::iterator_traits<Iterator>::value_type future_type; |
||||
typedef decltype(std::declval<future_type>().get()) value_type; |
||||
static constexpr bool is_void = std::is_void<value_type>::value; |
||||
}; |
||||
|
||||
|
||||
} // detail
|
||||
|
||||
|
||||
// Waits until all futures contain results
|
||||
template<typename Iterator> |
||||
inline |
||||
void wait(Iterator first, Iterator last) { |
||||
for (; first != last; ++first) |
||||
first->wait(); |
||||
} |
||||
|
||||
|
||||
// Waits until all futures contain results with a given timeout duration and
|
||||
// returns a container of std::future::status
|
||||
template<typename Result, typename Iterator, typename Rep, typename Period> |
||||
inline |
||||
Result wait_for(Iterator first, Iterator last, |
||||
const std::chrono::duration<Rep, Period>& timeout_duration, |
||||
Result result) { |
||||
for (; first != last; ++first) |
||||
result.push_back(first->wait_for(timeout_duration)); |
||||
return result; |
||||
} |
||||
|
||||
|
||||
// Waits until all futures contain results with a given timeout duration and
|
||||
// returns a vector of std::future::status
|
||||
template<typename Iterator, typename Rep, typename Period> |
||||
inline |
||||
std::vector<std::future_status> wait_for(Iterator first, Iterator last, |
||||
const std::chrono::duration<Rep, Period>& timeout_duration) { |
||||
return wait_for(first, last, timeout_duration, std::vector<std::future_status>{}); |
||||
} |
||||
|
||||
|
||||
// Waits until all futures contain results with a given timeout time and
|
||||
// returns a container of std::future::status
|
||||
template<typename Result, typename Iterator, typename Clock, typename Duration> |
||||
inline |
||||
Result wait_until(Iterator first, Iterator last, |
||||
const std::chrono::time_point<Clock, Duration>& timeout_time, |
||||
Result result) { |
||||
for (; first != last; ++first) |
||||
result.push_back(first->wait_until(timeout_time)); |
||||
return result; |
||||
} |
||||
|
||||
|
||||
// Waits until all futures contain results with a given timeout time and
|
||||
// returns a vector of std::future::status
|
||||
template<typename Iterator, typename Clock, typename Duration> |
||||
inline |
||||
std::vector<std::future_status> wait_until(Iterator first, Iterator last, |
||||
const std::chrono::time_point<Clock, Duration>& timeout_time) { |
||||
return wait_until(first, last, timeout_time, std::vector<std::future_status>{}); |
||||
} |
||||
|
||||
|
||||
// Calls get() on all futures
|
||||
template<typename Iterator, |
||||
typename = typename std::enable_if<cxxpool::detail::future_info<Iterator>::is_void>::type> |
||||
inline |
||||
void get(Iterator first, Iterator last) { |
||||
for (; first != last; ++first) |
||||
first->get(); |
||||
} |
||||
|
||||
|
||||
// Calls get() on all futures and stores the results in the returned container
|
||||
template<typename Result, typename Iterator, |
||||
typename = typename std::enable_if<!cxxpool::detail::future_info<Iterator>::is_void>::type> |
||||
inline |
||||
Result get(Iterator first, Iterator last, Result result) { |
||||
for (; first != last; ++first) |
||||
result.push_back(first->get()); |
||||
return result; |
||||
} |
||||
|
||||
|
||||
// Calls get() on all futures and stores the results in the returned vector
|
||||
template<typename Iterator, |
||||
typename = typename std::enable_if<!detail::future_info<Iterator>::is_void>::type> |
||||
inline |
||||
std::vector<typename detail::future_info<Iterator>::value_type> |
||||
get(Iterator first, Iterator last) { |
||||
return cxxpool::get(first, last, std::vector<typename cxxpool::detail::future_info<Iterator>::value_type>{}); |
||||
} |
||||
|
||||
|
||||
namespace detail { |
||||
|
||||
|
||||
template<typename Index, Index max = std::numeric_limits<Index>::max()> |
||||
class infinite_counter { |
||||
public: |
||||
|
||||
infinite_counter() |
||||
: count_{0} |
||||
{} |
||||
|
||||
infinite_counter& operator++() { |
||||
if (count_.back() == max) |
||||
count_.push_back(0); |
||||
else |
||||
++count_.back(); |
||||
return *this; |
||||
} |
||||
|
||||
bool operator>(const infinite_counter& other) const { |
||||
if (count_.size() == other.count_.size()) { |
||||
return count_.back() > other.count_.back(); |
||||
} else { |
||||
return count_.size() > other.count_.size(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
std::vector<Index> count_; |
||||
}; |
||||
|
||||
|
||||
class priority_task { |
||||
public: |
||||
typedef std::size_t counter_elem_t; |
||||
|
||||
priority_task() |
||||
: callback_{}, priority_{}, order_{} |
||||
{} |
||||
|
||||
// cppcheck-suppress passedByValue
|
||||
priority_task(std::function<void()> callback, std::size_t priority, cxxpool::detail::infinite_counter<counter_elem_t> order) |
||||
: callback_{std::move(callback)}, priority_(priority), order_{std::move(order)} |
||||
{} |
||||
|
||||
bool operator<(const priority_task& other) const { |
||||
if (priority_ == other.priority_) { |
||||
return order_ > other.order_; |
||||
} else { |
||||
return priority_ < other.priority_; |
||||
} |
||||
} |
||||
|
||||
void operator()() const { |
||||
return callback_(); |
||||
} |
||||
|
||||
private: |
||||
std::function<void()> callback_; |
||||
std::size_t priority_; |
||||
cxxpool::detail::infinite_counter<counter_elem_t> order_; |
||||
}; |
||||
|
||||
|
||||
} // detail
|
||||
|
||||
|
||||
// Exception thrown by the thread_pool class
|
||||
class thread_pool_error : public std::runtime_error { |
||||
public: |
||||
explicit thread_pool_error(const std::string& message) |
||||
: std::runtime_error{message} |
||||
{} |
||||
}; |
||||
|
||||
|
||||
// A thread pool for C++
|
||||
//
|
||||
// Constructing the thread pool launches the worker threads while
|
||||
// destructing it joins them. The threads will be alive for as long as the
|
||||
// thread pool is not destructed. One may call add_threads() to add more
|
||||
// threads to the thread pool.
|
||||
//
|
||||
// Tasks can be pushed into the pool with and w/o providing a priority >= 0.
|
||||
// Not providing a priority is equivalent to providing a priority of 0.
|
||||
// Those tasks are processed first that have the highest priority.
|
||||
// If priorities are equal those tasks are processed first that were pushed
|
||||
// first into the pool (FIFO).
|
||||
class thread_pool { |
||||
public: |
||||
|
||||
// Constructor. No threads are launched
|
||||
thread_pool() |
||||
: done_{false}, paused_{false}, threads_{}, tasks_{}, task_counter_{}, |
||||
task_cond_var_{}, task_mutex_{}, thread_mutex_{} |
||||
{} |
||||
|
||||
// Constructor. Launches the desired number of threads. Passing 0 is
|
||||
// equivalent to calling the no-argument constructor
|
||||
explicit thread_pool(std::size_t n_threads) |
||||
: thread_pool{} |
||||
{ |
||||
if (n_threads > 0) { |
||||
std::lock_guard<std::mutex> thread_lock(thread_mutex_); |
||||
const auto n_target = threads_.size() + n_threads; |
||||
while (threads_.size() < n_target) { |
||||
std::thread thread; |
||||
try { |
||||
thread = std::thread{&thread_pool::worker, this}; |
||||
} catch (...) { |
||||
shutdown(); |
||||
throw; |
||||
} |
||||
try { |
||||
threads_.push_back(std::move(thread)); |
||||
} catch (...) { |
||||
shutdown(); |
||||
thread.join(); |
||||
throw; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Destructor. Joins all threads launched. Waits for all running tasks
|
||||
// to complete
|
||||
~thread_pool() { |
||||
shutdown(); |
||||
} |
||||
|
||||
// deleting copy/move semantics
|
||||
thread_pool(const thread_pool&) = delete; |
||||
thread_pool& operator=(const thread_pool&) = delete; |
||||
thread_pool(thread_pool&&) = delete; |
||||
thread_pool& operator=(thread_pool&&) = delete; |
||||
|
||||
// Adds new threads to the pool and launches them
|
||||
void add_threads(std::size_t n_threads) { |
||||
if (n_threads > 0) { |
||||
{ |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
if (done_) |
||||
throw thread_pool_error{"add_threads called while pool is shutting down"}; |
||||
} |
||||
std::lock_guard<std::mutex> thread_lock(thread_mutex_); |
||||
const auto n_target = threads_.size() + n_threads; |
||||
while (threads_.size() < n_target) { |
||||
std::thread thread(&thread_pool::worker, this); |
||||
try { |
||||
threads_.push_back(std::move(thread)); |
||||
} catch (...) { |
||||
shutdown(); |
||||
thread.join(); |
||||
throw; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Returns the number of threads launched
|
||||
std::size_t n_threads() const { |
||||
{ |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
if (done_) |
||||
throw thread_pool_error{"n_threads called while pool is shutting down"}; |
||||
} |
||||
std::lock_guard<std::mutex> thread_lock(thread_mutex_); |
||||
return threads_.size(); |
||||
} |
||||
|
||||
// Pushes a new task into the thread pool and returns the associated future.
|
||||
// The task will have a priority of 0
|
||||
template<typename Functor, typename... Args> |
||||
auto push(Functor&& functor, Args&&... args) -> std::future<decltype(functor(args...))> { |
||||
return push(0, std::forward<Functor>(functor), std::forward<Args>(args)...); |
||||
} |
||||
|
||||
// Pushes a new task into the thread pool while providing a priority and
|
||||
// returns the associated future. Higher priorities are processed first
|
||||
template<typename Functor, typename... Args> |
||||
auto push(std::size_t priority, Functor&& functor, Args&&... args) -> std::future<decltype(functor(args...))> { |
||||
typedef decltype(functor(args...)) result_type; |
||||
auto pack_task = std::make_shared<std::packaged_task<result_type()>>( |
||||
std::bind(std::forward<Functor>(functor), std::forward<Args>(args)...)); |
||||
auto future = pack_task->get_future(); |
||||
{ |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
if (done_) |
||||
throw cxxpool::thread_pool_error{"push called while pool is shutting down"}; |
||||
tasks_.emplace([pack_task]{ (*pack_task)(); }, priority, ++task_counter_); |
||||
} |
||||
task_cond_var_.notify_one(); |
||||
return future; |
||||
} |
||||
|
||||
// Returns the current number of queued tasks
|
||||
std::size_t n_tasks() const { |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
return tasks_.size(); |
||||
} |
||||
|
||||
// Clears all queued tasks, not affecting currently running tasks
|
||||
void clear() { |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
decltype(tasks_) empty; |
||||
tasks_.swap(empty); |
||||
} |
||||
|
||||
// If enabled then pauses the processing of tasks, not affecting currently
|
||||
// running tasks. Disabling it will resume the processing of tasks
|
||||
void set_pause(bool enabled) { |
||||
{ |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
paused_ = enabled; |
||||
} |
||||
if (!paused_) |
||||
task_cond_var_.notify_all(); |
||||
} |
||||
|
||||
private: |
||||
|
||||
void worker() { |
||||
for (;;) { |
||||
cxxpool::detail::priority_task task; |
||||
{ |
||||
std::unique_lock<std::mutex> task_lock(task_mutex_); |
||||
task_cond_var_.wait(task_lock, [this]{ |
||||
return !paused_ && (done_ || !tasks_.empty()); |
||||
}); |
||||
if (done_ && tasks_.empty()) |
||||
break; |
||||
task = tasks_.top(); |
||||
tasks_.pop(); |
||||
} |
||||
task(); |
||||
} |
||||
} |
||||
|
||||
void shutdown() { |
||||
{ |
||||
std::lock_guard<std::mutex> task_lock(task_mutex_); |
||||
done_ = true; |
||||
paused_ = false; |
||||
} |
||||
task_cond_var_.notify_all(); |
||||
std::lock_guard<std::mutex> thread_lock(thread_mutex_); |
||||
for (auto& thread : threads_) |
||||
thread.join(); |
||||
} |
||||
|
||||
bool done_; |
||||
bool paused_; |
||||
std::vector<std::thread> threads_; |
||||
std::priority_queue<cxxpool::detail::priority_task> tasks_; |
||||
cxxpool::detail::infinite_counter< |
||||
typename detail::priority_task::counter_elem_t> task_counter_; |
||||
std::condition_variable task_cond_var_; |
||||
mutable std::mutex task_mutex_; |
||||
mutable std::mutex thread_mutex_; |
||||
}; |
||||
|
||||
|
||||
} // cxxpool
|
@ -0,0 +1,132 @@ |
||||
/*
|
||||
* Simple header-only thread pool implementation in modern C++. |
||||
* |
||||
* Created: Aug 9, 2020. |
||||
* Repository: https://github.com/leiless/threadpool.hpp
|
||||
* LICENSE: BSD-2-Clause |
||||
*/ |
||||
|
||||
#ifndef THE_THREADPOOL_HPP |
||||
#define THE_THREADPOOL_HPP |
||||
|
||||
#include <functional> |
||||
#include <mutex> |
||||
#include <condition_variable> |
||||
#include <queue> |
||||
#include <thread> |
||||
#include <future> |
||||
|
||||
#define THE_NAMESPACE_BEGIN(name) namespace name { |
||||
#define THE_NAMESPACE_END() } |
||||
|
||||
THE_NAMESPACE_BEGIN(concurrent) |
||||
|
||||
class threadpool { |
||||
public: |
||||
explicit threadpool(size_t threads) : alive(true) { |
||||
if (threads == 0) { |
||||
throw std::runtime_error("thread pool size cannot be zero"); |
||||
} |
||||
|
||||
for (auto i = 0llu; i < threads; i++) { |
||||
std::cerr << "work" << std::endl; |
||||
workers.emplace_back([this] { worker_main(); }); |
||||
} |
||||
} |
||||
|
||||
// see: https://stackoverflow.com/a/23771245/13600780
|
||||
threadpool(const threadpool &) = delete; |
||||
threadpool & operator=(const threadpool &) = delete; |
||||
|
||||
~threadpool() noexcept { |
||||
{ |
||||
std::lock_guard<decltype(mtx)> lock(mtx); |
||||
alive = false; |
||||
} |
||||
|
||||
cv.notify_all(); |
||||
|
||||
for (auto & worker : workers) { |
||||
worker.join(); |
||||
} |
||||
} |
||||
|
||||
template<typename Fn, typename... Args> |
||||
decltype(auto) enqueue(Fn && fn, Args &&... args) { |
||||
return enqueue(false, fn, args...); |
||||
} |
||||
|
||||
template<typename Fn, typename... Args> |
||||
decltype(auto) enqueue_r(Fn && fn, Args &&... args) { |
||||
return enqueue(true, fn, args...); |
||||
} |
||||
|
||||
private: |
||||
template<typename Fn, typename... Args> |
||||
decltype(auto) enqueue(bool block_on_shutdown, Fn && fn, Args &&... args) { |
||||
using return_type = std::invoke_result_t<Fn, Args...>; |
||||
using pack_task = std::packaged_task<return_type()>; |
||||
|
||||
auto t = std::make_shared<pack_task>( |
||||
std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...) |
||||
); |
||||
auto future = t->get_future(); |
||||
|
||||
{ |
||||
std::lock_guard<decltype(mtx)> lock(mtx); |
||||
if (!alive) { |
||||
throw std::runtime_error("enqueue on stopped thread pool"); |
||||
} |
||||
tasks.emplace([t = std::move(t)] { (*t)(); }, block_on_shutdown); |
||||
} |
||||
|
||||
cv.notify_one(); |
||||
return future; |
||||
} |
||||
|
||||
using task = std::pair<std::function<void()>, bool>; |
||||
|
||||
[[nodiscard]] inline task poll_task() noexcept { |
||||
task t; |
||||
|
||||
std::unique_lock<decltype(mtx)> lock(mtx); |
||||
cv.wait(lock, [this] { return !tasks.empty() || !alive; }); |
||||
|
||||
while (!tasks.empty()) { |
||||
if (!alive && !tasks.front().second) { |
||||
tasks.pop(); |
||||
continue; |
||||
} |
||||
|
||||
t = std::move(tasks.front()); |
||||
tasks.pop(); |
||||
break; |
||||
} |
||||
|
||||
return t; |
||||
} |
||||
|
||||
void worker_main() { |
||||
while (true) { |
||||
std::cerr << "I" << std::endl; |
||||
task t = poll_task(); |
||||
std::cerr << "II" << std::endl; |
||||
// The thread pool is going to shutdown
|
||||
if (t.first == nullptr) break; |
||||
std::cerr << "III" << std::endl; |
||||
t.first(); |
||||
std::cerr << "IIII" << std::endl; |
||||
} |
||||
} |
||||
|
||||
bool alive; |
||||
std::mutex mtx; |
||||
std::condition_variable cv; |
||||
std::queue<task> tasks; |
||||
std::vector<std::thread> workers; |
||||
}; |
||||
|
||||
THE_NAMESPACE_END() |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,45 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
// Copyright Amir Hassan (kallaballa) <amir@viel-zu.org>
|
||||
|
||||
#include "nanoguicontext.hpp" |
||||
|
||||
namespace cv { |
||||
namespace v4d { |
||||
namespace detail { |
||||
NanoguiContext::NanoguiContext(V4D& v4d, FrameBufferContext& fbContext) : |
||||
mainFbContext_(fbContext), nguiFbContext_(v4d, "NanoGUI", fbContext) { |
||||
fbCtx().makeCurrent(); |
||||
screen_ = new nanogui::Screen(); |
||||
screen_->initialize(nguiFbContext_.getGLFWWindow(), false); |
||||
form_ = new cv::v4d::FormHelper(screen_); |
||||
fbCtx().resizeWindow(fbCtx().getSize()); |
||||
fbCtx().makeNoneCurrent(); |
||||
} |
||||
|
||||
void NanoguiContext::render() { |
||||
screen().draw_widgets(); |
||||
} |
||||
|
||||
void NanoguiContext::build(std::function<void(cv::v4d::FormHelper&)> fn) { |
||||
fbCtx().makeCurrent(); |
||||
fn(form()); |
||||
screen().perform_layout(); |
||||
fbCtx().makeNoneCurrent(); |
||||
} |
||||
|
||||
nanogui::Screen& NanoguiContext::screen() { |
||||
return *screen_; |
||||
} |
||||
|
||||
cv::v4d::FormHelper& NanoguiContext::form() { |
||||
return *form_; |
||||
} |
||||
|
||||
FrameBufferContext& NanoguiContext::fbCtx() { |
||||
return nguiFbContext_; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
// Copyright Amir Hassan (kallaballa) <amir@viel-zu.org>
|
||||
|
||||
#ifndef SRC_OPENCV_NANOGUICONTEXT_HPP_ |
||||
#define SRC_OPENCV_NANOGUICONTEXT_HPP_ |
||||
|
||||
#include "framebuffercontext.hpp" |
||||
|
||||
#ifdef __EMSCRIPTEN__ |
||||
#include <emscripten.h> |
||||
#endif |
||||
|
||||
#include <opencv2/v4d/formhelper.hpp> |
||||
|
||||
namespace cv { |
||||
namespace v4d { |
||||
namespace detail { |
||||
/*!
|
||||
* Used to setup a nanogui context |
||||
*/ |
||||
class NanoguiContext { |
||||
nanogui::Screen* screen_; |
||||
cv::v4d::FormHelper* form_; |
||||
FrameBufferContext& mainFbContext_; |
||||
FrameBufferContext nguiFbContext_; |
||||
cv::UMat preFB_; |
||||
cv::UMat fb_; |
||||
cv::UMat postFB_; |
||||
public: |
||||
NanoguiContext(V4D& v4d, FrameBufferContext& fbContext); |
||||
void render(); |
||||
void build(std::function<void(cv::v4d::FormHelper&)> fn); |
||||
nanogui::Screen& screen(); |
||||
cv::v4d::FormHelper& form(); |
||||
|
||||
FrameBufferContext& fbCtx(); |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif /* SRC_OPENCV_NANOGUICONTEXT_HPP_ */ |
@ -0,0 +1,16 @@ |
||||
# Display an image using direct framebuffer access {#v4d_display_image_fb} |
||||
|
||||
@prev_tutorial{v4d_display_image_pipeline} |
||||
@next_tutorial{v4d_vector_graphics} |
||||
|
||||
| | | |
||||
| -: | :- | |
||||
| Original author | Amir Hassan (kallaballa) <amir@viel-zu.org> | |
||||
| Compatibility | OpenCV >= 4.7 | |
||||
|
||||
## Using direct framebuffer access |
||||
Instead of feeding to the video pipeline we can request the framebuffer in a ```fb``` context and copy the image to it. But first we must manually resize and color convert the image. |
||||
|
||||
\htmlinclude "../samples/example_v4d_display_image_fb.html" |
||||
|
||||
@include samples/display_image_fb.cpp |
@ -0,0 +1,16 @@ |
||||
# Render vector graphics {#v4d_vector_graphics} |
||||
|
||||
@prev_tutorial{v4d_display_image_fb} |
||||
@next_tutorial{v4d_vector_graphics_and_fb} |
||||
|
||||
| | | |
||||
| -: | :- | |
||||
| Original author | Amir Hassan (kallaballa) <amir@viel-zu.org> | |
||||
| Compatibility | OpenCV >= 4.7 | |
||||
|
||||
## Vector graphics |
||||
Through the nvg context javascript-canvas-like rendering is possible. |
||||
|
||||
\htmlinclude "../samples/example_v4d_vector_graphics.html" |
||||
|
||||
@include samples/vector_graphics.cpp |
Loading…
Reference in new issue