drawing to a bitmap from a thread Topic is solved

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.
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

drawing to a bitmap from a thread

Post by mael15 »

hello,
i am using wxMemoryDC to draw inside of a secondary thread without any problems. but it sais in the wxThread docs "GUI calls, such as those to a wxWindow or wxBitmap are explicitly not safe at all in secondary threads and could end your application prematurely."
does this exclude wxMemoryDC? if not, is this not true for windows?
thanx.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: drawing to a bitmap from a thread

Post by doublemax »

First of all: Multithread issues don't always and immediately lead to a crash. Often they only lead to random crashes or other unexpected behavior.

Usually i'd say using wxBitmap in a secondary thread is not safe. But as the "thread" sample does this, i guess it's ok if you use mutexes and/or wxMutexGuiEnter/wxMutexGuiLeave calls to make sure that the bitmap is not accessed from multiple threads at once.
Use the source, Luke!
catalin
Moderator
Moderator
Posts: 1618
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Re: drawing to a bitmap from a thread

Post by catalin »

IIRC using wxBitmap (which is needed for a wxMemoryDC) in a secondary thread under wxGTK will eventually lead to a crash. Maybe other ports too - some are more forgiving than others but as it's not guaranteed to work it is not advertized as "supported". And even more so if you use fonts, brushes or other UI resources.
One more path that may be worth exploring is to use a wxGraphicsContext with a wxImage (since 2.9.3). Keep in mind the same concern about UI resources.
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: drawing to a bitmap from a thread

Post by mael15 »

doublemax wrote:i guess it's ok if you use mutexes
i am very thorough with mutexes, so i keep drawing in threads using wxMemoryDC.
catalin wrote:under wxGTK will eventually lead to a crash. Maybe other ports too - some are more forgiving than others
since my app is for in-house use and windows only, it seems to be ok.
catalin wrote:One more path that may be worth exploring is to use a wxGraphicsContext with a wxImage
official documentation is quite small. what would be the difference? is it something like a second gui apart from the main thread?
thanx!
User avatar
T-Rex
Moderator
Moderator
Posts: 1249
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Re: drawing to a bitmap from a thread

Post by T-Rex »

I would suggest not to use any GUI call inside the worker thread, this increases the dependency of business logic from UI. You can e.g. post the event to your window, attach the pointer to your bitmap (or use any other way to let UI know that bitmap is ready), then handle the event in GUI/main thread, and draw it.

Or you can use 2 bitmaps in secondary thread: first which is used by worker thread for drawing while UI is processing/displaying the second one. When UI finishes working with second bitmap, the worker thread sends the notification that UI should use it, and then keeps drawing into the secondbitmap.

Similar approach can be found here (but with data buffers for wxImage): https://github.com/T-Rex/wxKinectHelper ... eenect.cpp - in my sample UI requests new buffer from backend when it is needed, but in your app you can notify the UI from thread by sending the event.
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: drawing to a bitmap from a thread

Post by mael15 »

i am not sure if the following has something to do with this thread.
i get this access violation error and have a hard time finding the cause:

Code: Select all

Ausnahme (erste Chance) bei 0x009295C0 (wxmsw30ud_core_vc_custom.dll) in myApp.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0xDDDDDDE1
Ausnahmefehler bei 0x009295C0 (wxmsw30ud_core_vc_custom.dll) in myApp.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0xDDDDDDE1
the position in the main Thread is

Code: Select all

wxMemoryDC mdcRepAr(*getBitmapAreas());
and in the secondary thread:

Code: Select all

wxMemoryDC mdcBtmp(*getTempBitmap());
these are two different wxBitmaps. the app crashes only after doing the same thing for a while, so the code basically works. is it possible that creating a wxMemoryDC in the main and secondary thread at the same time causes the crash?
thanx!
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: drawing to a bitmap from a thread

Post by doublemax »

Just creating two independent wxMemoryDC instances in different threads shouldn't be a problem.

But: whenever you're using gui operations in multiple threads in wxWidgets, you lose the right to complain about crashes ;)

At the very least you must make sure that GDI objects are created and used in the same thread. You must not create a wxBitmap in the main thread and write to it from another thread. To transfer finished bitmaps to the main thread, use a raw byte container like wxImage (which is not a GDI object by itself).

Also be aware that all GDI classes, like wxBitmap, wxPen, wxBrush etc are reference counted. Don't copy any instances, because they will be shallow copies pointing to the same reference data. E.g. never use stock items like wxBLACK_PEN etc.
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: drawing to a bitmap from a thread

Post by mael15 »

sorry for the late reopening of this thread. i continue to have this problem.

what i am doing at the moment:
a) draw to a temporary wxBitmap in a thread
b) make a copy of the completely drawn temporary bitmap to store it
c) main UI gets the stored bitmap to show it on screen

could you be so kind an elaborate this:
doublemax wrote:To transfer finished bitmaps to the main thread, use a raw byte container like wxImage (which is not a GDI object by itself).
since i do a lot of drawing, i see no better way than drawing in a thread. when i would draw only in the main thread, my programme would freeze often.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: drawing to a bitmap from a thread

Post by doublemax »

mael15 wrote:could you be so kind an elaborate this:
doublemax wrote:To transfer finished bitmaps to the main thread, use a raw byte container like wxImage (which is not a GDI object by itself).
b) make a copy of the completely drawn temporary bitmap to store it
Instead of making a copy into another wxBitmap, you use a wxImage. Unfortunately that means that in the main thread it has to be converted to a wxBitmap again before you can draw it, but it's the cleanest solution.

There is another option: If your drawing takes very long, it probably means that you perform many drawing operations after another. You could render in a timer event but only render some of the operations and continue in the next timer event until you're done.
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: drawing to a bitmap from a thread

Post by mael15 »

doublemax wrote:Instead of making a copy into another wxBitmap, you use a wxImage. Unfortunately that means that in the main thread it has to be converted to a wxBitmap again before you can draw it, but it's the cleanest solution.
i think this helped. at least i now do not get any errors with GDI objects in it anymore. i store the image data in an wxImage object and only convert it once into an wxBitmap when it is needed for the first time.
thank you! =D>
mael15
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 539
Joined: Fri May 22, 2009 8:52 am
Location: Bremen, Germany

Re: drawing to a bitmap from a thread

Post by mael15 »

i have another issue related to this.
i have a huge horizontal bitmap in the main thread and when the user scrolls horizontally, a worker thread should draw the scrolled part, send it to the main thread as wxImage, and then the main Thread should convert it to wxBitmap an copy it to the right place on the huge bitmap.

my problem is: i fail to create a custom payload that i can use with wxThreadEvent::SetPayload.
i tried it like this:

Code: Select all

class ThreadEventPayloadBase {
public:
	enum class TPayloadType : unsigned int {
		P_PROGRESS = 0,
		P_REFRESH,
		P_XYT_RECT_BTMP
	};

	ThreadEventPayloadBase(TPayloadType _type) : type(_type){}
	ThreadEventPayloadBase(const ThreadEventPayloadBase &other){
		type = other.type;
	}

	TPayloadType getType() const { return type; }
	void setType(TPayloadType val) { type = val; }
private:
	TPayloadType type = TPayloadType::P_PROGRESS;
};

class ThreadEventPayloadXytRect : public ThreadEventPayloadBase {
public:
	ThreadEventPayloadXytRect(wxImage *img, int _xPos) : paintedInThread(img), xPos(_xPos), ThreadEventPayloadBase(ThreadEventPayloadBase::TPayloadType::P_XYT_RECT_BTMP){}
	ThreadEventPayloadXytRect(const ThreadEventPayloadXytRect &other) : ThreadEventPayloadBase(other){
		xPos = other.xPos;
		if (other.paintedInThread)
			paintedInThread = new wxImage(*other.paintedInThread);
	}
	~ThreadEventPayloadXytRect(){
		delete getPaintedImage();
	}

	wxImage *getPaintedImage() const { return paintedInThread; }
	void setPaintedImage(wxImage * val) { paintedInThread = val; }
	int getPos() const { return xPos; }
	void setPos(int val) { xPos = val; }
private:
	wxImage *paintedInThread = nullptr;
	int xPos = 0;
};
in the worker thread, i do:

Code: Select all

wxThreadEvent *threadEvt = new wxThreadEvent();
wxImage *img = new wxImage(getTempBitmap()->ConvertToImage());
ThreadEventPayloadXytRect payload(img, xPos);
threadEvt->SetPayload(payload);
wxQueueEvent(objectInMainThread, threadEvt);
but in the main thread, i get an error
assert "Assert failure" failed in wxAny::As(): Incorrect or non-convertible data type
when i try to get the payload:

Code: Select all

ThreadEventPayloadBase pl = evt.GetPayload<ThreadEventPayloadBase>();
what can i do about it?
thank you!
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: drawing to a bitmap from a thread

Post by doublemax »

Your new class is unknown to wxAny, you'd have to implement a new wxAnyValueType for it.
http://docs.wxwidgets.org/trunk/classwx ... _type.html

But i'm not sure it's worth the effort, i'd just create the whole ThreadEventPayloadXytRect on the heap and send the pointer with the event. Or derive a new class from wxThreadEvent and store all information in there.
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: drawing to a bitmap from a thread

Post by mael15 »

doublemax wrote:i'd just create the whole ThreadEventPayloadXytRect on the heap and send the pointer with the event.
you are right, using wxEvent::SetEventObject is way easier. or did you mean something else?
thank you!
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: drawing to a bitmap from a thread

Post by doublemax »

you are right, using wxEvent::SetEventObject is way easier. or did you mean something else?
SetEventObject could work, but it's usually reserved, i'd prefer SetExtraLong. But it probably doesn't matter.
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: drawing to a bitmap from a thread

Post by mael15 »

doublemax wrote:E.g. never use stock items like wxBLACK_PEN etc.
catalin wrote:And even more so if you use fonts, brushes or other UI resources.
One more path that may be worth exploring is to use a wxGraphicsContext with a wxImage (since 2.9.3). Keep in mind the same concern about UI resources.
But even in a wxGraphicsContext I would have to use wxPen etc. to draw. Or is it safe to just use wxPen(wxColour(0, 0, 0)) instead of wxBLACK_PEN?
Post Reply