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.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.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)
class Render_Thread(c4d.threading.C4DThread) :
    def Main(self) :
        thread = self.Get()
        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")
        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)
            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")
      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 :)


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 ^^)