Crash when running plugin on complex scene



  • On 21/05/2016 at 10:25, xxxxxxxx wrote:

    Hello everybody,

    I made a preview plugin, which is basically a GeDialog with an UserArea.
    To render the document or the preview, I use a C4DThread. 
    It is working perfect when I have a simple scene (e.g only a cube, some lights)
    But as soon as I try to use my plugin with a bigger scene, more mesh etc. 
    Cinema 4D just crashes. There is a popup that says: "Application Error!" and then a path to the bugreport.
    My problem now is, 1. I don't know why Cinema is crashing and second I don't reall understand the Bugreport.

    This is the complete code that I use.

    import c4d
    from c4d import gui, plugins, utils, bitmaps, storage
    from c4d.threading import C4DThread
    import os
      
    PLUGIN_ID = 1037431
      
      
    b_calculate = 1000
    quality = 1010
    scale = 1020
    antislider = 10016
    Render_Image = bitmaps.BaseBitmap()
      
    calculating = False
    dis_width = 0
    dis_height = 0
    ww = 0
    hh = 0
    new_rd = None
    redraw = False
      
    class RenderDisplay(gui.GeUserArea) :
      
        def GetMinSize(self) :
            self.width = 250
            self.height = 250
            return (self.width, self.height)
        
        def __init__(self, bmp) :
            global dis_width
            global dis_height
            
            super(RenderDisplay, self).__init__()
            self.bmp = Render_Image
            dis_width = self.GetWidth()/100
            dis_height = self.GetHeight()/100
            
        def DrawMsg(self, x1, y1, x2, y2, msg_ref) :
            self.OffScreenOn()
            self.SetClippingRegion(x1, y1, x2, y2)
              
            Color = self.GetColorRGB(c4d.COLOR_BG)
            back_color = c4d.Vector(Color['r'] / 255.0, Color['g'] / 255.0, Color['b'] / 255.0)
              
            self.DrawSetPen(back_color)
            self.DrawRectangle(x1, y1, x2, y2)
              
            if self.bmp:
                ww = self.bmp.GetBw()
                hh = self.bmp.GetBh()
                if ww > 0 and hh > 0:
                    self.DrawBitmap(self.bmp, 0, 0, self.GetWidth(), self.GetHeight(), 0, 0, ww, hh, c4d.BMP_NORMAL)
                    self.Redraw(True)
      
    class Render_Thread(c4d.threading.C4DThread) :
        def Main(self) :
            thread = self.Get()
            doc=c4d.documents.GetActiveDocument()
            Render_Image.Init(int(ww),int(hh),24)
            rend = c4d.documents.RenderDocument(doc ,new_rd, Render_Image, c4d.RENDERFLAGS_EXTERNAL|c4d.RENDERFLAGS_NODOCUMENTCLONE, thread)
            
    class DiffRender(c4d.gui.GeDialog) :
        thumbnail = RenderDisplay(None)
        thread = Render_Thread()
         
        def CreateLayout(self) :
            self.SetTitle("Owl View V2.3")
              
            self.AddUserArea(100000, c4d.BFH_CENTER | c4d.BFV_CENTER | c4d.BFH_SCALEFIT |c4d.BFV_SCALEFIT, 3, 3)
            self.AttachUserArea(self.thumbnail, 100000)
              
            self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, title="", rows=4, cols=4,)
           
            self.AddEditSlider(id=quality, flags=c4d.BFH_SCALEFIT, initw=50, inith=12)
            self.AddStaticText(id=scale, flags=c4d.BFH_SCALEFIT | c4d.BFH_CENTER | c4d.BFV_CENTER, initw=120, inith=12, name="--- x ---")
            self.antislider = self.AddSlider(antislider, c4d.BFH_SCALEFIT, 1, 3)
            self.AddButton(b_calculate, c4d.BFH_SCALEFIT | c4d.BFH_CENTER | c4d.BFV_CENTER, initw=0, inith=0, name="Render")
            self.GroupEnd()
            return True
      
        def InitValues(self) :
            self.SetPercent(quality, 1, min=0.0, max=100.0, step=1.0, tristate=False)
              
            doc = c4d.documents.GetActiveDocument()
            rd = doc.GetActiveRenderData()
              
            ww = rd[c4d.RDATA_XRES_VIRTUAL]
            hh = rd[c4d.RDATA_YRES_VIRTUAL]
      
            qual = self.GetReal(quality)
            self.SetString(id=scale, value="( " + str(int(ww * qual)) + " x " + str(int(hh * qual)) + " )")
            
            calculating = True
            return True
      
        def Command(self, id, msg) :
            global calculating
            global anti
            global new_rd
            global ww
            global hh
            
            
            doc = c4d.documents.GetActiveDocument()
            
            self.qualsli = self.GetReal(quality)
            self.SetReal(quality, self.qualsli, min=0.01, max=1.0, step=0.01, format=c4d.FORMAT_PERCENT, min2=0.01, max2=1.0,  tristate=False)
            
            self.antsli = self.GetReal(antislider)
            self.SetReal(antislider, self.antsli, min=0.0, max=2.0, step=1.0, min2=0.0, max2=2.0, tristate=False)
            
        
            
            if id == quality:
                rd = doc.GetActiveRenderData()
                ww = rd[c4d.RDATA_XRES_VIRTUAL]
                hh = rd[c4d.RDATA_YRES_VIRTUAL]
                qual = self.GetReal(quality)
                self.SetString(id=scale, value="( " + str(int(ww * qual)) + " x " + str(int(hh * qual)) + " )")
                
                
            if id == b_calculate:
                calculating = True
                self.thread.Start(mode=c4d.THREADMODE_ASYNC, priority=c4d.THREADPRIORITY_NORMAL)
                anti = long(self.antsli)
                rd=doc.GetActiveRenderData()
                o_ww=rd[c4d.RDATA_XRES]
                o_hh=rd[c4d.RDATA_YRES]
                ww=int(o_ww*self.GetReal(quality))
                hh=int(o_hh*self.GetReal(quality))
                new_rd=doc.GetActiveRenderData().GetData()
                new_rd[c4d.RDATA_XRES]=ww
                new_rd[c4d.RDATA_YRES]=hh
                if anti == 0:
                    new_rd[c4d.RDATA_ANTIALIASING] = 0
                    
                if anti == 1: 
                    new_rd[c4d.RDATA_ANTIALIASING] = 1
                    
                if anti == 2:
                    new_rd[c4d.RDATA_ANTIALIASING] = 2
                
            return True
        
    class SimpleCC_CMD(c4d.plugins.CommandData) :
        dialog = None
        def Execute(self, doc) :
              if self.dialog is None:
                 self.dialog = DiffRender()
              return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaulth=270, defaultw=195)
      
        def RestoreLayout(self, sec_ref) :
              if self.dialog is None:
                 self.dialog = DiffRender()
              return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
      
    if __name__ == "__main__":
          bmp = bitmaps.BaseBitmap()
          dir, f = os.path.split(__file__)
          fn = os.path.join(dir, "res", "icon.tif")
          bmp.InitWith(fn)
          c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID, str="Owl View", help="A simple plugin to preview your scene", info=0, dat=SimpleCC_CMD(), icon=bmp)
    

    This code works similar to Nitromans Magic Preview, with the squares that shows you the render process, but since I implemented this feature, I get the crashes.

    Hopefully someone can help me :)

    greetings, 
    neon



  • On 23/05/2016 at 08:44, xxxxxxxx wrote:

    Hi neon,

    it looks like you have some severe threading issue. At least when I tried it here in a debugger, I found some evidence of trashed memory.
    I hope you don't mind, we can't debug this for you.

    Some stuff you are doing definitely looks fishy. Not saying any one is the reason for your problem, nor do I think my list is complete. This is just the stuff, that occurred to me immediately:

    You store global RenderData in new_rd. You modify this after you have started the thread, which is using it. Most likely a bad idea and in best case your thread is only working on inconsistent RenderData.

    Similar with the BaseBitmap you are rendering into. One thread renders into the bitmap, while another reads it in order to display it in the dialog. I would have said, this might work out, if you only changed the bitmap, but you are re-initializing the BaseBitmap from your thread, which has some chance of relocating the image in memory...

    I'm also not sure it's a good idea to call Redraw() from DrawMsg(). I'd rather do it the other way round and trigger the redraw from your render thread, like it's done int the Py-MemoryViewer example.

    I know, this is probably not the answer you have waited for, but perhaps it still helps.



  • On 23/05/2016 at 10:05, xxxxxxxx wrote:

    Hello Andreas,

    Thanks for your reply!

    Your reply did help me alot, I changed some things like the Redraw call and the RenderData (I really feel kinda dumb about that, should have seen this myself).
    And It is not crashing anylonger. I have not started in debugging my code for memory leaks, but as far as I can tell, it doesn't seem to have big issues.

    But I am very greatfull for your help and your time you invested! 
    I am now trying to debug it myself and see if I can do something about the trashed memory you mentioned (if there is still any, changed almost all my code ^^)

    greetings, 
    neon


Log in to reply