wxPanel on another panel, repaint using a lot CPU

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

I have a custom wxPanel to show infos about whatever is under the cursor (the orange panel)
panelOnBtmp.jpg
this is a wxPanel and I copy all the underlying bitmaps every time the mouse moves and the panel with it. This uses a lot of CPU, but when I do not copy the bitmaps while the mouse is moving, the panel leaves a trace (old versions of it).
Is there a better way that uses less CPU?
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: wxPanel on another panel, repaint using a lot CPU

Post by PB »

There must be drawing issue somewhere.

Also, are you sure you cannot use a wx(Rich)ToolTip instead of the custom panel?
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

PB wrote: Tue Apr 14, 2020 8:38 pm There must be drawing issue somewhere.

Also, are you sure you cannot use a wx(Rich)ToolTip instead of the custom panel?
the standard wxtooltip was not versatile enough, I will check out wxRichToolTip, looks better, thanx!
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

PB wrote: Tue Apr 14, 2020 8:38 pmThere must be drawing issue somewhere.
Can I make drawing faster (less CPU hungry) by somehow not having to draw the whole background on every little mouse movement? Maybe using https://docs.wxwidgets.org/3.1.2/classw ... ipper.html?
PB wrote: Tue Apr 14, 2020 8:38 pmAlso, are you sure you cannot use a wx(Rich)ToolTip instead of the custom panel?
I looked into it, it is not customizable enough, too big and ugly on MSW.
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxPanel on another panel, repaint using a lot CPU

Post by doublemax »

Can I make drawing faster (less CPU hungry) by somehow not having to draw the whole background on every little mouse movement?
Are you drawing individual circles? If yes, try rendering into a background bitmap instead that's only updated when the content changes. Additionally, you can update only the "dirty" rectangles. Check the code snippet using GetUpdateRegion() from here: https://docs.wxwidgets.org/trunk/classw ... event.html

I think you could also just use a borderless wxFrame for the tooltip. To avoid Z-order issues, i'd show it only when the main frame is active.
Use the source, Luke!
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

doublemax wrote: Sun Apr 19, 2020 11:45 amAre you drawing individual circles?
The biggest part of cpu usage comes from wxDc::DrawBitmap on a wxBufferedPaintDC. The circles are already painted on a wxBitmap earlier.
doublemax wrote: Sun Apr 19, 2020 11:45 amAdditionally, you can update only the "dirty" rectangles. Check the code snippet using GetUpdateRegion() from here: https://docs.wxwidgets.org/trunk/classw ... event.html
That is nice! A simple

Code: Select all

wxBufferedPaintDC bpdc(this);
bpdc.SetDeviceClippingRegion(GetUpdateRegion());
reduced cpu usage by ~30-60%, thanx!
doublemax wrote: Sun Apr 19, 2020 11:45 amI think you could also just use a borderless wxFrame for the tooltip. To avoid Z-order issues, i'd show it only when the main frame is active.
I don't understand what you mean by "borderless"? At the moment, it is a

Code: Select all

wxPanel(par, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE)
wxBORDER_NONE does not really make a difference? looks a bit cleaner though... ;)
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxPanel on another panel, repaint using a lot CPU

Post by doublemax »

I don't understand what you mean by "borderless"?
I meant a real, complete wxFrame, an independent toplevel window for the tooltip, that you position at the correct coordinates.
Use the source, Luke!
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

doublemax wrote: Mon Apr 20, 2020 7:27 amI meant a real, complete wxFrame, an independent toplevel window for the tooltip, that you position at the correct coordinates.
Cool idea, tried it, but it leads to ~20% higher cpu load than wxPanel. Only with wxFrame it is not the wxPaintEvent of the underlying panel but SetPosition and SetSizeHints that cost more. But I am pretty happy with it now, it uses about half of the cpu compared to before. So if there is not much else one can try, then maybe this is as good as it gets. :wink:
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxPanel on another panel, repaint using a lot CPU

Post by doublemax »

Of course you still get paint events in the underlying window when you move the frame accross.
Only with wxFrame it is not the wxPaintEvent of the underlying panel but SetPosition and SetSizeHints that cost more.
I find it hard to believe that these cause so much CPU load.

In fact, nothing i see on your screenshot should take any significant CPU load on modern hardware.

Code: Select all

wxBufferedPaintDC bpdc(this);
bpdc.SetDeviceClippingRegion(GetUpdateRegion());
Here you should definitely use the wxBufferedPaintDC overload that takes a wxBitmap &buffer as paramter. This buffer bitmap should be static and only get (re-) created when the client size changes. This should save some time.
Use the source, Luke!
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

doublemax wrote: Mon Apr 20, 2020 10:29 am

Code: Select all

wxBufferedPaintDC bpdc(this);
bpdc.SetDeviceClippingRegion(GetUpdateRegion());
Here you should definitely use the wxBufferedPaintDC overload that takes a wxBitmap &buffer as paramter. This buffer bitmap should be static and only get (re-) created when the client size changes. This should save some time.
I wrote this little test program and cannot see any difference in cpu usage when moving the mouse quickly on the maximized window with a bitmap showing. I used the bitmap from my first post.

Code: Select all

#pragma once

#include <wx/frame.h>
#include <wx/app.h>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/dcbuffer.h>

class CursorPanel : public wxPanel {
public:
	CursorPanel(wxWindow* par) : wxPanel(par){
		SetOwnBackgroundColour(wxColour(255, 126, 40));
		SetFont(wxFont(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
		wxBoxSizer* outerBox = new wxBoxSizer(wxVERTICAL);
		SetSizer(outerBox);

		coordsText = new wxStaticText(this, wxID_ANY, wxT("5/10"));
		wxString label = coordsText->GetLabel();
		coordsText->SetForegroundColour(*wxBLACK);
		GetSizer()->Add(coordsText, 1, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
	}

	wxPoint getCursorCoords() {
		wxPoint ret;
		wxString label = coordsText->GetLabel();
		ret.x = wxAtoi(label.BeforeFirst('/'));
		ret.y = wxAtoi(label.AfterFirst('/'));
		return ret;
	}
	void setCursorCoords(wxPoint newCoords) {
		if (newCoords == getCursorCoords())
			return;
		coordsText->SetLabel(wxString::Format(wxT("%i/%i"), newCoords.x, newCoords.y));
		GetSizer()->SetSizeHints(this);
	}
private:
	wxStaticText* coordsText = nullptr;
};

class MyFrame1 : public wxFrame 
{
public:
	MyFrame1( wxWindow* parent = NULL, 
		wxWindowID id = wxID_ANY, 
		const wxString& title = wxEmptyString, 
		const wxPoint& pos = wxDefaultPosition, 
		const wxSize& size = wxSize( 500,300 ), 
		long style = wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) :
		wxFrame(parent, id, title, pos, size, style)
	{
		wxInitAllImageHandlers();
		wxFileName f(wxStandardPaths::Get().GetExecutablePath());
		wxString appPath(f.GetPath());
		loadedBtmp = wxBitmap(appPath + wxT("\\panelOnBtmp.jpg"), wxBITMAP_TYPE_JPEG);
		bool btmpOk = loadedBtmp.IsOk();

		bufferBtmp = wxBitmap(GetClientSize());

		SetSizer(new wxBoxSizer(wxVERTICAL));

		curPan = new CursorPanel(this);
		Bind(wxEVT_MOTION, &MyFrame1::onMouseMotion, this);
		Bind(wxEVT_PAINT, &MyFrame1::onPaint, this);
		Bind(wxEVT_SIZE, &MyFrame1::onResize, this);

		this->SetSizeHints(wxDefaultSize, wxDefaultSize);
		this->Centre(wxBOTH);

	}
	~MyFrame1() {}

	void onMouseMotion(const wxMouseEvent& evt) {
		this;
		wxPoint cursorSize(15, 15);
		wxPoint evtPos = evt.GetPosition();
		curPan->SetPosition(evtPos + cursorSize);
		curPan->setCursorCoords(evt.GetPosition());
	}

	void onPaint(const wxPaintEvent& evt) {
		wxBufferedPaintDC bpdc(this/*, bufferBtmp*/);
		bpdc.SetDeviceClippingRegion(GetUpdateRegion());
		bpdc.DrawBitmap(loadedBtmp, wxPoint(0, 0));
	}

	void onResize(const wxSizeEvent& evt) {
		bufferBtmp = wxBitmap(GetClientSize());
	}

	CursorPanel* curPan = nullptr;
	wxBitmap loadedBtmp, bufferBtmp;
private:
};

class MyApp : public wxApp {
	virtual bool OnInit();
};
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxPanel on another panel, repaint using a lot CPU

Post by doublemax »

Hmm, the CPU load on my system is around 1-2%, no matter how fast i move the mouse. From your posts i would have expected something much worse ;)
Use the source, Luke!
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

doublemax wrote: Mon Apr 20, 2020 9:23 pm Hmm, the CPU load on my system is around 1-2%, no matter how fast i move the mouse. From your posts i would have expected something much worse ;)
Hmmmmmmm, when I maximize the frame on a full hd display and move the mouse around it goes up to 8-9% on my i7 3.5GHz 16GB ram system. At least when not using

Code: Select all

bpdc.SetDeviceClippingRegion(GetUpdateRegion());
That seems a lot to me for such a simple task, but I could live with it. Are you on wxMSW?
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxPanel on another panel, repaint using a lot CPU

Post by doublemax »

Yes, i'm under Windows.

If i make the window fullscreen, i can get it up to 4%. But, i get 4% even if i leave the paint event handler empty. I made a few variants of the code, but i never got it below these 4%.

There is one approach i haven't tried, because it was a little too much effort, but if you really want to optimize it more, maybe you can try:

Get rid of CursorPanel, instead draw its content yourself. And - although i always preach not to use it - here the use of wxClientDC might make sense. Instead of forcing a refresh when the cursor moves, draw into the wxClientDC of the frame, and only the parts that have changed. I can't think of any faster way to do it.
Use the source, Luke!
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: wxPanel on another panel, repaint using a lot CPU

Post by PB »

Regarding the CPU utilization: I believe that on modern CPUs just the percentage can be deceiving, as the CPU underclocks when not fully utilized. For example, my desktop AMD CPU underclocks to < 2 GHz from its default 3.4. The current speed can be seen on the Performance tab of the Task Manager.

Does running your application make your CPU go to its full speed?
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: wxPanel on another panel, repaint using a lot CPU

Post by mael15 »

PB wrote: Tue Apr 21, 2020 8:33 am Regarding the CPU utilization: I believe that on modern CPUs just the percentage can be deceiving, as the CPU underclocks when not fully utilized. For example, my desktop AMD CPU underclocks to < 2 GHz from its default 3.4. The current speed can be seen on the Performance tab of the Task Manager.
That is really interesting. In my posts I used the cpu values straight from the process tab of the task manager, but the ressource monitor for this process shows lower values. I guess these are more adequate.
When doublemax's tips worked, I noticed around 40% of cpu usage came from a ProcessIdle function. I trace these with the visual studio cpu diagnostic tool.
PB wrote: Tue Apr 21, 2020 8:33 am Does running your application make your CPU go to its full speed?
I noticed it went to a max value in the cpu usage chart. I show a real time signal on screen and additionally moving the mouse with this CursorPanel over a wxBitmap made the signal stutter on a machine less powerful than mine. It is way better now.
doublemax wrote: Tue Apr 21, 2020 8:06 amThere is one approach i haven't tried, because it was a little too much effort, but if you really want to optimize it more, maybe you can try:

Get rid of CursorPanel, instead draw its content yourself. And - although i always preach not to use it - here the use of wxClientDC might make sense. Instead of forcing a refresh when the cursor moves, draw into the wxClientDC of the frame, and only the parts that have changed. I can't think of any faster way to do it.
I am happy now and only try this if I should need more optimization in the future. Thank you for you valuable input, I learned a lot from it! =D>
Post Reply