ported multi-context rendering to the new api

pull/3471/head
kallaballa 2 years ago
parent 050977370d
commit 66966406a3
  1. 5
      modules/v4d/include/opencv2/v4d/detail/glcontext.hpp
  2. 6
      modules/v4d/include/opencv2/v4d/util.hpp
  3. 60
      modules/v4d/include/opencv2/v4d/v4d.hpp
  4. 90
      modules/v4d/samples/many_cubes-demo.cpp
  5. 4
      modules/v4d/samples/video-demo.cpp
  6. 7
      modules/v4d/src/detail/glcontext.cpp
  7. 5
      modules/v4d/src/v4d.cpp

@ -20,6 +20,7 @@ namespace detail {
* Used to setup an OpengLG context
*/
class CV_EXPORTS GLContext : public V4DContext {
const int32_t idx_;
cv::Ptr<FrameBufferContext> mainFbContext_;
cv::Ptr<FrameBufferContext> glFbContext_;
public:
@ -27,7 +28,7 @@ public:
* Creates a OpenGL Context
* @param fbContext The framebuffer context
*/
GLContext(cv::Ptr<FrameBufferContext> fbContext);
GLContext(const int32_t& idx, cv::Ptr<FrameBufferContext> fbContext);
virtual ~GLContext() {};
/*!
* Execute function object fn inside a gl context.
@ -36,7 +37,7 @@ public:
* and performs drawing using opengl
*/
virtual void execute(std::function<void()> fn) override;
const int32_t& getIndex() const;
cv::Ptr<FrameBufferContext> fbCtx();
};
}

@ -217,10 +217,8 @@ struct Lambda {
template<typename T>
static const void* fn(const void* new_fn = nullptr) {
thread_local const void* fn;
if (new_fn != nullptr)
fn = new_fn;
return fn;
CV_Assert(new_fn);
return new_fn;
}
};

@ -82,22 +82,15 @@ namespace detail {
template <typename T> using static_not = std::integral_constant<bool, !T::value>;
//https://stackoverflow.com/questions/19961873/test-if-a-lambda-is-stateless#:~:text=As%20per%20the%20Standard%2C%20if,lambda%20is%20stateless%20or%20not.
template <typename T, typename U>
struct helper : helper<T, decltype(&U::operator())>
{};
template <typename T, typename C, typename R, typename... A>
struct helper<T, R(C::*)(A...) const>
template<typename T, typename ... Args>
struct is_function
{
static const bool value = std::is_convertible<T, std::function<R(A...)>>::value || std::is_convertible<T, R(*)(A...)>::value;
static const bool value = std::is_constructible<T,std::function<void(Args...)>>::value;
};
template<typename T>
struct is_stateless
{
static const bool value = helper<T,T>::value;
};
//https://stackoverflow.com/a/34873353/1884837
template<class T>
struct is_stateless_lambda : std::integral_constant<bool, sizeof(T) == sizeof(std::true_type)>{};
template<typename T> std::string int_to_hex( T i )
{
@ -108,13 +101,10 @@ template<typename T> std::string int_to_hex( T i )
return stream.str();
}
//template<typename Tfn> std::string func_hex(Tfn& fn) {
// return int_to_hex((size_t) &fn);
//}
template<typename Tlamba> std::string lambda_ptr_hex(Tlamba&& l) {
return int_to_hex((size_t)Lambda::ptr(l));
}
}
using namespace cv::v4d::detail;
@ -342,8 +332,9 @@ public:
}
template <typename Tfn, typename ... Args>
void gl(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
typename std::enable_if<std::is_invocable_v<Tfn>, void>::type
gl(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("gl", fn, -1);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...](){
emit_access<std::true_type, cv::UMat, Args...>(id, true, &fbCtx()->fb());
@ -355,21 +346,22 @@ public:
}
template <typename Tfn, typename ... Args>
void gl(const size_t& idx, Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
void gl(int32_t idx, Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("gl", fn, idx);
TimeTracker::getInstance()->execute(id, [this, fn, idx, id, &args...](){
emit_access<std::true_type, cv::UMat, Args...>(id, true, &fbCtx()->fb());
(emit_access<std::true_type, std::remove_reference_t<Args>, Args...>(id, std::is_const_v<std::remove_reference_t<Args>>, &args),...);
emit_access<std::true_type, cv::UMat, Args...>(id, false, &fbCtx()->fb());
std::function functor(fn);
add_transaction(glCtx(idx), id, std::forward<decltype(functor)>(fn), std::forward<Args>(args)...);
std::function<void((const int32_t&,Args...))> functor(fn);
add_transaction<decltype(functor),const int32_t&>(fbCtx(),id, std::forward<decltype(functor)>(functor), glCtx(idx)->getIndex(), std::forward<Args>(args)...);
});
}
template <typename Tfn>
void branch(Tfn fn) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("branch", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id](){
@ -381,7 +373,7 @@ public:
template <typename Tfn, typename ... Args>
void branch(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("branch", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...](){
@ -393,7 +385,7 @@ public:
template <typename Tfn>
void endbranch(Tfn fn) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("endbranch", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id] {
@ -405,7 +397,7 @@ public:
template <typename Tfn, typename ... Args>
void endbranch(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("endbranch", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...](){
@ -417,7 +409,7 @@ public:
template <typename Tfn, typename ... Args>
void fb(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("fb", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...]{
using Tfb = std::add_lvalue_reference_t<typename std::tuple_element<0, typename function_traits<Tfn>::argument_types>::type>;
@ -455,7 +447,7 @@ public:
template <typename Tfn, typename ... Args>
void capture(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("capture", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...]{
using Tfb = std::add_lvalue_reference_t<typename std::tuple_element<0, typename function_traits<Tfn>::argument_types>::type>;
@ -488,7 +480,7 @@ public:
template <typename Tfn, typename ... Args>
void write(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("write", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...]{
using Tfb = std::add_lvalue_reference_t<typename std::tuple_element<0, typename function_traits<Tfn>::argument_types>::type>;
@ -504,7 +496,7 @@ public:
template <typename Tfn, typename ... Args>
void nvg(Tfn fn, Args&&... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("nvg", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...](){
emit_access<std::true_type, cv::UMat, Args...>(id, true, &fbCtx()->fb());
@ -517,7 +509,7 @@ public:
template <typename Tfn, typename ... Args>
void single(Tfn fn, Args&&... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("single", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...](){
(emit_access<std::true_type, std::remove_reference_t<Args>, Args...>(id, std::is_const_v<std::remove_reference_t<Args>>, &args),...);
@ -528,7 +520,7 @@ public:
template <typename Tfn, typename ... Args>
void parallel(Tfn fn, Args&&... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
const string id = make_id("parallel", fn);
TimeTracker::getInstance()->execute(id, [this, fn, id, &args...](){
(emit_access<std::true_type, std::remove_reference_t<Args>, Args...>(id, std::is_const_v<std::remove_reference_t<Args>>, &args),...);
@ -539,7 +531,7 @@ public:
template<typename Tfn, typename ... Args>
void imgui(Tfn fn, Args&& ... args) {
CV_Assert(detail::is_stateless<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
CV_Assert(detail::is_stateless_lambda<std::remove_cv_t<std::remove_reference_t<decltype(fn)>>>::value);
auto s = self();
imguiCtx()->build([s, fn, &args...](ImGuiContext* ctx) {
fn(s, ctx, args...);

@ -7,7 +7,6 @@
//adapted from https://gitlab.com/wikibooks-opengl/modern-tutorials/-/blob/master/tut05_cube/cube.cpp
/* Demo Parameters */
#ifndef __EMSCRIPTEN__
constexpr size_t NUMBER_OF_CUBES = 10;
constexpr long unsigned int WIDTH = 1280;
@ -23,7 +22,7 @@ constexpr double FPS = 60;
constexpr const char* OUTPUT_FILENAME = "many_cubes-demo.mkv";
#endif
const unsigned long DIAG = hypot(double(WIDTH), double(HEIGHT));
const int glow_kernel_size = std::max(int(DIAG / 138 % 2 == 0 ? DIAG / 138 + 1 : DIAG / 138), 1);
const int GLOW_KERNEL_SIZE = std::max(int(DIAG / 138 % 2 == 0 ? DIAG / 138 + 1 : DIAG / 138), 1);
using std::cerr;
using std::endl;
@ -65,15 +64,29 @@ const unsigned short triangle_indices[] = {
// Top
5, 1, 0, 0, 4, 5
};
//Simple transform & pass-through shaders
static GLuint load_shader() {
using namespace cv::v4d;
class ManyCubesDemoPlan : public Plan {
struct Cache {
cv::UMat down_;
cv::UMat up_;
cv::UMat blur_;
cv::UMat dst16_;
} cache_;
cv::UMat frame_;
GLuint vao[NUMBER_OF_CUBES];
GLuint shaderProgram[NUMBER_OF_CUBES];
GLuint uniformTransform[NUMBER_OF_CUBES];
cv::Size sz_;
//Simple transform & pass-through shaders
static GLuint load_shader() {
//Shader versions "330" and "300 es" are very similar.
//If you are careful you can write the same code for both versions.
#if !defined(__EMSCRIPTEN__) && !defined(OPENCV_V4D_USE_ES3)
#if !defined(__EMSCRIPTEN__) && !defined(OPENCV_V4D_USE_ES3)
const string shaderVersion = "330";
#else
#else
const string shaderVersion = "300 es";
#endif
#endif
const string vert =
" #version " + shaderVersion
@ -89,7 +102,7 @@ static GLuint load_shader() {
gl_Position = transform * vec4(pos, 1.0);
color = vertex_color;
}
)";
)";
const string frag =
" #version " + shaderVersion
@ -102,14 +115,14 @@ static GLuint load_shader() {
void main() {
frag_color = vec4(color, 1.0);
}
)";
)";
//Initialize the shaders and returns the program
return cv::v4d::initShader(vert.c_str(), frag.c_str(), "fragColor");
}
}
//Initializes objects, buffers, shaders and uniforms
static void init_scene(const cv::Size& sz, GLuint& vao, GLuint& shaderProgram, GLuint& uniformTransform) {
//Initializes objects, buffers, shaders and uniforms
static void init_scene(const cv::Size& sz, GLuint& vao, GLuint& shaderProgram, GLuint& uniformTransform) {
glEnable (GL_DEPTH_TEST);
glGenVertexArrays(1, &vao);
@ -144,10 +157,10 @@ static void init_scene(const cv::Size& sz, GLuint& vao, GLuint& shaderProgram, G
shaderProgram = load_shader();
uniformTransform = glGetUniformLocation(shaderProgram, "transform");
glViewport(0,0, sz.width, sz.height);
}
}
//Renders a rotating rainbow-colored cube on a blueish background
static void render_scene(const double& x, const double& y, const double& angleMod, GLuint& vao, GLuint& shaderProgram, GLuint& uniformTransform) {
//Renders a rotating rainbow-colored cube on a blueish background
static void render_scene(const double& x, const double& y, const double& angleMod, GLuint& vao, GLuint& shaderProgram, GLuint& uniformTransform) {
//Use the prepared shader program
glUseProgram(shaderProgram);
@ -192,46 +205,37 @@ static void render_scene(const double& x, const double& y, const double& angleMo
glBindVertexArray(vao);
//Draw
glDrawElements(GL_TRIANGLES, triangles * 3, GL_UNSIGNED_SHORT, NULL);
}
}
#ifndef __EMSCRIPTEN__
//applies a glow effect to an image
static void glow_effect(const cv::UMat& src, cv::UMat& dst, const int ksize) {
thread_local cv::UMat resize;
thread_local cv::UMat blur;
thread_local cv::UMat dst16;
//applies a glow effect to an image
static void glow_effect(const cv::UMat& src, cv::UMat& dst, const int ksize, Cache& cache) {
cv::bitwise_not(src, dst);
//Resize for some extra performance
cv::resize(dst, resize, cv::Size(), 0.5, 0.5);
cv::resize(dst, cache.down_, cv::Size(), 0.5, 0.5);
//Cheap blur
cv::boxFilter(resize, resize, -1, cv::Size(ksize, ksize), cv::Point(-1, -1), true,
cv::boxFilter(cache.down_, cache.blur_, -1, cv::Size(ksize, ksize), cv::Point(-1, -1), true,
cv::BORDER_REPLICATE);
//Back to original size
cv::resize(resize, blur, src.size());
cv::resize(cache.blur_, cache.up_, src.size());
//Multiply the src image with a blurred version of itself
cv::multiply(dst, blur, dst16, 1, CV_16U);
cv::multiply(dst, cache.up_, cache.dst16_, 1, CV_16U);
//Normalize and convert back to CV_8U
cv::divide(dst16, cv::Scalar::all(255.0), dst, 1, CV_8U);
cv::divide(cache.dst16_, cv::Scalar::all(255.0), dst, 1, CV_8U);
cv::bitwise_not(dst, dst);
}
}
#endif
using namespace cv::v4d;
class ManyCubesDemoPlan : public Plan {
cv::UMat frame_;
GLuint vao[NUMBER_OF_CUBES];
GLuint shaderProgram[NUMBER_OF_CUBES];
GLuint uniformTransform[NUMBER_OF_CUBES];
public:
void setup(cv::Ptr<V4D> window) {
sz_ = window->fbSize();
for(size_t i = 0; i < NUMBER_OF_CUBES; ++i) {
window->gl(i, [](const cv::Size& sz, GLuint& v, GLuint& sp, GLuint& ut){
window->gl(i, [](const size_t& ctxIdx, cv::Size& sz, GLuint& v, GLuint& sp, GLuint& ut){
CV_UNUSED(ctxIdx);
init_scene(sz, v, sp, ut);
}, window->fbSize(), vao[i], shaderProgram[i], uniformTransform[i]);
}, sz_, vao[i], shaderProgram[i], uniformTransform[i]);
}
}
void infer(cv::Ptr<V4D> window) {
@ -243,20 +247,20 @@ public:
//Render using multiple OpenGL contexts
for(size_t i = 0; i < NUMBER_OF_CUBES; ++i) {
window->gl(i, [i](GLuint& v, GLuint& sp, GLuint& ut){
double pos = (((double(i) / NUMBER_OF_CUBES) * 2.0) - 1) + (1.0 / NUMBER_OF_CUBES);
double angle = sin((double(i) / NUMBER_OF_CUBES) * 2 * M_PI);
window->gl(i, [](const int32_t& ctxIdx, GLuint& v, GLuint& sp, GLuint& ut){
double pos = (((double(ctxIdx) / NUMBER_OF_CUBES) * 2.0) - 1) + (1.0 / NUMBER_OF_CUBES);
double angle = sin((double(ctxIdx) / NUMBER_OF_CUBES) * 2 * M_PI);
render_scene(pos, pos, angle, v, sp, ut);
}, vao[i], shaderProgram[i], uniformTransform[i]);
}
//Aquire the frame buffer for use by OpenCV
window->fb([](cv::UMat& framebuffer, cv::UMat& f) {
window->fb([](cv::UMat& framebuffer, cv::UMat& f, Cache& cache) {
#ifndef __EMSCRIPTEN__
glow_effect(framebuffer, framebuffer, glow_kernel_size);
glow_effect(framebuffer, framebuffer, GLOW_KERNEL_SIZE, cache);
#endif
framebuffer.copyTo(f);
}, frame_);
}, frame_, cache_);
window->write([](cv::UMat& outputFrame, const cv::UMat& f){
f.copyTo(outputFrame);

@ -24,7 +24,7 @@ constexpr long unsigned int HEIGHT = 960;
#endif
constexpr bool OFFSCREEN = false;
const unsigned long DIAG = hypot(double(WIDTH), double(HEIGHT));
const int glow_kernel_size = std::max(int(DIAG / 138 % 2 == 0 ? DIAG / 138 + 1 : DIAG / 138), 1);
const int GLOW_KERNEL_SIZE = std::max(int(DIAG / 138 % 2 == 0 ? DIAG / 138 + 1 : DIAG / 138), 1);
#ifndef __EMSCRIPTEN__
constexpr double FPS = 60;
constexpr const char* OUTPUT_FILENAME = "video-demo.mkv";
@ -205,7 +205,7 @@ public:
#ifndef __EMSCRIPTEN__
window->fb([](cv::UMat &framebuffer) {
glow_effect(framebuffer, framebuffer, glow_kernel_size);
glow_effect(framebuffer, framebuffer, GLOW_KERNEL_SIZE);
});
#endif

@ -9,8 +9,8 @@
namespace cv {
namespace v4d {
namespace detail {
GLContext::GLContext(cv::Ptr<FrameBufferContext> fbContext) :
mainFbContext_(fbContext), glFbContext_(new FrameBufferContext(*fbContext->getV4D(), "OpenGL", *fbContext)) {
GLContext::GLContext(const int32_t& idx, cv::Ptr<FrameBufferContext> fbContext) :
idx_(idx), mainFbContext_(fbContext), glFbContext_(new FrameBufferContext(*fbContext->getV4D(), "OpenGL", *fbContext)) {
#ifdef __EMSCRIPTEN__
run_sync_on_main<19>([&,this](){
mainFbContext_->initWebGLCopy(fbCtx()->getIndex());
@ -52,6 +52,9 @@ void GLContext::execute(std::function<void()> fn) {
});
}
const int32_t& GLContext::getIndex() const {
return idx_;
}
cv::Ptr<FrameBufferContext> GLContext::fbCtx() {
return glFbContext_;
}

@ -46,6 +46,9 @@ V4D::V4D(const cv::Size& size, const cv::Size& fbsize, const string& title, Allo
parallelContext_ = new detail::ParallelContext();
if(flags & IMGUI)
imguiContext_ = new detail::ImGuiContextImpl(mainFbContext_);
//preallocate the primary gl context
glCtx(-1);
}
V4D::~V4D() {
@ -113,7 +116,7 @@ cv::Ptr<GLContext> V4D::glCtx(int32_t idx) {
if(it != glContexts_.end())
return (*it).second;
else {
cv::Ptr<GLContext> ctx = new GLContext(mainFbContext_);
cv::Ptr<GLContext> ctx = new GLContext(idx, mainFbContext_);
glContexts_.insert({idx, ctx});
return ctx;
}

Loading…
Cancel
Save