cnjinhao / nana

a modern C++ GUI library
https://nana.acemind.cn
Boost Software License 1.0
2.33k stars 335 forks source link

How to do smooth drawing with nana #376

Open wjiali6 opened 5 years ago

wjiali6 commented 5 years ago

I have a code which draws a png image on frame graphics ,But I found that no matter how often I call the refresh window I still get some flickering (it seems nana produces drawing calls with vsync),this is how I do my drawing nana::timer t1; dw = new nana::drawing(form); dw->draw([this](nana::paint::graphics &g){ ball.paste(g,{ballPosition.x,ballPosition.y}); }); t1.interval(10); t1.elapse([this](){ this->_calculate_ball_pos(); nana::API::refresh_window(form); }); t1.start(); _calculate_ball_pos() looks like this ` _calculate_ball_pos() { static bool down = true; const int delta = 2; auto ballsize = ball.size(); auto playgroundsize = plygrd->size(); if(down) { if(ballPosition.y + ballsize.height/2 == playgroundsize.height) { down = false; ballPosition.y -= delta; return; } if(ballPosition.y + ballsize.height/2 + delta >= playgroundsize.height) { ballPosition.y = playgroundsize.height - ballsize.height/2; down = false; return; } ballPosition.y += delta; } else { if(ballPosition.y == 0) { down = true; ballPosition.y +=delta; return; } if(ballPosition.y - delta <= 0) { ballPosition.y = 0; down = true; return; } ballPosition.y -= delta; } }

`

cnjinhao commented 5 years ago

Use a thread instead of a timer.

dw = new nana::drawing(form);
dw->draw([this](nana::paint::graphics &g){ ball.paste(g,{ballPosition.x,ballPosition.y}); });

std::thread thr([this]{
    while(true)
    {
           _calculate_ball_pos();
           nana::API::refresh_window(form);
           std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
});
wjiali6 commented 5 years ago

Using another thread solves the flickering problem but I noticed It's very bad in efficiency, sleep 10 milli Sec between every refresh window call ,uses up 10% to 17% of my old i5-3230 @2.6GHz laptop,I'll try again on my i7-6700 home when I'm off duty.

wjiali6 commented 5 years ago

same program compiled from one source runs perfectly (cpu usage 1 to 0)on i7 6700 windows 7 ,but consumes about 12% to 15% on i5 3230 windows 10. it might nothing to do with nana

wjiali6 commented 5 years ago

After a quick profiling with visual studio ,now I have this data nana::API::refresh_window(*plygrd); --consumes 61% percent of cpu time and in that call nana::detail::window_layout::maproot takes 36.4% nana::detail::drawer::refresh takes 24.4%

and along with this test I found that nana pastes the whole scene every time, this causes cpu consumption grows significantly as the scene gets larger

cnjinhao commented 5 years ago

Thanks for your feedback!

beru commented 5 years ago

In the past, I used nana library to write graphical application that runs on Windows platform. I could achieve smooth drawing by directly modifying pixels that nana library manages. This might be kind of dirty hack though. I'm not sure if current nana library still supports this way.


void draw_foo(paint::graphics& g, ....)
{
    auto sz = g.size();
    paint::pixel_buffer pbuffer;
    rectangle r;
    r.x = 0;
    r.y = 0;
    r.width = sz.width;
    r.height = sz.height;
    pbuffer.attach(g.handle(), r);
    pixel_color_t* line = pbuffer.raw_ptr(0);
    size_t lineBytes = pbuffer.bytes_per_line();
    memset(line, 0xFF, lineBytes * r.height);
}

void show_form()
{
    appearance ap;
    ap.minimize = false;
    form fm(parent, size(1400, 800), ap);
    ....
    drawing dw{fm};
    paint::graphics g;
    dw.draw([&](paint::graphics& graph){
        graph.bitblt(0, get_controls_bottom(), g);
    });
    // call this on event
    auto& draw = [&]() {
        if (!is_ok_to_draw) {
            return;
        }
        UTILITY_SCOPE_EXIT([&](){
            dw.update();
        });
        draw_foo(
            g,
            ......
        );
    };

    .....

}

An another idea solution is to use Vulkan. https://github.com/tim37021/NANA_VULKAN

wjiali6 commented 5 years ago

In the past, I used nana library to write graphical application that runs on Windows platform. I could achieve smooth drawing by directly modifying pixels that nana library manages. This might be kind of dirty hack though. I'm not sure if current nana library still supports this way.

void draw_foo(paint::graphics& g, ....)
{
  auto sz = g.size();
  paint::pixel_buffer pbuffer;
  rectangle r;
  r.x = 0;
  r.y = 0;
  r.width = sz.width;
  r.height = sz.height;
  pbuffer.attach(g.handle(), r);
  pixel_color_t* line = pbuffer.raw_ptr(0);
  size_t lineBytes = pbuffer.bytes_per_line();
  memset(line, 0xFF, lineBytes * r.height);
}

void show_form()
{
  appearance ap;
  ap.minimize = false;
  form fm(parent, size(1400, 800), ap);
  ....
  drawing dw{fm};
  paint::graphics g;
  dw.draw([&](paint::graphics& graph){
      graph.bitblt(0, get_controls_bottom(), g);
  });
  // call this on event
  auto& draw = [&]() {
      if (!is_ok_to_draw) {
          return;
      }
      UTILITY_SCOPE_EXIT([&](){
          dw.update();
      });
      draw_foo(
          g,
          ......
      );
  };

  .....

}

An another ~idea~ solution is to use Vulkan. https://github.com/tim37021/NANA_VULKAN I'll try your sollution and feed back by the end of the week,thank you

wjiali6 commented 5 years ago

In the past, I used nana library to write graphical application that runs on Windows platform. I could achieve smooth drawing by directly modifying pixels that nana library manages. This might be kind of dirty hack though. I'm not sure if current nana library still supports this way.

void draw_foo(paint::graphics& g, ....)
{
    auto sz = g.size();
    paint::pixel_buffer pbuffer;
    rectangle r;
    r.x = 0;
    r.y = 0;
    r.width = sz.width;
    r.height = sz.height;
    pbuffer.attach(g.handle(), r);
    pixel_color_t* line = pbuffer.raw_ptr(0);
    size_t lineBytes = pbuffer.bytes_per_line();
    memset(line, 0xFF, lineBytes * r.height);
}

void show_form()
{
    appearance ap;
    ap.minimize = false;
    form fm(parent, size(1400, 800), ap);
    ....
    drawing dw{fm};
    paint::graphics g;
    dw.draw([&](paint::graphics& graph){
        graph.bitblt(0, get_controls_bottom(), g);
    });
    // call this on event
    auto& draw = [&]() {
        if (!is_ok_to_draw) {
            return;
        }
        UTILITY_SCOPE_EXIT([&](){
            dw.update();
        });
        draw_foo(
            g,
            ......
        );
    };

    .....

}

An another ~idea~ solution is to use Vulkan. https://github.com/tim37021/NANA_VULKAN I'll try your sollution and feed back by the end of the week,thank you

following your solution ,I did something like size_t lineBytes = pbuffer_of_img.bytes_per_line(); for(auto n = 0; n < height_of_img ; n++ ) { memcpy(x+y + pbuffer_of_form,pbuffer_of_img,pbuffer_of_img.bytes_per_line();) } still having a jumping effect. I wander if it something to do with amount of pixels for every call for _calculate_ball_pos, then I change _calculate_ball_pos as `` _calculate_ball_pos() { static auto lastTimechecked = std::chrono::steady_clock::now();

if(this->ballPos->y >plgrd->size().height)
{
    this->ballPos->y = 0;
}else
{
    auto now = std::chrono::steady_clock::now();
    auto delta = (now - lastTimechecked).count();
    this->ballPos->y += delta/10000000 + 0.5;
    lastTimechecked = now;
}

}

`` but still have flickering