Debugging Plugins

Introduction

The following article will be all around debugging your Cinema 4D plugins. Addressing mainly the beginners in plugin development, there still may be one or two tricks for the veterans as well. It begins with setting up your environment for an increased fun in debugging, then goes over to some general debugging techniques, before finishing with some more Cinema 4D specific techniques. While the screenshots in this article are taken from Visual Studio 2013 and Xcode 6.0.1, everything shown and said should work in Visual Studio 2012 and Xcode 5.x or 6.1.x as well. If there's a place this is not the case, please notify sdk_support@maxon.net.




1. Preparing Your Toolbox


1.1 Start Cinema 4D Automatically With Your Debugger

At first you'll want to configure your project for debugging. Most of it is already set correctly by default (assuming you are using the project files delivered with the SDK). In order to have Cinema 4D started, once you click the "Debug" button, you need to set the path to the Cinema 4D executable.

Depending on your Cinema 4D version, operating system and the IDE in use it's a bit different.

1.1.1 Visual Studio

Enter the project properties and go to Debugging settings, like so: Menu: Project → Properties → Configuration Properties → Debugging

For Cinema 4D R15 and following it is sufficient to simply select your Cinema 4D executable for Command. The result will probably look like this ..\..\Cinema 4D 64 Bit.exe (R15) or ..\..\..\Cinema 4D.exe (R16), although the path may depend on your system and project location. Especially with R16 where you can move your projects anywhere you like.

For Versions before Cinema 4D R15 it is suggest to use both options marked by 1 in the screenshot: Command and Working Directory. Unfortunately Working Directory can not be a relative path. But in order to make use of the Debug mode (explained next), you will need to have Working Directory point to the directory, where the Cinema 4D executable is located (absolute path, the trailing backslash is important) and only have the name of the Cinema 4D executable as Command, like it is shown in the following screenshot.

thumb
Debug options in VS2013, 64-Bit Debug build for Cinema 4D R13 SDK

Attention
It's pretty easy to mess up the build targets, if you don't pay attention to the selected Configuration and Platform (the exclamation marks in the screenshot above). You need to select Configuration and Platform before applying any changes.
It is recommended to set the Debugging Command for Configuration: "All Configurations". As the executable differs for 32-Bit and 64-Bit version of Cinema 4D, you'll have to configure Platform Win32 in a second step, if you still need to build 32-Bit versions.
Note
The name of the Cinema 4D executable depends on the version. You need to have the correct Platform (right exclamation mark) selected. Since Cinema 4D R15 there are no 32-Bit versions anymore (although we unfortunately forgot to remove the 32-Bit Platform in R15 and R16).

1.1.2 Xcode

Enter the scheme settings either via menu Menu: Product → Scheme → Edit Scheme... or a bit quicker via Cmd + <. At first one needs to note, that this is the place to switch between debug and release builds, which may be a bit confusing especially for developers who grew up with Visual Studio. Then choose Executable → Other... and select your Cinema 4D executable, like in the screenshot below.

thumb
Xcode Scheme Settings – Info

1.2 Start Cinema 4D in Debug Mode

You may want to start Cinema 4D itself in debug mode. In debug mode Cinema 4D will provide you with all kinds additional runtime information on a system console. For example valuable information on memory management, it will even warn you about memory leaks. Please note, this will slow down Cinema 4D considerably, so you may want to enable Debug mode temporarily only. An even better way would be to have special debug build targets, which additionally enables the debug mode.

Attention
It is advised for every plugin developer to use debug mode at least once before releasing a plugin, just to be sure about memory leaks.

1.2.1 Cinema 4D < R15

Create an empty text file named c4d_debug.txt inside of the Cinema 4D program folder, next to your Cinema 4D executable. As long as you followed the Command and Working Directory setup before, it's that simple!

1.2.2 Cinema 4D ≥ R15 and Visual Studio

Add g_alloc=debug to your Commandline Arguments (marked 2 in the above screenshot) for Configuration: "Debug" and Platform: "All Platforms".

1.2.3 Cinema 4D ≥ R15 and Xcode

In Xcode's scheme settings change to the arguments tab. Add g_alloc=debug as argument, like in the screenshot below.

thumb
Xcode Scheme Settings – Arguments

1.3 More Commandline Arguments

-debug
≥R15: Use this instead of g_alloc=debug in order to start Cinema 4D in Debug mode without the additional memory management information and memory leak detection
g_logfile=[string]
≥R16: Debug Console output will be written to a file
g_console=true
≥R16: Open the Debug Console (was done via -debug in versions <R16)

Even more command line options can be found in <C4D install dir>/resource/config.txt.

Note
Note the "-" in front of some parameters. These have historic reasons. For new parameters beginning with "g_" there is no prefix "-", neither in Visual Studio or Xcode nor on commandline.

1.4 Use TypeViewer for VisualStudio and Xcode

You may have seen downloads of TypeViewer for Visual Studio 2005 and 2010 in our development area. Good news for everybody, who's working with Visual Studio 2012+ and is developing with Cinema 4D SDK R15+. The functionality is seamlessly integrated into the project files. No additional work needed. It simply works. The same is true for Xcode.




2. Time Is Money


You can significantly speed up your development turnaround times by creating a special start-up layout in Cinema 4D for your plugin development.

  • If you have modified your Cinema 4D start-up layout before, don't forget to save it, so you can return to it anytime you need to.
  • Remove everything not needed in the context of your new plugin, especially Content Browser views can severely impede your start-up times.
  • Integrate the console Menu: Script → Console... into your layout, e.g. have it docked on one side of your screen. This provides you with a quick overview on your debug output.
  • Open your plugin and attach its interface or have a start-up scene using your plugin.
  • Save this layout as e.g. plugin_dev.l4d as well as start-up layout.




3. Printf Debugging

 Further Reading:

Some may call it the purest form of debugging, it certainly is one of the most simple. In Cinema 4D you have several options to print information. They differ in location of output, if the code is still active in release builds and type of string to print. Let's go through them quickly, noting advantages and disadvantages. Afterwards you'll find a table, showing which function outputs where and when.

3.1 void GePrint(const String& str)

Use GePrint in your plugin to show elementary messages/information to the user. For example a message on successful initialization plus version information could be printed to the script console. You should avoid to flood the script console, as the user might want to use the script console himself or other components want to have their info visible to the user as well. Since GePrint prints regardless of build target and Debug mode, it's less suited for debugging needs.

3.2 void GeConsoleOut(const String& str)

GeConsoleOut behaves a bit differently on Windows and Mac. On Windows it basically does the same as GePrint. Yet on Mac it prints to the debugger's output window as well.

3.3 void GeDebugOut(const String& s) and void GeDebugOut(const Char* s, ...)

As the name suggests, these functions are made for debugging. Mainly because the output can only be seen, when Cinema 4D is run in Debug mode. GeDebugOut comes in two flavors, the first works with Cinema 4D's String class (just like GePrint). The second works with format strings (C-strings with special placeholders to provide formatted output), as you might know them from ANSI-C and stdio-functions, like printf. Here you can use all the format strings known from the last mentioned stdio-functions. So you can do something like this:

const Char name[] = "Cinema 4D";
UInt32 val = 5;
GeDebugOut("Debugging %s plugins is %dx more fun than reading white pages (0x%p).", name, val, &val);
Attention
Your resulting string must not be longer than 2048 characters!
Despite it's name, GeDebugOut still does its job in release builds. If the user manages to start Cinema 4D in Debug mode, he will be able to see your output. Having this said, it should be clear that it uses CPU time in release builds as well.
So you will need to take care for this.

3.3 New in Cinema 4D R15

3.3.1 CriticalOutput(fmt)

The name says all about its purpose. CriticalOutput is to be used in insolvable situations. Printing-wise it works much like the second GeDebugOut flavor, as it's using C-like format strings. Yet, there are some differences:

  • It triggers a breakpoint, if there is a debugger present
  • Information on source-file and line number is added
  • A line break is automatically appended
  • No 2048 character limit

3.3.2 DiagnosticOutput(fmt)

This is pretty much the same as second flavor GeDebugOut. Only difference is an automatically appended line-break.

3.3.3 DebugOutput(flags, fmt)

Last but not least, DebugOutput will probably get your most used debug print function. It's main advantage, it does nothing in release builds. Neither will it reveal your output to a user running Cinema 4D in Debug mode, nor will it use any CPU time in a release build. If you are developing for R15 and above, it's suggested, you take a closer look at this function and try to get used to it. Additionally you can make use of OUTPUT_FLAGS to adjust it to your needs:

OUTPUT_DIAGNOSTIC
Set by default
OUTPUT_WARNING
Prepends a line "WARNING:" to your output
OUTPUT_CRITICAL
Prepends a line "CRITICAL:" to your output
OUTPUT_NOLINEBREAK
Suppresses an automatically appended line-break, so you can use multiple calls to assemble a your line
OUTPUT_HEADER
Adds source file and line number information
Note
For DebugOutput to print anything in Cinema 4D versions ≥ R16, you need to add -debug to your Commandline Arguments. g_alloc=debug is not sufficient.

3.4 Which Print Goes Where

Cinema 4DDebug Mode: OnDebug Mode: Off
LocationScript ConsoleDebuggerDebug ConsoleScript ConsoleDebugger
Build TargetRel.Dbg.Rel.Dbg.Rel.Dbg.Rel.Dbg.Rel.Dbg.
GePrint××××××
GeConsoleOutMM××MM
GeDebugOut××××××
Additional print methods with Cinema 4D ≥ R15:
CriticalOutput××××××
DiagnosticOutput××××××
DebugOutput××××××××
√: Output is visible     ×: No output      ∗: Output is configurable via OUTPUT_FLAGS      M: Mac only

3.5 Conversions to String

To have all info in one place, you'll need the following functions to print your variables:

Cinema 4D < R15Cinema 4D ≥ R15
String::ToRealString::ParseToFloat
String::ToLongString::ParseToInt32
LLongToStringString::IntToString
LongToStringString::IntToString
RealToStringString::FloatToString
PointerToStringString::HexToString
MemoryToStringString::MemoryToString




4. Beyond Printf Debugging - Use Your Debugger


In the beginning we already talked about starting Cinema 4D from within your IDE and in your debugger. Some may wonder, what's the point, as you obviously have no debug information and symbol tables for Cinema 4D. You know what? It doesn't really matter. You can still debug your plugin, you can step through your code, set breakpoints and watch data structures. You can do basically everything you are used to with your debugger. At a point, where you are calling Cinema 4D API functions, you may feel a bit blindfolded, but you can always use "Step Out" or "Run to Cursor" functionality to get back into your own project code. Discussing every detail of debugging with your specific IDE is way beyond the scope of this article. Still the basics are shown and your attention shall be directed to some nifty features, you may not have noticed, yet. And there's so much more, that won't be covered: Watch points, Symbolic breakpoints. See the Further Reading section, if you feel like diving into the details.

4.1 Visual Studio

thumb
Debugging Rounded Tube SDK Example in VS2013

Above screenshot shows a typical debugging situation (here the rounded tube plugin from the SDK examples). You will want to have several windows docked to your debug layout and you should note, that there's more than just the average breakpoint.

4.1.1 Debug Windows

  • Output
    This is where most of the debug prints write to. When Cinema 4D is starting up, you see a lot of information, not too important for the average plugin developer. But after the start phase it should be quiet here and you can use it nicely for debug outputs and output from assertions. Furthermore output from Breakpoints is shown here as well (see below).
  • Breakpoints
    Useful to quickly toggle or reconfigure breakpoints, without the need to jump to the respective code line. In fact it can be used to quickly navigate to interesting code lines, so you may want to have this window not only in your debug layout. Sort of another category of bookmarks.
  • Locals
    Here you will be provided with the current data visible in the current scope. No more and no less. You can add data to the watch window from here saw well. Changed data is highlighted.
  • Watch Window(s)
    You can have several of these open in parallel. Add data to the watch windows, in order to keep an eye on, even if the variable is not in current scope. Also good to see, if data gets changed by somebody else, like a bug, when you are writing over the limits of an array.
  • Data Probes (not shown in screenshot)
    Actually no real window, but you can attach data probes to source lines via right click "Pin to source". You'll get a minimal watch window attached to the source code, showing the content of a certain variable.
  • Call Stack
    Since you are programming a computer, it is assumed you are familiar with the concept of a call stack. If you are not, then you should definitely learn about it. See further reading links to do so. Anyway, here you see the stack, can click to jump into prior call levels. A good place to come to know the call hierarchy (the basics), to learn how many call levels are involved with certain calls (when optimizing) and certain bugs, for example infinite recursion, are easily identified here.

4.1.2 Breakpoints

  • The Ordinary
    Added with a simple left click into the leftmost column of your editor view, these are behaving as everyone would suspect: The execution gets halted upon hit and you are ready to use your debugger. Via right click you can transform these into any of the following breakpoints.
  • When hit...
    With this option you can change a breakpoint into a debug print. With the big advantage you can add this debug print at runtime! When selecting this option, you can enter the message to print (it's shown in there, how to access your variables). By default the breakpoint will mutate into a debug print. You need to deselect the option to "Continue Execution", if you want halt the execution as well.
  • Hit Count
    A simple version of the conditional breakpoint. Simply specify how many times you want to step over this breakpoint until it finally triggers a halt of the execution.
  • Conditional
    The swiss army knife of all breakpoints. You can specify a condition, in the same way you write the condition for an if statement. In most cases, this is all you need, to trigger on the exact occasion of a bug.

4.2 Xcode

In general Xcode offers the same options as Visual Studio. Therefore it shall be enough to describe the differences.

Unfortunately there does not seem to be comfortable equivalents for Visual Studio's Watch Window and Data Probes. There is an option to watch memory areas, but Visual Studio's Watch Window seems much more versatile.

The screenshot below shows the call stack in Xcode. Note the small buttons in the lower left, which offer a very nice option to show only the relevant part of the stack. Nice feature.

thumb
Viewing Stacktrace in Xcode

In contrast to Visual Studio, Xcode has one breakpoint, which is vastly configurable and can do all the stuff (and more) described for Visual Studio. Lets look at Xcode's breakpoint manager.

thumb
Breakpoints in Xcode

A side note, while we are at it, note the small "All" button in the lower left of the "Locals" view. There you have the option to blend in global variables and even registers. Nice.

Obviously a breakpoint will be configured by Ctrl-clicking or right clicking and selecting Edit Breakpoint.... The screenshot below shows a configuration doing something similar to Visual Studio's "When hit..." breakpoint. Note that you have all the options to combine this with a condition or a "Hit Count" (Visual Studio speak, in Xcode it's the "Ignore" option).

thumb
An equivalent to Visual Studio’s “When hit…” breakpoint




5. Built-in Bugtraps


Assertions are a good way to develop your code into a more stable product. In their simplest form, assertions can be used to trigger a breakpoint in a certain branch. Why would you want to do this? Well, first of all you may have code, that depends on certain prerequisites and/or assumptions. You can use an assertion to assure your assumptions are correct. Or you may not be sure, something is working the way you expect it to, again assertions can be used to notify you about wrong assumptions. And you can use assertions to mark code not yet completed. In this way you don't always have to watch your comments, but will have a notification mechanism, when such incomplete branches are hit.

For example you have an enumeration and a switch/case testing these cases. Typically you will have a default case, which catches everything not covered by your cases. This can be a good place for an assertion. Whenever you extend your enumeration, the debugger will get triggered as soon as a switch/case is hit, that was not yet adapted to the extended enum. Otherwise your plugin might stumble through the default case and might show wrong behavior. Depending on the wrong behavior this can be quite difficult to notice. It may even slip through into your release...

Or consider the following code. Admittedly bad code, but for the sake of demonstration in a few lines... you'll get the point:

// UNSAFE ARRAY ACCESS
#define SOMEARRAY_SIZE 16
static Int32 g_someArray[SOMEARRAY_SIZE];
void PrintSomeArray(Int32 idx)
{
GePrint("Value: " + String::IntToString(g_someArray[idx]));
}

Now you may call PrintSomeArray, forgetting all about your array's size. What will happen? Well, it depends... It depends on parameter idx of course. Large values will probably crash. Which in a weird way is good, as you will notice the bug. Values only slightly greater than SOMEARRAY_SIZE will most likely work, though. With these the function will print some memory content behind your array. This again may be uninitialized or initialized with some of your other data. Now there are chances you'll receive values which look good, but it will depend on sunshine and groundfog, one time your plugin will run, next time it may crash (assuming you are actually working with the data) and on a third run it will show erratic behavior, which may even depend on what you did before with your plugin. The resulting bugs can get even more interesting, if you are writing into the array. Functions will stop working, you didn't even intend to change... you may never have known these ever existed, if you get the wrong values right. ;)

Of course you need to check the index. If the index depends on user input, you'll have no chance but to check idx every time. On the other hand, the index may be depending on your other code, only. Then it might be a good idea to use an assertion like this:

// Better array access (of course you ought to use BaseArray instead)
#define SOMEARRAY_SIZE 16
static Int32 g_someArray[SOMEARRAY_SIZE];
void PrintSomeArray(Int32 idx)
{
DebugAssert((idx >= 0) && (idx < SOMEARRAY_SIZE));
GePrint("Value: " + String::IntToString(g_someArray[idx]));
}

Now, idx gets checked in your debug builds and wrong indices will be obvious. Release builds on the other hand will have no check and will be as fast as you intended.

In the Cinema 4D SDK you have a plethora of assertions to use. On some you can add a string to be printed, when the assertion fails, some are unconditional (e.g. for the above mentioned switch/case) and some also work in release builds. See the table below to get an overview.

Attention
As most of the assertions are active in debug builds only, you MUST NOT put needed code into these. Never do something like this: DebugAssert(a == (b = c))
In a release build b will never be assigned!
Note
For assertions which are active in release builds and output a string, the string will be shown only on the debug console. For a user to be able to see this, he'd need to run his Cinema 4D in Debug mode.
Cinema 4D < R15Cinema 4D ≥ R15StringUnconditionalRelease
GeBoom()CriticalStop(cstr)
GeAssert(cond)DebugAssert(cond)×××
GeBreak()CriticalStop(cstr)
CriticalAssert(cond, cstr)×
DebugStop(cstr)×




6. Be Aware of Floating Point Madness


 Further Reading:
The talk is not about actual debugging here, rather bug prevention when handling Float datatypes and it will be short. While you can read more on the strange stuff, you can experience with Float datatype in the article Floating-Point Notes, here's a short list of things to be aware of and a few SDK functions that will make your life a bit easier. Special care needs to be taken, when
  • comparing Float values
    ⇒ Make use of Bool CompareFloatTolerant(a, b)
  • converting Float variables into integer variables
    ⇒ Use conversion macros SAFEINT32(), SAFEINT64(), SAFEINT()
  • importing floating-point values from files
    ⇒ Use Bool CheckFloat(float) and Float RepairFloat(float)
  • calculating with floating-point numbers, as floating-point exceptions are likely to be disabled in Cinema 4D
    ⇒ You may temporarily enable these (But pay attention! If in doubt have a look at the Floating-Point Notes article) using Int32 GeDebugSetFloatingPointChecks(Int32 on)




7. Keep an Eye on Your Memory Allocations


Allocating memory is trivial, correct freeing or releasing formerly allocated memory, that's what many programmers seem to have problems with. And although Cinema 4D provides a lot of convenience functions, like e.g. the nice AutoAlloc mechanism, this can be as well an issue in your plugins.

Checking for memory leaks the above mentioned Debug mode is irreplaceable. But there's another one you get for free. Included with the SDK examples there's a small plugin called Memory Statistics. It's nothing more, but a table showing the info provided by Bool GeGetMemoryStat(BaseContainer& stat). Yet, it can provide you with a nice feeling for what is going on. Simply dock it in your debug layout. Look at it from time to time... You'll not only know, your plugin is running wild, but your overall feeling for memory allocations will improve.

memory_statistics.png

It was already said, when talking about usage of processor time. Same is true here. Memory you don't use, is free for others. It's a community service to watch your allocations.




8. Multithreading Can Be Tricky


Cinema 4D is heavily multithreaded and this has major impact on your plugin development. While the idea of multithreading may seem obvious and simple, the implications for software developers are not. To explain the details (like correct serialization and proper locking, avoiding deadlocks and so on) would definitely be beyond the scope of this article. In any case you should pay close attention to restrictions in regards to multithreading mentioned in the SDK documentation. We are aware our documentation on this topic is far from perfect. While we are trying to improve, we appreciate any pointers given by the community. Simply send an email to sdk_support@maxon.net. Many functions in the Cinema 4D SDK have to be called from main thread. For example pretty much anything interacting with the GUI. You can use Bool GeIsMainThread(void) to ensure this in your code.




9. Watch Your Execution Times


 Downloads:
To state the obvious: Optimizing your plugin's runtime is a community service! Processing power not used by your plugin is available for others. At first you will have to determine the hotspots of your code. Of course a professional profiling tool makes this job easy. Taking the cost of these tools into account, many of you won't have access to such tools. Well, developers on Mac have a clear advantage here, as they get Instruments for free (someday I'll probably write an article about profiling your plugins with Instruments). But instead you can simply use the debugger or debug outputs to find out, which functions are called the most or which loops have the longest duration. Take special care for functions like GetVirtualObjects() in ObjectData plugins or Message() and Draw() functions in general. Afterwards it's as simple as using GeGetMilliSeconds() to measure your execution time. Depending on the function, it may also be a good idea to keep track of minimum and/or maximum times.

Attention
Don't measure your debug output nor do your measurements in debug builds! This will heavily screw up your measurements. Always use a release build with all debug output turned off.

Here's some example code for Cinema 4D ≥ R15:

// A Header for Debugging Execution Time (det.h)
#ifndef __DET_H_
#define __DET_H_
#include "c4d.h"
// use a distinct define to toggle measurement on and off, as you don't want to measure in debug builds
#define DEBUG_EXECUTION_TIME
// add your measurement jobs here and don't forget the names in the array below
enum
{
DET_SPECIAL_LOOP,
DET_SOME_OTHER_JOB,
_DET_NUM_MEASUREMENTS, // keep last
};
static const char* g_arrDETNames[_DET_NUM_MEASUREMENTS] = // class String can't be used as static global
{
"Special loop",
"Some other job",
};
#ifdef DEBUG_EXECUTION_TIME
void InitDET(void);
void StartDET(const Int32 idxJob);
void FinishDET(const Int32 idxJob);
void PrintDET(const Int32 job);
#define DET_INIT() InitDET() // call this somewhere in your start-up to get proper minimum values later on
#define DET_START(job) StartDET(job)
#define DET_FINISH(job) FinishDET(job)
#define DET_PRINT(job) PrintDET(job)
#else // DEBUG_EXECUTION_TIME
#define DET_INIT()
#define DET_START(job)
#define DET_FINISH(job)
#define DET_PRINT(job)
#endif // DEBUG_EXECUTION_TIME
#endif // __DET_H_
// Implementation of Debugging Execution Time (det.cpp)
#include "det.h"
typedef struct _tDebugExecTime tDebugExecTime;
struct _tDebugExecTime {
Float64 fStart;
Float64 fCurrent;
Float64 fMin;
Float64 fMax;
};
static tDebugExecTime g_arrDET[_DET_NUM_MEASUREMENTS];
void InitDET(void) // call this somewhere in your start-up to get proper minimum values later on
{
for (Int32 idxJob = 0; idxJob < sizeof(g_arrDET)/sizeof(g_arrDET[0]); ++idxJob) {
g_arrDET[idxJob].fMin = MAXVALUE_FLOAT64;
}
}
void StartDET(const Int32 idxJob)
{
g_arrDET[idxJob].fStart = GeGetMilliSeconds();
}
void FinishDET(const Int32 idxJob)
{
const Float64 fTime = GeGetMilliSeconds() - g_arrDET[idxJob].fStart;
g_arrDET[idxJob].fCurrent = fTime;
if (fTime < g_arrDET[idxJob].fMin) g_arrDET[idxJob].fMin = fTime;
if (fTime > g_arrDET[idxJob].fMax) g_arrDET[idxJob].fMax = fTime;
}
#define DET_PRINT_MEASUREMENT(n) GePrint(String(g_arrDETNames[n]) + ": " + \
String::FloatToString(g_arrDET[n].fCurrent) + " / " + \
String::FloatToString(g_arrDET[n].fMin) + " / " + \
String::FloatToString(g_arrDET[n].fMax))
void PrintDET(const Int32 job)
{
if ((job > 0) && (job < _DET_NUM_MEASUREMENTS))
{
DET_PRINT_MEASUREMENT(job);
}
else
{
for (Int32 idxJob = 0; idxJob < sizeof(g_arrDET)/sizeof(g_arrDET[0]); ++idxJob)
{
DET_PRINT_MEASUREMENT(idxJob);
}
}
}

To use the above header, initialize somewhere in your start-up code:

// Initialization
DET_INIT();

Then measure like so:

// Measurement
DET_START(DET_SPECIAL_LOOP);
while (condition) {
// ... this ominous loop you always wanted to know the runtime of
}
DET_FINISH(DET_SPECIAL_LOOP);

And evaluate your results:

// Result
DET_INIT();




10. Active Object Dialog Plugin


You may already know the Active Object Dialog plugin, as it's one of the SDK examples. You will most likely have compiled it yourself, if you may want to do it now. Most people tend to forget about it, after having it called once.

⇒ You rather should not forget about it!

The Active Object Dialog plugin not only gives you a great overview about what's going on in a scene, but it will also provide you with address information, info on object caches and dirty flags, which is great not only for ObjectData plugins. It really goes a long way in combination with your debug information or the debugger.

thumb
Active Object Properties plugin in action