From da06a452d3deda40f746ffa81981271240e3b7ab Mon Sep 17 00:00:00 2001 From: namark <namark@disroot.org> Date: Mon, 27 Jul 2020 04:54:00 +0400 Subject: [PATCH] nanovg framebuffers. --- common/simple_vg.cpp | 106 +++++++++++++++++++++++++++++++++++++++--- common/simple_vg.h | 60 ++++++++++++++++++++++-- common/sketchbook.hpp | 78 ++++++++++++++++++++++++++++--- 3 files changed, 228 insertions(+), 16 deletions(-) diff --git a/common/simple_vg.cpp b/common/simple_vg.cpp index 047cb8a..64192ac 100644 --- a/common/simple_vg.cpp +++ b/common/simple_vg.cpp @@ -1,5 +1,6 @@ #include "simple_vg.h" #include "simple/support/enum.hpp" +#include "simple/support/algorithm.hpp" using namespace simple::vg; @@ -46,12 +47,55 @@ frame canvas::begin_frame(float2 size, float pixelRatio) noexcept return frame(raw.get(), size, pixelRatio); } -frame::frame(NVGcontext* context, float2 size, float pixelRatio) noexcept : +frame canvas::begin_frame(framebuffer& fb) const noexcept +{ + return frame(raw.get(), fb); +} + +framebuffer::framebuffer(int2 size, enum flags flags) noexcept : + flags(flags), size(size), - pixelRatio(pixelRatio), - context(context) + raw(nullptr) +{} +framebuffer::framebuffer(const canvas& canvas, int2 size, enum flags flags) : + framebuffer(size, flags) { - nvgBeginFrame(context, size.x(), size.y(), pixelRatio); + create(canvas); +} + +bool framebuffer::create(const canvas& canvas) +{ + if(raw) + return false; + + raw = decltype(raw)( + nvgluCreateFramebuffer( + canvas.raw.get(), + size.x(), size.y(), + support::to_integer(flags) + ) + ); + return true; +} + +simple::vg::paint framebuffer::paint(simple::support::range<int2> range, float opacity, float angle) const +{ + auto size = range.upper() - range.lower(); + return nvgImagePattern(nullptr, + range.lower().x(), range.lower().y(), + size.x(), size.y(), + angle, raw->image, opacity + ); +} + +simple::vg::paint framebuffer::paint(float opacity, float angle) const +{ + return paint({int2::zero(), size}, opacity, angle); +} + +void framebuffer::deleter::operator()(NVGLUframebuffer* raw) const noexcept +{ + nvgluDeleteFramebuffer(raw); } paint::paint(NVGpaint raw) noexcept : raw(raw) {} @@ -67,9 +111,45 @@ paint paint::radial_gradient(float2 center, rangef radius, support::range<rgba_v )); } +paint paint::radial_gradient(range2f bounds, rangef radius, support::range<rgba_vector> colors) noexcept +{ + auto size = (bounds.upper() - bounds.lower())/2; + return radial_gradient( + bounds.lower() + size, + radius * size.x(), + colors + ); +} + +frame::frame(NVGcontext* context, float2 size, float pixelRatio) noexcept : + size(size), + pixelRatio(pixelRatio), + buffer(nullptr), + context(context) +{ + nvgBeginFrame(context, size.x(), size.y(), pixelRatio); +} + +frame::frame(NVGcontext* context, const framebuffer& fb) noexcept : + size(fb.size), + pixelRatio(1), + buffer(&fb), + context(context) +{ + nvgluBindFramebuffer(buffer->raw.get()); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glViewport( + 0,0, + buffer->size.x(), buffer->size.y() + ); + nvgBeginFrame(context, size.x(), size.y(), pixelRatio); +} + frame::frame(frame&& other) noexcept : size(other.size), pixelRatio(other.pixelRatio), + buffer(other.buffer), context(other.context) { other.context = nullptr; @@ -79,6 +159,8 @@ frame::~frame() noexcept { if(context) nvgEndFrame(context); + if(buffer) + nvgluBindFramebuffer(nullptr); } sketch frame::begin_sketch() noexcept @@ -133,8 +215,20 @@ sketch& sketch::rectangle(const range2f& bounds) noexcept sketch& sketch::line(float2 from, float2 to) noexcept { - nvgMoveTo(context, from.x(), from.y()); - nvgLineTo(context, to.x(), to.y()); + move(from); + vertex(to); + return *this; +} + +sketch& sketch::move(float2 to) noexcept +{ + nvgMoveTo(context, to.x(), to.y()); + return *this; +} + +sketch& sketch::vertex(float2 v) noexcept +{ + nvgLineTo(context, v.x(), v.y()); return *this; } diff --git a/common/simple_vg.h b/common/simple_vg.h index 41eb6be..885df64 100644 --- a/common/simple_vg.h +++ b/common/simple_vg.h @@ -11,6 +11,7 @@ namespace simple::vg { using float2 = geom::vector<float, 2>; + using int2 = geom::vector<int, 2>; using graphical::rgb_vector; using graphical::rgba_vector; using graphical::rgb_pixel; @@ -20,6 +21,10 @@ namespace simple::vg using rect2f = geom::segment<float2>; using anchored_rect2f = geom::anchored_segment<float2>; + class frame; + class framebuffer; + class sketch; + class paint { NVGpaint raw; @@ -28,13 +33,13 @@ namespace simple::vg public: paint() = delete; static paint radial_gradient(float2 center, rangef radius, support::range<rgba_vector>) noexcept; + static paint radial_gradient(range2f range, rangef radius, support::range<rgba_vector>) noexcept; static paint linear_gradient() noexcept; // TODO static paint range_gradient() noexcept; // TODO friend class sketch; + friend class framebuffer; }; - class frame; - class canvas { public: @@ -53,6 +58,7 @@ namespace simple::vg canvas& clear(const rgba_pixel& color) noexcept; frame begin_frame(float2 size, float pixelRatio = 1) noexcept; + frame begin_frame(framebuffer&) const noexcept; private: @@ -62,13 +68,51 @@ namespace simple::vg }; std::unique_ptr<NVGcontext, deleter> raw; + + friend class framebuffer; }; using ::operator |; using ::operator &; using ::operator &&; - class sketch; + class framebuffer + { + public: + enum class flags : + std::underlying_type_t<NVGimageFlags> + { + none = 0, + mipmap = NVG_IMAGE_GENERATE_MIPMAPS, + repeat_x = NVG_IMAGE_REPEATX, + repeat_y = NVG_IMAGE_REPEATY, + flip_y = NVG_IMAGE_FLIPY, + premultiplied = NVG_IMAGE_PREMULTIPLIED, + nearest = NVG_IMAGE_NEAREST + }; + + const flags flags; + const int2 size; + + framebuffer(int2 size, enum flags = flags::none) noexcept; + framebuffer(const canvas&, int2 size, enum flags = flags::none); + bool create(const canvas&); + + vg::paint paint(support::range<int2>, float opacity = 1, float angle = 0) const; + vg::paint paint(float opacity = 1, float angle = 0) const; + private: + + struct deleter + { + void operator()(NVGLUframebuffer*) const noexcept; + }; + + std::unique_ptr<NVGLUframebuffer, deleter> raw; + + friend class frame; + }; + + // TODO: prevent two frames with same context from coexisting class frame { public: @@ -78,9 +122,11 @@ namespace simple::vg frame(frame&&) noexcept; const float2 size; const float pixelRatio; + const framebuffer * const buffer; private: NVGcontext* context; frame(NVGcontext*, float2 size, float pixelRatio = 1) noexcept; + frame(NVGcontext*, const framebuffer&) noexcept; friend class canvas; }; @@ -106,6 +152,11 @@ namespace simple::vg sketch& line(float2 from, float2 to) noexcept; sketch& arc(float2 center, rangef angle, float radius) noexcept; sketch& arc(float2 center, float2 radius, float tau_factor, float anchor = 0) noexcept; // TODO + sketch& move(float2) noexcept; + sketch& vertex(float2) noexcept; + + sketch& scale(float2) noexcept; + sketch& reset_matrix() noexcept; sketch& fill(const paint&) noexcept; sketch& fill(const rgba_vector&) noexcept; @@ -134,4 +185,7 @@ namespace simple::vg template<> struct simple::support::define_enum_flags_operators<simple::vg::canvas::flags> : std::true_type {}; +template<> struct simple::support::define_enum_flags_operators<enum simple::vg::framebuffer::flags> + : std::true_type {}; + #endif /* end of include guard */ diff --git a/common/sketchbook.hpp b/common/sketchbook.hpp index eb93813..48e6bcb 100644 --- a/common/sketchbook.hpp +++ b/common/sketchbook.hpp @@ -8,6 +8,7 @@ #include <vector> #include <array> #include <queue> +#include <list> #include "simple/support.hpp" #include "simple/graphical.hpp" @@ -16,13 +17,18 @@ #include "simple/interactive/event.h" #include "simple_vg.h" -#include "simple_vg.cpp" +#include "simple_vg.cpp" // TODO: woops, don't do this #include "math.hpp" #if defined __EMSCRIPTEN__ #include <emscripten.h> #endif +#if defined __ANDROID__ +#undef main +#endif + +using namespace simple::vg; using namespace std::chrono_literals; using namespace simple; using namespace graphical::color_literals; @@ -44,6 +50,7 @@ using scancode = interactive::scancode; using keycode = interactive::keycode; using mouse_button = interactive::mouse_button; using common::lerp; +using simple::support::make_range; constexpr int max_int = std::numeric_limits<int>::max(); @@ -51,6 +58,7 @@ support::random::engine::tiny<unsigned> tiny_rand{std::random_device{}}; support::random::distribution::naive_int<int> tiny_int_dist{0, max_int}; support::random::distribution::naive_real<float> tiny_float_dist{0, 1}; +constexpr auto trand_int(decltype(tiny_int_dist)::param_type range = {0, max_int}) { return tiny_int_dist(tiny_rand, range); }; @@ -95,6 +103,16 @@ class Program std::array<wave, 32> waves = {}; std::mutex dam; + std::list<std::pair<framebuffer, draw_fun>> framebuffers; + void create_framebuffers(const canvas& canvas) + { + for(auto&& [fb, draw] : framebuffers) + { + fb.create(canvas); + draw(canvas.begin_frame(fb)); + } + } + bool run = true; Program(const int argc, const char * const * const argv) : argc(argc), argv(argv) {} @@ -106,6 +124,7 @@ class Program std::string name = ""; int2 size = int2(400,400); bool fullscreen = false; + graphical::display::mode display; // nop works ok with function pointers without having to specify template params :/ @@ -152,8 +171,30 @@ class Program current.remaining = current.total; } + const framebuffer& request_framebuffer(int2 size, draw_fun draw,enum framebuffer::flags flags = framebuffer::flags::none) + { + framebuffers.emplace_back( + std::make_pair( + framebuffer(size, flags), + std::move(draw) + ) + ); + return framebuffers.back().first; + } + + void remove_framebuffer(const framebuffer& framebuffer) + { + framebuffers.erase(std::find_if( + framebuffers.begin(), + framebuffers.end(), + [&](auto&& fb) + { + return &fb.first == &framebuffer; + } + )); + } - friend int main(int argc, char const* argv[]); + friend int main(int argc, char* argv[]); }; inline void process_events(Program&); @@ -163,12 +204,15 @@ inline void process_events(Program&); void start(Program&); -int main(int argc, char const* argv[]) try +int main(int argc, char* argv[]) try { Program program{argc, argv}; - start(program); + graphical::initializer graphics; + program.display = (*graphics.displays().begin()).current_mode(); + start(program); + sdlcore::initializer interactions(sdlcore::system_flag::event); // TODO: make optional @@ -235,13 +279,17 @@ int main(int argc, char const* argv[]) try std::cout << "vsync didn't work" << '\n'; program.frametime = framerate<60>::frametime; } - float2 win_size = float2(win.size()); +#if !defined NANOVG_GLES2 && !defined NANOVG_GLES3 glewInit(); +#endif auto canvas = vg::canvas(vg::canvas::flags::antialias | vg::canvas::flags::stencil_strokes); canvas.clear(); - program.draw_once(canvas.begin_frame(win_size)); + program.create_framebuffers(canvas); + auto stolen_framebuffers = std::move(program.framebuffers); + glViewport(0,0, win.size().x(), win.size().y()); + program.draw_once(canvas.begin_frame(float2(win.size()))); auto& now = Program::clock::now; auto frame_start = now(); @@ -251,7 +299,8 @@ int main(int argc, char const* argv[]) try frame_start = now(); process_events(program); - program.draw_loop(canvas.begin_frame(win_size), delta_time); + canvas.clear(); + program.draw_loop(canvas.begin_frame(float2(win.size())), delta_time); win.update(); }; #if defined __EMSCRIPTEN__ @@ -317,6 +366,21 @@ void process_events(Program& program) { program.end(); }, + [](const window_size_changed& w) + { + glViewport(0,0, + w.data.value.x(), + w.data.value.y() + ); + }, [](auto) { } }, *event); } + + + +#if defined __ANDROID__ +extern "C" __attribute__((visibility("default"))) +int SDL_main(int argc, char* argv[]) { return main(argc, argv); } +#endif + -- GitLab