Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Exception with thread rendering

    PYTHON Development
    0
    20
    1.8k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      On 08/04/2013 at 07:49, xxxxxxxx wrote:

      Hi everyone!

      I want to render a picture using a thread, so C4D isn't freezing during rendering. I use a CommandData plugin and the c4dThread module. Here's my thread code:

        
      import c4d  
      from c4d import documents  
      from c4d.threading import C4DThread  
        
      class RenderThread(C4DThread) :  
        def __init__(self, doc) :  
            super(RenderThread, self).__init__()  
            self.doc = doc  
          
        def Main(self) :  
            renderData = self.doc.GetActiveRenderData()  
            xResolution = int(round(renderData[c4d.RDATA_XRES]))  
            yResolution = int(round(renderData[c4d.RDATA_YRES]))  
        
            renderBmp = c4d.bitmaps.BaseBitmap()  
            renderBmp.Init(x=xResolution, y=yResolution, depth=32, flags=c4d.INITBITMAPFLAGS_0)  
        
            result = documents.RenderDocument(self.doc, renderData.GetData(), renderBmp, c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_CREATE_PICTUREVIEWER | c4d.RENDERFLAGS_OPEN_PICTUREVIEWER)  
            print result  
      

      This is the call:

        
      class CommandDataExecute(plugins.CommandData) :      
        __dialog = None  
        renderThread = None  
          
        def Execute(self, doc) :  
            doc = documents.GetActiveDocument()  
        
            if self.renderThread and self.renderThread.IsRunning() :  
                #todo  
                pass  
            else:  
                self.renderThread = RenderThread(doc)  
                print 'start thread successful: ', self.renderThread.Start()       
            return True  
        
        def RestoreLayout(self, sec_ref) :  
            pass  
      

      My goal is to render several pictures one after the other in the picture viewer. But the picture viewer doesn't open with this code (but if I launch the code directly in the plugin main thread it works). When I try to open the picture viewer from the menu after launching my plugin, I get an Access Violation and C4D terminates.
      So how can i reach my goal? Thanks for any advice in advance!

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 08/04/2013 at 08:30, xxxxxxxx wrote:

        threading does not allow gui operations, and also trying to lauch the whole rendering
        operation from another thread than the c4d main thread does seem to be pretty optimistic 
        to me 😉

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 08/04/2013 at 08:42, xxxxxxxx wrote:

          In this thread https://developers.maxon.net/forum/topic/7030/7938_cinema-freezes-while-rendering&KW=rendering&PID=32944#32944
          it is suggested to use threads for rendering. I'm confused, what's correct? Are there any other possibilites to prevent C4D from freezing during rendering?
          I speak of dozens of pretty big pictures (11k x 8k pixels). If there is no graphical response to the user, this will be a serious problem.

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 08/04/2013 at 10:03, xxxxxxxx wrote:

            When it comes to rendering and working on the scene at the same time. It might be simpler to open a second instance of C4D to work on it while your other instance is rendering the scene.

            Right click on the C4D shortcut icon and edit the Properties->Target field so you can run multiple instances of C4D.
            Example: "C:\Program Files\MAXON\CINEMA 4D R13\CINEMA 4D 64 Bit.exe" -parallel

            This will let you work on the same scene while it's rendering in the other instance of C4D. But you will probably have choppy performance. So to help with that you can limit the number of threads used for rendering.
            That will allow smoother editing of the scene in the other opened instance of C4D.

            In the preferences. Go to Renderer->Custom Number of threads
            Set it to 1 or maybe 2 threads. How high you can set it will depend on your computer system.

            -ScottA

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 08/04/2013 at 10:18, xxxxxxxx wrote:

              well,

              opening dialog boxes or even just printing to the console can lead in a threaded environment 
              to crashing c4d. it is stated in the documents and i can also confirm it from personal experience.
              i cannot guarantee that using the render document method is a gui operation, but it seems very
              likely imho. i am not quite sure nikklas has considered that in the linked thread, but i might be 
              wrong.

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 08/04/2013 at 11:46, xxxxxxxx wrote:

                Hi!

                RenderDocument() is not a GUI operation and it should be safe to call it from a threaded context.
                However, using the RENDERFLAGS_OPEN_PICTUREVIEWER flag only works in synchronous (non-
                threaded) context. You can use c4d.bitmaps.ShowBitmap() to manually show the bitmap in the
                Picture Viewer. Also remove the RENDERFLAGS_CREATE_PICTUREVIEWER flag in this case,
                otherwise your bitmap would be shown twice in the PV.

                Even modifieng the document to be rendered is fine while rendering, since the document is copied
                internally for rendering.

                Note that passing the C4DThread, which is normally used by RenderDocument() to check if it should
                stop rendering or continue by calling C4DThread.TestBreak(), will not work. Its a bug in the API and
                you will get a System Error with "bad argument to internal function".

                I can't reproduce that Cinema freezes.

                import c4d
                  
                class RenderThread(c4d.threading.C4DThread) :
                  
                    def __init__(self, doc, bmp, flags=c4d.RENDERFLAGS_EXTERNAL) :
                        super(RenderThread, self).__init__()
                        self.doc = doc
                        self.bmp = bmp
                        self.flags = flags
                        self.result = None
                        self._assert()
                  
                    def _assert(self) :
                        assert self.doc and self.bmp
                        assert all(self.bmp.GetSize()), "Bitmap is not initialized."
                  
                    def Main(self) :
                        self._assert()
                  
                        w, h = self.bmp.GetSize()
                        rdata = self.doc.GetActiveRenderData()
                        rdata = rdata.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)
                        rdata[c4d.RDATA_XRES] = w
                        rdata[c4d.RDATA_YRES] = h
                        rdata[c4d.RDATA_SAVEIMAGE] = False
                  
                        self.result = c4d.documents.RenderDocument(
                                doc, rdata.GetData(), self.bmp, self.flags)
                  
                def main() :
                    bmp = c4d.bitmaps.BaseBitmap()
                    bmp.Init(600, 300, 32)
                  
                    thread = RenderThread(doc, bmp)
                    thread.Start()
                    thread.Wait(False)
                    
                    print thread.result == c4d.RENDERRESULT_OK
                    c4d.bitmaps.ShowBitmap(bmp)
                  
                  
                main()
                
                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 08/04/2013 at 13:52, xxxxxxxx wrote:

                  Niklas,

                  Your example doesn't work for me.
                  While the thread is running and rendering. Everything is frozen and can't be used until the rendering is completed.

                  -ScottA

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 08/04/2013 at 14:17, xxxxxxxx wrote:

                    Hi Scott,

                    yes, that is due to this line:

                    thread.Wait(False)
                    

                    I need to keep the thread alive somehow, and there is no other option than waiting in the
                    main-loop until the thread is done in the script manager. In a plugin, like Nachtmahr did, you
                    can store the thread in the plugin object (CommandData in this case) to keep it alive.

                    I should've added that the script freezes Cinema, because, as a script, it can't be avoided (ok,
                    with some really hacky stuff, it could be avoided, but then, still, I couldn't open the PV from the
                    thread).

                    -Niklas

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 08/04/2013 at 14:33, xxxxxxxx wrote:

                      I also tried it in a simple Command plugin.
                      But I'm still getting the same results. Everything is frozen until the rendering is finished.

                      import c4d  
                      from c4d import gui  
                      from c4d import documents  
                      from c4d import utils, bitmaps, storage,plugins  
                        
                      #get an ID from the plugincafe  
                      PLUGIN_ID = 1000001  
                        
                      class RenderThread(c4d.threading.C4DThread) :  
                        
                        def __init__(self, doc, bmp) :  
                            super(RenderThread, self).__init__()  
                            self.doc = doc  
                            self.bmp = bmp  
                              
                        def render(self) :  
                                print "rendering"  
                                  
                                w, h = self.bmp.GetSize()  
                                rdata = self.doc.GetActiveRenderData()  
                                rdata = rdata.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)  
                                rdata[c4d.RDATA_XRES] = w  
                                rdata[c4d.RDATA_YRES] = h  
                                rdata[c4d.RDATA_SAVEIMAGE] = True  
                                self.bmp = c4d.documents.RenderDocument(self.doc, rdata.GetData(), self.bmp, c4d.RENDERFLAGS_EXTERNAL)              
                        
                          
                      class SceneRender(c4d.plugins.CommandData) :  
                        
                        def Execute(self, doc) :  
                          
                            bmp = c4d.bitmaps.BaseBitmap()  
                            bmp.Init(600, 300, 32)          
                            w, h = bmp.GetSize()      
                        
                            thread = RenderThread(doc, bmp)  
                            thread.Start()  
                            thread.render()  
                            #thread.Wait(False)  
                        
                            c4d.bitmaps.ShowBitmap(bmp)  
                        
                            return True   
                              
                      if __name__ == "__main__":      
                          
                        help = "The text shown at the bottom of C4D when the plugin is selected in the menu"      
                        plugins.RegisterCommandPlugin(PLUGIN_ID, "Scene Render", 0, None, help, SceneRender())
                      

                      -ScottA

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        On 09/04/2013 at 05:03, xxxxxxxx wrote:

                        Hi, you didn't overload the correct threading fuctions. Pls use C4DThread.Main() instead of render().
                        Also make your thread variable as a member of your SceneRender class, otherwise your RenderThread object will be destroyed when Execute(...) is left.

                        Cheers, Seb

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          On 09/04/2013 at 10:53, xxxxxxxx wrote:

                          Thanks Sebastien,

                          I've got it working now. And it does render the scene while I'm also working on it.
                          But I'm having trouble with ending the thread properly.
                          I'm not 100 % ceratin. But it seems like the thread never ever stops?:

                          import c4d  
                          from c4d import documents  
                          from c4d import utils, bitmaps, storage,plugins  
                            
                          #get an ID from the plugincafe  
                          PLUGIN_ID = 1000001  
                            
                          class RenderThread(c4d.threading.C4DThread) :   
                            
                            bmp = c4d.bitmaps.BaseBitmap()  
                                  
                            def Main(self) :      
                            
                                    doc = c4d.documents.GetActiveDocument()          
                            
                                    rdata = doc.GetActiveRenderData()  
                                    rdata = rdata.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)  
                                    w = rdata[c4d.RDATA_XRES]  
                                    h = rdata[c4d.RDATA_YRES]  
                                    rdata[c4d.RDATA_SAVEIMAGE] = True  
                                      
                                    bmp = c4d.bitmaps.BaseBitmap()  
                                    bmp.Init(int(w), int(h), 32)              
                                    bmp = c4d.documents.RenderDocument(doc, rdata.GetData(), bmp, c4d.RENDERFLAGS_EXTERNAL)   
                            
                              
                          class SceneRender(c4d.plugins.CommandData) :   
                            
                            doc = c4d.documents.GetActiveDocument()  
                            thread = RenderThread()  
                            image = thread.bmp  
                            
                            def Execute(self, doc) :  
                              
                                self.thread.Start()  
                                self.thread.End(False)      
                                if self.thread and not self.thread.IsRunning() : print "Render Finished"     #<------ This never happens!!! Thread never stops?  
                                  
                                return True  
                                  
                          if __name__ == "__main__":      
                              
                            help = "The text shown at the bottom of C4D when the plugin is selected in the menu"      
                            plugins.RegisterCommandPlugin(PLUGIN_ID, "SceneRender", 0, None, help, SceneRender())
                          

                          -ScottA

                          1 Reply Last reply Reply Quote 0
                          • H
                            Helper
                            last edited by

                            On 09/04/2013 at 14:51, xxxxxxxx wrote:

                            Hi Scott,

                            your thread is still running while the Execute() method has already reached its end. At the point
                            you check if the thread is still running, the thread is actually still running, and you never happen
                            to reach this statement again. Try this instead:

                                    if self.thread and self.thread.IsRunning() :
                                        print "Cannot start render, still running ..."
                                    else:
                                        self.thread.Start()
                            

                            If you pressed the command in Cinema, it will start to render. If you then press it again and
                            rendering is not done at this point, it will tell you about this, otherwise it will start a new
                            render thread.

                            You do not have to call End() on the thread object. If you wanted to wait until the thread is
                            finished, you would again block Cinema's main-thread resulting in a temporary freeze.

                            PS: One usually explictly binds a thread to a context, it is rather unusual to create one thread
                            object and start it more than once. 🙂

                            Best,
                            -NIklas

                            1 Reply Last reply Reply Quote 0
                            • H
                              Helper
                              last edited by

                              On 09/04/2013 at 15:29, xxxxxxxx wrote:

                              Thanks Nik,

                              I did try it that way also. But it still doesn't give me any way check the thread for it's stopped state.
                              Per your comment on CGTalk. You made it sound like we can't get the stopped state due to a bug.
                              I'm not sure if that's what you meant?

                              The way I normally use threads is:
                              -Create a thread class.
                              -Run a loop inside that class with some task being run per loop iteration
                              -Then ending the thread

                              All of this is done in the thread class.
                              To make the thread class code execute. I would use a call from one of the plugins methods. For example when a gui button is pressed.
                              I don't normally do any thread status checking from within the plugin's methods like in my example. So this is new territory for me.

                              At this point. I can create a thread and render the scene out while still working on the scene. Which is sort of a success.
                              But there is no visual feedback as to what's going on. And no way to even tell when the thread has finished running.
                              And if I try to open the image viewer while rendering. All I get is a black image in the image viewer. Instead of the sequence of renders being rendered.

                              You mentioned you had similar problems and had to find a completely different way to hack around this without using threads at all. So maybe what I'm trying to do can't be done?

                              -ScottA

                              1 Reply Last reply Reply Quote 0
                              • H
                                Helper
                                last edited by

                                On 09/04/2013 at 22:56, xxxxxxxx wrote:

                                Hi Scott,

                                What I said is, that when you start a render in the Picture Viewer using RenderDocument(), you can
                                not stop the render anymore (eg. closing the PV doesn't ask you to stop the render and the
                                "Stop Render" command in the PV is greyed out).

                                I don't see where you are unable to check for its stopped state. IsRunning() is just fine for this job.
                                A thread ends automatically when it quits its Main() method, you do not have to call End() on it.
                                What End() does is stated in the docs: It enables you to interrupt the thread before it is actually
                                done (imagine, eg: When Cinema closes and your thread is still running, you want to tear it down,
                                either waiting for it to finish or even interrupt it).

                                As for the visual feedback: What do you expect? You didn't implement any visual feedback

                                Since my PV Render Queue plugin is only rendering in the Picture Viewer and does not require
                                any reference to the rendered image, I've been using a simple CallCommand() to start the
                                Picture Viewer rendering (as I said @CGSociety). Although I'm not a fan of using CallCommand(),
                                it was necessary to get around this "I can't stop the rendering, what a sh*t plugin!" thing. 😉

                                Best,
                                -Niklas

                                1 Reply Last reply Reply Quote 0
                                • H
                                  Helper
                                  last edited by

                                  On 10/04/2013 at 04:14, xxxxxxxx wrote:

                                  Thanks for all the answers!
                                  I changed my code a bit to copy yours, but now I get this Exception after calling ShowBitmap() :
                                  RuntimeError: illegal operation, invalid cross-thread call

                                  This is my Thread:

                                    
                                  class RenderThread(C4DThread) :  
                                    def __init__(self, doc) :  
                                        super(RenderThread, self).__init__()  
                                        self.doc = doc  
                                      
                                    def Main(self) :  
                                        renderData = self.doc.GetActiveRenderData()  
                                        xResolution = int(renderData[c4d.RDATA_XRES])  
                                        yResolution = int(renderData[c4d.RDATA_YRES])  
                                    
                                        renderBmp = c4d.bitmaps.BaseBitmap()  
                                        renderBmp.Init(x=xResolution, y=yResolution, depth=32)  
                                    
                                        result = documents.RenderDocument(self.doc, renderData.GetData(), renderBmp, c4d.RENDERFLAGS_EXTERNAL)  
                                        print result  
                                        if result == c4d.RENDERRESULT_OK:  
                                            c4d.bitmaps.ShowBitmap(renderBmp)  
                                  

                                  And this is the call:

                                    
                                  class CommandDataExecute(plugins.CommandData) :      
                                    __dialog = None  
                                    renderThread = None  
                                      
                                    #plugin started by user  
                                    def Execute(self, doc) :  
                                        doc = documents.GetActiveDocument()  
                                    
                                        if self.renderThread and self.renderThread.IsRunning() :  
                                            print 'Thread is still running. Please try again later'  
                                        else:  
                                            self.renderThread = RenderThread(doc)  
                                            print 'start Thread erfolgreich: ', self.renderThread.Start()  
                                  

                                  Any suggestions on this?

                                  1 Reply Last reply Reply Quote 0
                                  • H
                                    Helper
                                    last edited by

                                    On 10/04/2013 at 06:13, xxxxxxxx wrote:

                                    Hi Nachtmahr,

                                    it has already been said: GUI Operations can not be invoked from a thread. ShowBitmap() opens
                                    the Picture Viewer and is therefore a GUI operation. You will have to invoke it from the main thread.
                                    Eg. all Message() and CoreMessage() are usually called from the main thread so it is safe to open the
                                    bitmap from one of these methods.

                                    Best,
                                    -Niklas

                                    1 Reply Last reply Reply Quote 0
                                    • H
                                      Helper
                                      last edited by

                                      On 10/04/2013 at 06:50, xxxxxxxx wrote:

                                      Hi Niklas,

                                      yes you said that before, I remember. But to call the PV from the main thread, I only see 2 possibilities:

                                      1. In the mainthread I wait for the renderthread to return, so I can open the PV with the picture. But in this case the thread is useless.
                                      2. I have to send a message from the renderthread, catch it with my Plugin and open the PV. But how? When I search for "SendMessage" I only get GeDialog.SendMessage which is not what I can use (I think) and to catch a message I'd use CommandData.Message. But here the API (only in the one for C++ -.-) states, that I only can use these two: MSG_COMMANDINFORMATION and MSG_BODYPAINTEXCHANGE. Is there any other Message I can catch, where to find the information, how to send usermessages, etc etc. So many questions and the documentation let them all be unanswered... _<_t_>_
                                        |

                                      ---|---

                                      <_<_t_>_

                                      1 Reply Last reply Reply Quote 0
                                      • H
                                        Helper
                                        last edited by

                                        On 10/04/2013 at 07:47, xxxxxxxx wrote:

                                        Hi Nachtmahr,

                                        no, GeDialog.SendMessage() is not what you need. Many ways will lead you to Rome, and so there
                                        are many was this can be achieved. I think the best option would be to implement a MessageData
                                        plugin and override its ~.CoreMessage() method. Then you can send EventAdd() from your
                                        RenderThread and check for EVMSG_CHANGE in CoreMessage(). When this message is sent, you
                                        simply check the thread for the bitmap and open it in the Picture Viewer. Make sure you have a
                                        reference to the thread. You can either use global variables (not good design) or pass the thread to
                                        the MessageData plugin.

                                        -Niklas

                                        1 Reply Last reply Reply Quote 0
                                        • H
                                          Helper
                                          last edited by

                                          On 10/04/2013 at 08:14, xxxxxxxx wrote:

                                          Thanks, I'll try that tomorrow! Looks like an interesting approach 🙂

                                          1 Reply Last reply Reply Quote 0
                                          • H
                                            Helper
                                            last edited by

                                            On 10/04/2013 at 12:10, xxxxxxxx wrote:

                                            Nik,

                                            You're misunderstanding me.
                                            I can run the thread just fine. But I could not find a way to test for it's finished condition.
                                            Then it just hit me today like a ton of bricks that the reason I probably can't do this is because I'm using a CommandData() plugin.
                                            Which means after the plugin is executed from the menu and the thread starts. The plugin then probably closes. So it makes sense to me now that there would be no way to check the Thread's finished status using a CommandData() plugin.

                                            Do you happen to have an old version of your plugin where it renders to the image viewer with a thread. But does not stop? Or have you deleted it?
                                            I would like to see what type of plugin you used(GeDialog?) And how you called the c4d.bitmaps.ShowBitmap() method.

                                            If not don't sweat it.

                                            -ScottA

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post