Different behavior on Mac versus PC [SOLVED]
-
On 16/11/2014 at 06:24, xxxxxxxx wrote:
User Information:
Cinema 4D Version: 15
Platform: Windows ; Mac ;
Language(s) : C++ ;---------
I develop my plugins on PC and then copy and compile them on the Mac (10.9).
Now I have this command plugin that behaves differently on the mac then on my pc.In User Area DrawMsg(), I render multiple times to bitmaps and after each render I display the bitmap in the UA.
So, pseudo code:
DrawMsg
do 10 times
render to bitmap
display bitmap in user areaNow, on the pc, bitmaps are show after each render - one by one - UA is updated after every DrawBitmap()
On the mac, the UA is not update after each render, but when the whole loop is ready.
So on the pc the UA is updated 10 time, on the mac only once?If I add an EventAdd(), the program gets in a loop, because CoreMessage is the triggered (by eventadd()), which in turn triggers ReDraw and thus DrawMsg().
I am using R15 and xcode v5.0.2.
-Pim
-
On 17/11/2014 at 02:15, xxxxxxxx wrote:
Hi,
why are you trying to draw multiple times within one DrawMsg()?
What are you trying to achieve?
Actually you should draw only what's absolutely needed in there and then get out of there as quickly as possible. -
On 17/11/2014 at 03:13, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Hi,
why are you trying to draw multiple times within one DrawMsg()?
What are you trying to achieve?
Actually you should draw only what's absolutely needed in there and then get out of there as quickly as possible.Hi Andreas,
I am optimizing my render preview window.
So, a separate window that show a preview render each time something changes.
To speed up and show the progress of the rendering I divide the render in multiple parts.
Just like the IRR render.
So I set the IRR window, render to a bitmap and display that bitmap in the UA.
At the end the whole window is then rendered and displayed.
Something like Physical rendering - progressive.IRR rendering and displaying the multiple bitmaps is done in one (1) DrawMsg.
I read from your reply, that that should not be done in DrawMsg.
So where should I do the rendering and where should I display the IRR rendered bitmaps?
Perhaps in Dialog::CoreMessage() where all changes are received?And again, it is working on a PC,but differently on a Mac?
Here some code snippets.
//DrawMsg() where rendering and display in UA is done. // divide screen in 4x4 parts // set IRR paramaters data.SetInt32(RDATA_RENDERREGION_LEFT,left); data.SetInt32(RDATA_RENDERREGION_TOP,right); data.SetInt32(RDATA_RENDERREGION_RIGHT, top); data.SetInt32(RDATA_RENDERREGION_BOTTOM , bottom); // for all parts of the screen // render IRR res = RenderDocument(doc, data, nullptr, nullptr, renderbmp, RENDERFLAGS_IRR | RENDERFLAGS_NODOCUMENTCLONE | RENDERFLAGS_SHOWERRORS, nullptr); if (res == RENDERRESULT_OK) { // draw the render, but only the part defined // put IRR part of the bitmap in the corresponding UA part DrawBitmap(renderbmp, j*xUA,i*yUA, xUA,yUA, j*xInc,i*yInc, xInc,yInc, BMP_NORMAL); } // CoreMessage() receives all changes Bool C4DomePreviewDialog::CoreMessage(Int32 id, const BaseContainer& msg) { switch (id) { case EVMSG_CHANGE: if (CheckCoreMessage(msg)) { sg.Redraw(); return true; } break; } return GeDialog::CoreMessage(id, msg);
-Pim
-
On 17/11/2014 at 06:07, xxxxxxxx wrote:
Hello Pim,
I think the way to go would be the following:
- move your rendering into its own thread, providing a bitmap in the end
- kick off the thread on change
- in DrawMsg() only copy the resulting bitmapIn this way you take load from the Message loop as well as from the GUI redraw, which will end up in a much nicer user experience.
-
On 17/11/2014 at 06:33, xxxxxxxx wrote:
Ok, thanks for the advice.
-
On 17/11/2014 at 07:06, xxxxxxxx wrote:
I forgot to comment on the different behavior, at least as I see this.
I don't think, it is documented how the behavior is, when doing multiple draws within one DrawMsg() call. If the GUI updates instantaneous or only after you leave DrawMsg(). This may be heavily dependent on the underlying GUI system. Your implementation should not rely on any assumptions on this behavior. -
On 17/11/2014 at 07:13, xxxxxxxx wrote:
Ok, thanks.
Any progress on the tutorial on the new R16 folder implementation and compiling / linking on a Mac?
-
On 17/11/2014 at 08:23, xxxxxxxx wrote:
Actually a bit off topic... yeah, I'm actively working on it. Yet, I haven't been able to reproduce your error. But we'll see. I hope to come up with a recipe and if this does not work on your side, we will have to see...
-
On 18/11/2014 at 02:35, xxxxxxxx wrote:
Originally posted by xxxxxxxx
I forgot to comment on the different behavior, at least as I see this.
I don't think, it is documented how the behavior is, when doing multiple draws within one DrawMsg() call. If the GUI updates instantaneous or only after you leave DrawMsg(). This may be heavily dependent on the underlying GUI system. Your implementation should not rely on any assumptions on this behavior.Hi Andreas,
I changed the source based on your suggestions and on the PC it is working =GUI updates instantaneous. On the mac it is still not working = update only after you leave DrawMsg().Can I send you a pm with the source?
-Pim
-
On 18/11/2014 at 05:09, xxxxxxxx wrote:
I don't think, there's anything I can do about it. As I said, the behavior may be operating system dependent here.
In what way does it make a difference for your plugin, when the update happens? -
On 18/11/2014 at 07:41, xxxxxxxx wrote:
Ok, I stripped everything to the bare minimum.
So now I have a dialog with one AddEditNumber field in it.
When a CoreMessage is detected, I start to increment that field.On a pc I can see the the value going up.
On a Mac, a long time nothing happens and then only the end value is shown.
So no real time updating!I guess I am doing something wrong or a perhaps it is a Mac compiler setting?
// Test update PC versus Mac // be sure to use a unique ID obtained from www.plugincafe.com #define ID_C4DomePreview 1033944 // Official Plugin Number for "C4Dome Preview V2" #include "c4d.h" #include "c4d_symbols.h" enum { GROUPSETTINGS = 1030, COUNT, _dummy }; class C4DomePreviewDialog : public GeDialog { public: virtual Bool CreateLayout(void); virtual Bool CoreMessage (Int32 id, const BaseContainer& msg); virtual Bool DoRender(); }; Bool C4DomePreviewDialog::CreateLayout(void) { // first call the parent instance Bool res = GeDialog::CreateLayout(); AddEditNumber(COUNT, BFH_LEFT, 40, 0); return res; } Bool C4DomePreviewDialog::DoRender() { Int32 index, indexMax; indexMax = 64000; for (index = 0; index<indexMax; index++) { SetInt32(COUNT,index); } MessageDialog("Back to 0."); //Set test value back to zero SetInt32(COUNT,0); return true; } Bool C4DomePreviewDialog::CoreMessage(Int32 id, const BaseContainer& msg) { switch (id) { case EVMSG_CHANGE: DoRender(); break; } return GeDialog::CoreMessage(id, msg); } class C4DomePreview : public CommandData { private: C4DomePreviewDialog dlg; public: virtual Bool Execute(BaseDocument* doc); virtual Bool RestoreLayout(void* secret); }; Bool C4DomePreview::Execute(BaseDocument* doc) { return dlg.Open(DLG_TYPE_ASYNC, ID_C4DomePreview, -1,-1, 200, 200); } Bool C4DomePreview::RestoreLayout(void* secret) { return dlg.RestoreLayout(ID_C4DomePreview, 0, secret); } Bool RegisterC4DomePreview(void) { return RegisterCommandPlugin(ID_C4DomePreview, "C4Dome Preview V2.0", 0, nullptr, String("C4Dome Preview v2.0"), NewObjClear(C4DomePreview)); }
Note: I do get "Value conversion errors" on the mac, but I do not think they are related.
-
On 18/11/2014 at 12:24, xxxxxxxx wrote:
Pim,
I see what you mean. But I stand by what I said earlier. Your assumption is highly system dependent. And as you may have noticed, even as the GUI is updating under windows, C4D is completely blocked. No user input is possible, while your loop is running. Because you are doing your loop withing the event loop. You really should not. You'd better stick to a design as I proposed in my second post.
-
On 18/11/2014 at 23:58, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Pim,
I see what you mean. But I stand by what I said earlier. Your assumption is highly system dependent. And as you may have noticed, even as the GUI is updating under windows, C4D is completely blocked. No user input is possible, while your loop is running. Because you are doing your loop withing the event loop. You really should not. You'd better stick to a design as I proposed in my second post.
Sorry, then you have to clarify in more detail (an example?) what you mean.
I thought I am doing this outside the event loop.
I am using CoreMessage to trigger my render loop, so outside any event loop?
Or am I missing something?Could you provide a small example?
-Pim
-
On 19/11/2014 at 05:33, xxxxxxxx wrote:
Hi,
here's your example code with a worker thread:// Test update PC versus Mac // be sure to use a unique ID obtained from www.plugincafe.com #define ID_C4DomePreview 1033944 // Official Plugin Number for "C4Dome Preview V2" #define ID_C4Dome_UpdGui 1000010 // TODO DO NOT FORGET TO CHANGE #include "c4d.h" #include "c4d_symbols.h" enum { GROUPSETTINGS = 1030, COUNT, _dummy }; enum MyCoreMessages { MYMSG_UPD_GUI = 0, _MYMSG_TERMINATOR // keep last }; class MyRenderThread : public C4DThread { public: Int32 index; Bool bRestart; GeSignal *pSig; MyRenderThread(); virtual void Main(void); virtual const Char* GetThreadName(void); }; void MyRenderThread::Main(void) { const Int32 indexMax = 64000; while (1) { if (TestBreak()) break; pSig->Wait(500); // -1 wait forever if (!bRestart) continue; // here's basically your former DoRender() code bRestart = false; for (index = 0; index < indexMax; index++) { SpecialEventAdd(ID_C4Dome_UpdGui, MYMSG_UPD_GUI, (UInt)index); if (bRestart || TestBreak()) break; GeSleep(10); } } } const Char* MyRenderThread::GetThreadName(void) { return "MyRenderThread"; } MyRenderThread::MyRenderThread() : C4DThread() { index = 0; bRestart = false; pSig = nullptr; } class C4DomePreviewDialog : public GeDialog { public: MyRenderThread *pThd; virtual Bool CreateLayout(void); virtual Bool CoreMessage(Int32 id, const BaseContainer& msg); }; Bool C4DomePreviewDialog::CreateLayout(void) { // first call the parent instance Bool res = GeDialog::CreateLayout(); AddEditNumber(COUNT, BFH_LEFT, 40, 0); return res; } Bool C4DomePreviewDialog::CoreMessage(Int32 id, const BaseContainer& msg) { switch (id) { case EVMSG_CHANGE: pThd->bRestart = true; // ask thread to restart, if it's already working pThd->pSig->Set(); // wake thread on change break; case ID_C4Dome_UpdGui: { UInt selector = (UInt)msg.GetVoid(BFM_CORE_PAR1); switch (selector) { case MYMSG_UPD_GUI: SetInt32(COUNT, (Int32)msg.GetVoid(BFM_CORE_PAR2)); // see SDK docs on SpecialEventAdd, you can pass pretty much any data in here break; default: DebugStop("Forgot to implement internal message!"); } } break; } return GeDialog::CoreMessage(id, msg); } class C4DomePreview : public CommandData { private: C4DomePreviewDialog dlg; MyRenderThread thd; public: virtual Bool Execute(BaseDocument* doc); virtual Bool RestoreLayout(void* secret); }; Bool C4DomePreview::Execute(BaseDocument* doc) { if (!thd.IsRunning()) { thd.pSig = GeSignal::Alloc(); if ((!thd.pSig) || (!thd.pSig->Init())) { CriticalStop("FAILED TO ALLOC OR INIT GeSignal"); return false; } thd.Start(); dlg.pThd = &thd; } return dlg.Open(DLG_TYPE_ASYNC, ID_C4DomePreview, -1, -1, 200, 200); } Bool C4DomePreview::RestoreLayout(void* secret) { return dlg.RestoreLayout(ID_C4DomePreview, 0, secret); } Bool RegisterC4DomePreview(void) { return RegisterCommandPlugin(ID_C4DomePreview, "C4Dome Preview V2.0", 0, nullptr, String("C4Dome Preview v2.0"), NewObjClear(C4DomePreview)); }
-
On 20/11/2014 at 08:54, xxxxxxxx wrote:
Ok, I see you are using a real thread.
I will use it as the base for my plugin and let you know the result.Thanks, Pim
-
On 27/11/2014 at 08:00, xxxxxxxx wrote:
Hi Andreas,
I managed to get the thing going on a PC. Not tried it a Mac yet.
That is because it is very unstable.
It works, but then it "suddenly" crashes.
I did not find exactly when it crashes, but one thing I notices is after adding some clones, playing with it and then adding another object crashes cinema.Do you have some tips to consider when using threads?
What I am doing.
Command plugin
Dialog User Area
On CoreMessage, start threat
on dialog message = message from thread, display bitmap in UAThreat:
Render scene, based on global variable, to a bitmap
Store the bitmap in a variable
When render is done, send message to dialogSome additional commands.
- The thread is asynchronous, so I am using some delays to give the dialog time to display the bitmap.
- I am using global variables, because I use a classes that are dependent of each other.
So A uses B and B uses A.
I did not manage to do that neatly (without global variables). I receive compiler errors.
I guess I do need to use .h files and put those classes in different files. -
On 27/11/2014 at 11:35, xxxxxxxx wrote:
Hi Pim,
now it will get messy...
What you are describing are the typical problems you run into with threading (unless doing it right, I should add, but this is not meant as an insult in any way). Threading is probably one of the hardest topics to come to grips with. And searching for bugs can be a pain, as the the code that did something wrong most likely ran a while ago and is history when the resulting bug occurs.Having this said, it will be very hard for an outstanding person to point you to the culprit.
One pitfall may be calling an API function in a thread context, that is not allowed to run in a thread context. The SDK docs have an article on this (which is not all complete, but may nevertheless give good hints) :
R15 SDK Docs: Important Threading Information
[URL-REMOVED]
Although you should pay attention to any notes in SDK docs on the functions you use in your thread.Another thing is memory corruption and/or concurrent access to data structures. Your global variables may well be suspect here. With threads you always have to be aware that at any given time two functions or code lines may access data they share. You need to closely inspect your code, if this can happen. And if so, you'll need some mechanism of serialization, catchwords like mutex and semaphore come to mind.
Then there's something you say, that makes me skeptical:
I am using some delays to give the dialog time to display the bitmap
This should not be needed, if you are doing it right. Many programmers try to mask race conditions with delays, in the hope, if they only make the chances small enough it will never happen. By experience I can tell, it will happen anyway. You need to cure the race condition, nothing else will help.
Finally in my article about debugging plugins on our new PluginCafe blog (
Debugging Cinema 4D Plugins Written in C++
[URL-REMOVED]), I have a small section about multithreading. There are some links under "Further Reading" that might be interesting and helpful in this context.I'm afraid, that's all I can say, given your brief description.
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
On 11/03/2015 at 12:15, xxxxxxxx wrote:
Hi Pim,
I'm hesitant to ask, since the thread is quite old by now. But did you make any progress? -
On 11/03/2015 at 14:30, xxxxxxxx wrote:
Hi Andreas,
Yes it is working, thanks to all your help.
-Pim