Problems with R16 [SOLVED]

On 10/10/2014 at 04:13, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   16 
Platform:   Windows  ; Mac  ;  Mac OSX  ; 
Language(s) :     C++  ;


I have massive problems in R16. All my code runs fine in R13-15 but in R16 I get some crashes that make no sense. Also during debugging I a lot of times get "Cinema4D.exe hat einen Haltepunkt ausgelöst". This happens for GetInputState for example, but also for GetVector of a basecontainer.

This happens in a scene hook btw. At first I thought this would be up to my code somehow but then I tried it directly with an unmodified version of the Cinema 4D sdk and the same happens there. Here is my test code added to main.cpp of the C4D sdk:

    class TestHook : public SceneHookData
    	static NodeData *Alloc(void) { return NewObj(TestHook); }
    	virtual EXECUTIONRESULT Execute(BaseSceneHook* node, BaseDocument* doc, BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags)
    		if(bt->TestBreak()) return EXECUTIONRESULT_OK;
    		BaseContainer res;
    Bool PluginStart(void)

GetInputState ALWAYS triggers a break with the above mentioned message window. And bt->TestBreak() randomly crashes (sometimes it does and sometimes it doesn't). I have no other plugins installed, just built the c4d sdk with the above addition of code.

As mentioned before, compiling with R13 API does not trigger any crashes (also running that version in R16.027 does NOT result in any crashes) and my code runs fine. Compiling with R16.027 brought up these problems (beside others).

What is this?

On 10/10/2014 at 04:38, xxxxxxxx wrote:

I'm not quite sure. I somehow have the feeling this is similar to the R16 memory leaks issue.

There might be more... for the lack of a better word "internal breakpoints" left over.

And then again may it's totally different, as I missed the crashes on first read (and posted too quickly).

Do you have this issue compiling the pure SDK examples as well? As you added code, I guess not.
I'll try this here, but my Mac environment is not set up completely, I can only check under Windows at the moment.

On 10/10/2014 at 04:43, xxxxxxxx wrote:

Nope, the sdk examples compile fine. I only noticed this when I ported my own project to R16. In a scenehook it is triggered so often that it makes debugging impossible that's why it started bothering me and when I got a crash at bt->TestBreak() I thought I'd better ask.

I am only checking under windows myself right now.

On 10/10/2014 at 04:45, xxxxxxxx wrote:

Ah, if you are under Windows, could you provide me with a plugin compiled in debug mode? This may be faster, than me having to set something up first. Just the binary should be fine for a first shot.
By the way, are you porting to R16 changing the code or are you trying to go with __LEGACY_API define?

On 10/10/2014 at 04:57, xxxxxxxx wrote:

Calling GetInputState() from any other but the main thread causes a breakpoint to be triggered.

The BaseContainer getter methods trigger breakpoints, for instance, if you call GetVector(id) but the
data at the specified id is not a vector.

This is one reason why it is important to properly initialize the node's container in Init().

On 10/10/2014 at 05:13, xxxxxxxx wrote:

Sure, where can I send it? And yep, I have changed the code and am not running with __LEGACY_API (though I do have some typedefs and #defines used myself).

Niklas: Shouldn't bt be the main thread here? It's not explicitly stated but I may want to check this myself. How do I get a pointer to the C4D main thread or is it the main thread if bt is nullptr?

Thanks for the tip about GetVector(id) as well but I have this element intialised correctly and the id is also correct and it is definetly a VECTOR element (and if it wasn't shouldn't that break point also be triggered in R13?)

On 10/10/2014 at 05:24, xxxxxxxx wrote:

We mailed already, that address would be fine.

On 10/10/2014 at 05:58, xxxxxxxx wrote:

Ok, at least I could find the culprit for the vector and Niklas was right. Although in the Init() of my object the vector was correctly initialised, I have a macro command where that element was set as a float! When I generated my object without the trigger :)

Thanks for that Niklas! Worth an entry in the docs I'd say..

Still the problem with GetInputState persists. Why is it triggering a break in R16 but not in R13 (during debugging). Hope you find a clue Andreas. Thanks for the effort!

On 10/10/2014 at 06:21, xxxxxxxx wrote:

Glad it helped. :-)

Afaik, Execute() is not called from the main thread. The main thread would not have pointer (the OS
thread ID is 0 for the main thread). You can use GeIsMainThread() to check if you're in the main thread
at any time.

Afaik (again), the BaseThread pointer is only passed so you can call TestBreak(). For instance, you
can tell the RenderDocument() function by returning True from your own C4DThread::TestDBreak()
implementation and passing the C4DThread's BaseThread pointer to the RenderDocument()

I've never tried to use BaseThread::TestBreak() from Execute(), maybe that returns True already
if ESC is pressed or something like that. :)


On 10/10/2014 at 06:45, xxxxxxxx wrote:

Thanks Niklas. And again right, it's definetly not the main thread (just checked with GeIsMainThread). Also I found the threading information in the docs stating that SceneHookData::Execute is indeed called in a threaded context and probably that's not necessarily the main thread. Makes sense.

Furthermore it seems the TestBreak call does not crash now anymore! Re-run several times now without a crash triggering so I guess that solved that issue already (so glad!).

In my own threads (executed within Execute of the scene hook) I override TestDBreak and that's also where I am calling GetInputState checking for a the ESC key (always triggering a break...very annoying! :). I guess I could pass the bt to the thread class and simply check for a break with it as  bt->TestBreak does indeed work when the user hits ESC (if I remember correctly).

At the beginning of the Execute however I also need the left mouse button so I kind of don't get around using GetInputState. Is there an alternative to it? (though I surely can outcomment it for debugging purposes with a preprocessor directive)

On 10/10/2014 at 06:45, xxxxxxxx wrote:

Niklas is right (of course I might add).
The first breakpoint is triggered, because GetInputState() is called from a thread.

On 10/10/2014 at 06:49, xxxxxxxx wrote:

Thanks Andreas for confirming. I guess if there is no alternative I will go with my _DEBUG preprocessor solution to avoid the break points.

On 10/10/2014 at 06:51, xxxxxxxx wrote:

I've noted two things for SDK Docs:
- a hint on internal breakpoint for using wrong datatype functions for a certain ID
- some more explanation on threading, the dos and don'ts, plus info on the threadcontext PluginStart gets called in

Katachi, do you mind, marking this as solved?

On 10/10/2014 at 06:55, xxxxxxxx wrote:

Thanks both of you and yep, consider it solved!

On 10/10/2014 at 09:45, xxxxxxxx wrote:

Glad I could help. :-)

@Andreas: good to read you're taking notes on things to be added to the documentation! :slightly_smiling_face:

Originally posted by xxxxxxxx

so I kind of don't get around using GetInputState. Is there an alternative to it? (though I surely can outcomment it for debugging purposes with a preprocessor directive)

Maybe you can get this data from the main thread (eg. in a MessageData plugin) and request it
from a scene hook.

I did not try to compile this code, it is completely from scratch! Just to give you an idea of
what I'm talking about. It might need to a bit of performance adjusting, like "only re-get the
input state if at least 200 milliseconds passed since the last time the state was retreived".

Edit : AAAAaaaand you should probably add a lock/semaphore to the InputStateData structure, or
return a copy of the data from GetRecentInputState(). :-)

> /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /// RecentInputState.h
> /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> #include "c4d.h"
> #pragma once
> struct InputStateData
> {
> Int32 timestamp; // current timestamp returned by GeGetMilliSeconds();
> BaseContainer bc; // filled by GetInputState();
> InputStateData() : timestamp(-1), bc() { }
> };
> const InputStateData* GetRecentInputState(Int32 pluginid);
> Bool RegisterRecentInputStateHook(Int32 pluginid, Int32 askdevice, Int32 askchannel);
> /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /// RecentInputState.cpp
> /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> #include "RecentInputState.h"
> #include "c4d.h"
> #include "HashMap.h"
> class InputStateHook;
> static maxon::HashMap<Int32, InputStateHook*> g_map;
> class InputStateHook : public MessageData
> {
> public:
> InputStateHook(Int32 askdevice, Int32 askchannel);
> virtual const InputStateData* GetData() const;
> virtual Bool CoreMessage(Int32 id, const BaseContainer& msg) override;
> private:
> Int32 _askdevice, _askchannel;
> InputStateData _data;
> };
> InputStateHook::InputStateHook(Int32 askdevice, Int32 askchannel)
> : _askdevice(askdevice), _askchannel(askchannel)
> {
> }
> Bool InputStateHook::CoreMessage(Int32 id, const BaseContainer& msg)
> {
> if (GetInputState(_askdevice, _askchannel, _data.bc))
> {
> _data.timestamp = GeGetMilliSeconds();
> }
> return true;
> }
> const InputStateData* InputStateHook::GetData() const
> {
> if (_data.timestamp > 0)
> return &_data;
> else
> return nullptr;
> }
> static const InputStateHook* GetRecentInputState(Int32 pluginid)
> {
> maxon::HashMap<Int32, InputStateHook*>::Entry* entry = g_map.FindEntry(pluginid);
> if (entry && entry->GetValue())
> {
> InputStateHook* hook = entry->GetValue();
> return hook->GetData();
> }
> return nullptr;
> }
> Bool RegisterRecentInputStateHook(Int32 pluginid, Int32 askdevice, Int32 askchannel)
> {
> InputStateHook* hook = NewObj(InputStateHook, askdevice, askchannel);
> if (hook)
> {
> String name = "InputStateHook:" + String::IntToString(askdevice) +
> String::IntToString(askchannel);
> Bool success = RegisterMessagePlugin(pluginid, name, 0, hook);
> if (success)
> g_map.Put(pluginid, hook);
> return success;
> }
> return false;
> }


On 10/10/2014 at 10:02, xxxxxxxx wrote:

Hey Niklas, thanks. That's quite a clever workaround idea!