Different behavior on Mac versus PC [SOLVED]



  • 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 UA

    Threat:
      Render scene, based on global variable, to a bitmap
      Store the bitmap in a variable
      When render is done, send message to dialog

    Some 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
    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++), 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.



  • 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


Log in to reply