Vortex Studio SDK: Advanced - Extension Categories and Filtering Options

Vortex® objects (e.g., parts, assemblies, constraints, collision geometries) are represented as dynamics extensions (VxSim::IDynamics, VxSim::IExtension), creating a lot of noise in the VxProfiler that is impossible to filter out, as all dynamics extensions belong to the same VxProfiler category. While some custom-made VxSim::IProfilingDataCollector extensions can provide their own filtering options, VxProfiler can be configured to collect data only from specific categories that must be registered to the VxProfiler prior to usage. However, the product's CSV provider does not provide any custom filtering. The result is a big CSV file that is complicated to filter.

Users can easily filter out Vortex extensions from the dynamics extensions section, so that they can concentrate on specific sections.

Categories

The VxProfiler is capable of filtering out section based on its categories. The categories are described in VxProfilerICD.h.

The main categories are:

  • Application
  • Engine
  • Graphics
  • Network
  • DynamicsExtensions
  • GraphicsExtensions
  • Default

Consider the example starting from the Application Post Update section:

Partial Section Hierarchy Category
ApplicationUpdate.ModulesPostUpdate Application
ApplicationUpdate.ModulesPostUpdate.%ModuleName% (for example, Engine) Same as Parent, i.e. Application
ApplicationUpdate.ModulesPostUpdate.Engine.ExtensionsPostStep Engine
ApplicationUpdate.ModulesPostUpdate.Engine.ExtensionsPostStep.%EXTENSION% DynamicsExtensions
Note
%MODULE NAME% is replaced by an actual registered name, called alternate name. The % syntax is not actually relevant, any section can be renamed if their alternate name are registered.

%EXTENSION% is the name of the extension formatted like this <Factory Key category name>.<Factory Key feature name>.<parent extension's name>.<extension's name>

For example, a Part named MyPart in an assembly named MyAssembly will show up like this : Dynamics.Part.MyAssembly.MyPart. That name is actually registered to the VxProfiler before being used.

The main problem in some past versions is that all extensions are DynamicsExtensions, so it is impossible to select only a specific type of dynamics extensions.

Now, all extensions will register its factory key's category, so rather than DynamicsExtensions, the part will belong to category Dynamics. Likewise, a cable will go under CableSystems, human dynamics under category Human and so on.

New Categories under Engine

The following categories registered by the dynamics module can be filtered by the Profiler:

  • Dynamics (part, assembly, constraint, collision geometries, etc.)
  • Content (e.g., mechanism)
  • Scripting (Python scripts)
  • Cable Systems
  • Earthwork Systems
  • Earthwork Systems Internal
  • Vehicle Systems
  • Vehicle Systems Internal
  • Terrain Systems
  • VxSensors
  • Marine
  • Human
  • Tree
  • Console
  • Debug
  • <Factory Key category> (Any IDynamics extension not provided by Vortex Product)

But by having each extension belong to its own category, a new problem arose. When turning on filtering, only the selected category will show up. But there are now many more categories, so we need to be able to specify what we want to exclude. The API was revised to allow exclusions.

VxProfiler Changes

Enable All

Previously, categories can be enabled via VxProfiler::enableCategory. The enabled categories will be the only ones provided to VxSim::IProfilingDataCollector if filtering is turned on. Otherwise, all categories are outputs.

However, to disable only a few categories, one would need to know all the categories and enable them all. Since it would be nearly impossible to do so, a new API call was added to enable all the categories.

Disabling Categories from Application Configuration

The ProfilerDisabledCategories field allows you to exclude some categories. At the root level, the field will only be applied if ProfilerEnabledCategories is missing or empty. If that is the case, all categories will be enabled and only those in the list will be filtered out.

At the node level, both can be present and will simply add or remove additional categories from the master list. They are applied in order after the master so if a node disables a category and another one after enables it, it will be enabled.

These lists only have an effect if parameter IsProfilerFilteringEnabled is set to true, otherwise everything is captured.

Application Configuration
{
	"ApplicationConfig" : {
	"Extensions" : [...],
	"Modules" : [ ...],
	"Nodes" : [
		...
		"ProfilerDisabledCategories" : [ "Dynamics", "Content", "Terrain Systems" ],
		...
		],
	"Parameters" : {
		"IsProfilerFilteringEnabled" : true,
		"ProfilerDisabledCategories" : [ "Application", "Graphics" ],
		...
		}
	}
}

Extensions Categories

Each extension registers its factory key's category to the VxProfiler when loaded, so there is no code needed to register your category. As each VxScopedProfilingSection needs a VxProfilingSectionInfo, one is also automatically registered with the same extension's factory key's category. The section's name is %EXTENSION NAME%. A section name can be replaced by an alternate name, but alternate names need to be registered, see below.

Note
Every extension has its category with a corresponding VxProfilingSectionInfo registered but at the moment, only the dynamics module creates sub-sections using the extension's category and thus, can be filtered.

It is recommended for a user making their own extension to have a meaningful category in their factory key so that they can filter on it.

Alternate Names (Advanced Use)

When making your own profiling sections, you have to specify which VxProfilingSectionInfo to use in a specific VxScopedProfilingSection. Each VxProfilingSectionInfo must be registered to the VxProfiler before they are used. If you want to use a section associated to an extension, function getSectionInfo can be used to fetch the extension section; you need to provide the section's category and original name.

When creating a VxScopedProfilingSection, an alternate name can also be given so that the section will be properly named when given to a IProfilingDataCollector.

For performance reasons, rather than using a string, each name has an ID and that is the ID that is being carried around the system, so each alternate name must be registered before being used. Alternate names are meant to be able to re-use the same section but give a different context, e.g., a module running all its extensions in a loop, re-using the same section with a different name. There is no filtering on names, only categories.

As for anything with the VxProfiler, registering your section and alternate name should be done before the simulation is actually started and the values should be kept. There is a cost associated to using the Profiler: alternate name ID and section creation involves some computing that should be done outside of simulation.

API Changes References

VxProfiler
class VxProfiler
{
...
	/// Enable/disable the profiling of all categories
	/// @note Only effective if the filtering is enabled
	///
	VXSIMCORE_DLLSPEC static void enableAllCategories(bool enable);

...
	/// Add an alternate name for a given section
	/// @param section Section to add the alternate name Id
	/// @param alternateName alternate name string
	/// @returns alternate name id
	VXSIMCORE_DLLSPEC static size_t registerAlternateName(const VxProfilingSectionInfo* section, const std::string& alternateName);	
 
	/// Remove an alternate name for a given section
	///
	VXSIMCORE_DLLSPEC static void unregisterAlternateName(const VxProfilingSectionInfo* section, size_t alternateNameId);

	/// Retrieve a section information
	/// @param name name of the section when it was registered, not an alternate name
	/// @param category category of the section
	///
	VXSIMCORE_DLLSPEC static const VxProfilingSectionInfo* getSectionInfo(const std::string& name, const std::string& category);

...
}

Example

The following is an example of the API.

Usage Example
...
#include <VxSim/Monitoring/VxProfiler.h>	
#include <VxSim/Monitoring/VxScopedProfilingSection.h>
...

namespace
{
	// Here we register the profiling section we will use, in the given category
	// This example uses its own, but there are predefined categories in VxProfilerICD.h
	//
	// VxProfilingSectionInfo we must be registered in the profiler, it is preferable create them once rather than when needed, since it is going to be the same pointer.
	const VxSim::VxProfilingSectionInfo* kMySection = VxSim::VxProfiler::registerSection("ComputeValues", kProfilingCategory, "Time taken to go compute Value and Other Value, include the settings update");
	const VxSim::VxProfilingSectionInfo* kMySubSection = VxSim::VxProfiler::registerSection("%value%", "", "Time taken to go compute Value only"); //Empty category, will inherit from parent

	// We also register description for the values. This will be useful when reading the profiler's result, but this part is optional
	bool reg =  VxSim::VxProfiler::registerValueDescription(kMySection ,"Computed Value", "Value computed after algotithm") ||
		VxSim::VxProfiler::registerTimestampDescription(kMySection ,"Settings", "Time after settings were fetch");

}
...
MyClass::MyClass()
{
	...

	// Alternate Name must be registered
	mPrecompId = VxProfiler::registerAlternateName(kMySubSection , "Precomputation");
	mCompId = VxProfiler::registerAlternateName(kMySubSection , "computation");

	// Get the extension section (registered automatically when the plugin was loaded) associated with the category
	mSectionPtr = VxProfiler::getSectionInfo("%EXTENSION NAME%", MyExtension->getCreationKey().categoryName);
	if(section)
	{
		mProfilingNameId = VxProfiler::registerAlternateName(mSectionPtr , MyExtension->getName());
	}

}
...
void MyClass::computeValues()
{
	// Create a section with the previously registered VxProfilingSectionInfo
	VxSim::VxScopedProfilingSection section(kMySection);

	updateSettings();
	section.addTimestamp("Settings"); // adds a timestamp to this section, this timestamp "Settings" was documented above

	int val = computeValue();

	section.addValue("Computed Value", val); // adds a value to this section, this value "Computed Value" was documented above

...
	{
		// Using the proper registered secton and registered alternate name. If filtering is turned on an this extension's category is filtered out, it won't show up in the data.
		VxSim::VxScopedProfilingSection section(mSectionPtr , mProfilingNameId );
		MyExtension->updateOutput(x);
	}
	...
}

...
int MyClass::computeValue()
{
	...
	/// Since this section is created after the one above, it become its child.
	/// Since this MySubSection category is empty, the resulting category will be the parent one (kProfilingCategory)
	/// Also using the second parameter so that the section name will show up as nValueName in the data. The name was registered above.
	/// If the second parameter was not used, the section would appear as %value% in the output.

	int x = 0;
	{
		VxSim::VxScopedProfilingSection section(MySubSection, mPrecompId );
		x = 0/0;
	}

	{
		VxSim::VxScopedProfilingSection section(MySubSection, mCompId );
		// Do the computation
		x *= INF/INF;
	}

	return x;
}