Library Setup and Driver Paths

class Handle

fluxEngine Handle

This class represents a handle to fluxEngine’s functionality. It is required to perform any operation with fluxEngine.

The default constructor does not actually create an object, the user must use one of the other constructors to actually create and initialize a handle.

Currently only one initialized handle may be created at a time. (This restriction will be relaxed in a future version.)

Each handle is associated with a default processing queue set so that the user need not create a processing queue set themselves. If processing multiple models at the same time is required, the user must create processing queue sets.

Library Setup

Handle() = default

Default constructor.

This does not actually create a handle, but allows the user to create variables that can be used to move a handle around.

inline Handle(void const *licenseData, std::size_t licenseDataSize)

Initialize fluxEngine.

This constructor initializes fluxEngine. Valid license data must be passed to this function, otherwise fluxEngine will not initialize itself. It is up to the user to read the license data from the given license file, if they choose to store it in a file.

If the call is successful a handle will be created. The following example shows a typical usage:

{
    fluxEngine::Handle handle(licenseData, licenseDataSize);
    // do something with handle
}
// handle has fallen out of scope and is destroyed
The resulting handle has move semantics, see the following example:
fluxEngine::Handle handle(licenseData, licenseDataSize);
// do some initial things
someObject.setHandle(std::move(handle));
If an error occurs (for example, because the license was not valid), an exception will be thrown. The following exceptions may be thrown by this constructor:

Parameters
  • licenseData – The raw bytes of the license

  • licenseDataSize – The number of raw bytes of the license

inline Handle(std::vector<std::byte> const &licenseData)

Initialize fluxEngine (convenience wrapper)

If the license data is present as a std::vector<std::byte> this convenience wrapper exists to allow the user to use that to construct a fluxEngine handle.

This wrapper is only available when compiling with a compiler that supports C++17.

See the documentation for the primary constructor for more details.

Parameters

licenseData – The raw bytes of the license

inline Handle(Handle &&other) noexcept

Move constructor.

Parameters

other – The handle to move into the newly created object

inline Handle &operator=(Handle &&other) noexcept

Move assignment operator.

Parameters

other – The handle to move into this object

Returns

A reference to this object

inline ~Handle()

Destructor.

Destroy a library handle, freeing its resources. All background threads will be stopped in the same manner as if stopProcessingThreads() had been called.

Any processing context associated with this handle will be marked as invalid and may hence not be used anymore. However, some memory associated with remaining processing contexts that have not been freed previous to a call to this method may still be in use until each remaining processing context is freed by the user.

inline void createProcessingThreads(int count)

Create processing threads.

fluxEngine may use parallization to speed up processing. In order to achieve this background threads must be created to run on additional CPU cores.

Calling this method is optional: by default processing will be single-threaded.

This method will start count - 1 threads when called, as the thread that asks for processing is always considered to be the first thread (with id 0). For example, if 4 is supplied to count this function will start 3 threads that run in the background. The thread that the user uses to call ProcessingContext::processNext() will be considered the thread with id 0, making processing use a total of 4 threads, which is the value supplied for count.

This method may only be called if there are currently no background threads associated with this handle. Otherwise stopProcessingThreads() must be called first to change the number of threads.

Any processing context that was created before a call to this method was made is marked as invalid and can only be destroyed, but not used anymore.

If an error occurs, an exception will be thrown. The following exceptions may be thrown by this method:

Parameters

count – The number of threads to use for parallel processing (one less than this number will be created by this method, see the description for details)

template<typename T>
inline void createProcessingThreads(int count, T &&initFunction)

Create processing threads (extended version)

Please read the documentation of the primary overload of createProcessingThreads() for a general overview.

This extended method allows the user to supply a thread initialization function that will be called at the beginning of the newly created background threads. This allows the user to customize the thread properties (such as the thread priority or the CPU affinity) themselves.

This function will only return once all thread initialization functions have run.

The thread initialization functions are only called for the backgronud threads that are started by this method; this means that for a count of 4 the initialization function will be called in 3 background threads, and it is up to the user to alter the thread in which they call ProcessingContext::processNext() to process data with fluxEngine.

The threads will be created sequentially, the next thread being created only after the previous thread’s initialization function has completed. This allows the user to directly modify global data structures in the initialization functions without the need for locking.

An example call to this method could be something like this:

handle.createProcessingThreads(4, [&] (int threadId, int threadCount) {
    (void) threadCount;
    // Run thread 1 on core 1, thread 2 on core 2, etc.
    int cpuCoreId = threadId;
    pinCurrentThreadCPUCoreWithId(cpuCoreId);
    setCurrentThreadPriority(ThreadPriority::High);
    // The thread 0 will be the thread that calls
    // processNext() on a processing context, and will have
    // to have these settings applied elsewhere
});
(The functions called within the initialization function are just example names. It would be up to the user to implement them.)

If the user detects an error condition during thread initialization and wants to abort, they should throw an exception in the initialization function they supplied, which will be propagated out of the call to this method.

Important: any attempt to call a method that accesses this handle inside the initialization functions will create a deadlock.

This method may only be called if there are currently no background threads associated with this handle. Otherwise stopProcessingThreads() must be called first to change the number of threads.

Any processing context that was created before a call to this method was made is marked as invalid and can only be destroyed, but not used anymore.

If an error occurs, an exception will be thrown. The following exceptions may be thrown by this method:

Parameters
  • count – The number of threads to use for parallel processing (one less than this number will be created by this method, see the description for details)

  • initFunction – The thread initialization function. This may be a lambda function

inline void stopProcessingThreads() noexcept

Stop all existing processing threads.

This will stop any background threads that are currently associated with a given handle. If processing is currently active on the handle, it will be aborted, as if ProcessingContext::abort() had been called. In that case this method may take a bit of time, as abort operations are not immediate, and this method will wait until the abort has completed.

Any processing context that was created before a call to this method was made is marked as invalid and can only be destroyed, but not used anymore.

This method is always successful: the only errors that could occur when calling this method would be non-recoverable.

This method may safely be called on an invalid handle, that would have no effect.

inline explicit operator bool() const noexcept

Boolean conversion operator.

This allows the user to easily check if a variable of this type currently holds a valid handle. For example:

if (handle) {
    // the handle is valid
}

Driver Paths

These methods of the Handle class allow the user to customize the paths for the driver framework.

inline void setDriverBaseDirectory(std::string const &directory)

Set the driver base directory.

When loading drivers this sets the base directory where the drivers may be found. If this method is not called, or an empty value is passed, the directory drivers one level above the directory of the currently running executable will be used. For example, if the executable is C:\App\bin\engine_test.exe, the default drivers directory would be C:\App\drivers. (This is the case on all platforms.)

Note that if this method is not used the user can also override the default via an environment variable per driver type. (See the introductory documentation for more details.)

The directory specified here must exist, otherwise it will not be used and an error will be raised.

Windows note: this will accept a path in the 8bit local file name encoding, which will not be able to represent all possible Unicode characters that may be used on Windows. Please use the override that accepts a std::wstring instead if possible, in order to supply a wide name.

Parameters

directory – The driver base directory

inline void setDriverBaseDirectory(std::wstring const &directory)

Set the driver base directory (wide/Unicode variant for Windows)

When loading drivers this sets the base directory where the drivers may be found. If this method is not called, or an empty value is passed, the directory drivers one level above the directory of the currently running executable will be used. For example, if the executable is C:\App\bin\engine_test.exe, the default drivers directory would be C:\App\drivers. (This is the case on all platforms.)

Note that if this method is not used the user can also override the default via an environment variable per driver type. (See the introductory documentation for more details.)

The directory specified here must exist, otherwise it will not be used and an error will be raised.

This variant is only available on Windows to specify wide/Unicode paths.

Parameters

directory – The driver base directory

inline void setDriverIsolationExecutable(std::string const &executable)

Set the path to the driver isolation executable.

Drivers are loaded via the fluxDriverIsolation executable (on Windows fluxDriverIsolation.exe), in case the default is not where the executable is deployed. The defaults are:

On Windows and macOS the fluxDriverIsolation executable is assumed to be in the same directory as the current executable by default. For example, if the executable is C:\App\bin\engine_test.exe on Windows, the default driver isolation path is assumed to be C:\App\bin\fluxDriverIsolation.exe. Similarly, on macOS, if the main executable is in /Applications/engine_test.app/Contents/MacOS/engine_test, the driver isolation executable is assumed to be in /Applications/engine_test.app/Contents/MacOS/fluxDriverIsolation.

On all other platforms (Linux) the executable is assumed to be in ../libexec/fluxDriverIsolation relative to the current executable path. For example, if the executable is in /opt/engine_test/bin/engine_test, the driver isolation executable will be looked for in /opt/engine_test/libexec/fluxDriverIsolation by default.

Windows note: this will accept a path in the 8bit local file name encoding, which will not be able to represent all possible Unicode characters that may be used on Windows. Please use the override that accepts a std::wstring instead if possible, in order to supply a wide name.

Parameters

executable – The path to the fluxDriverIsolation executable

inline void setDriverIsolationExecutable(std::wstring const &executable)

Set the path to the driver isolation executable (wide/Unicode variant for Windows)

Drivers are loaded via the fluxDriverIsolation executable (on Windows fluxDriverIsolation.exe), in case the default is not where the executable is deployed. The defaults are:

On Windows and macOS the fluxDriverIsolation executable is assumed to be in the same directory as the current executable by default. For example, if the executable is C:\App\bin\engine_test.exe on Windows, the default driver isolation path is assumed to be C:\App\bin\fluxDriverIsolation.exe. Similarly, on macOS, if the main executable is in /Applications/engine_test.app/Contents/MacOS/engine_test, the driver isolation executable is assumed to be in /Applications/engine_test.app/Contents/MacOS/fluxDriverIsolation.

On all other platforms (Linux) the executable is assumed to be in ../libexec/fluxDriverIsolation relative to the current executable path. For example, if the executable is in /opt/engine_test/bin/engine_test, the driver isolation executable will be looked for in /opt/engine_test/libexec/fluxDriverIsolation by default.

This variant is only available on Windows to specify wide/Unicode paths.

Parameters

executable – The path to the fluxDriverIsolation executable

Processing Plugins

These methods of the Handle class allow the user to interact with processing plugins.

inline std::size_t processingPluginCount() const

Get the number of processing plugins that were loaded by fluxEngine.

During startup fluxEngine will automatically load any processing plugin it can find in a path adjacent to where the fluxEngine DLLs are installed. Any plugin that is found is attempted to be loaded.

Plugins may provide additional functionality to fluxEngine, or they may provide implementations of algorithms that are optimized for specific devices, for example specific CPU instruction sets.

Returns

The number of processing plugins that were loaded

inline std::string processingPluginId(std::size_t index) const

Get the id of a processing plugin.

For a given processing plugin identified by index, which must run from 0 to one less than the count returned by processingPluginCount(), return the id of that plugin.

The id will be a UUID that is encoded as a string.

Parameters

index – The index of the plugin, must be between 0 and one less than the total number of plugins. The index will remain stable for the lifetime of the fluxEngine handle

Returns

The id of the processing plugin

inline std::string processingPluginName(std::size_t index) const

Get the name of a processing plugin.

For a given processing plugin identified by index, which must run from 0 to one less than the count returned by processingPluginCount(), return a human-readable name of that plugin.

Parameters

index – The index of the plugin, must be between 0 and one less than the total number of plugins. The index will remain stable for the lifetime of the fluxEngine handle

Returns

The name of the processing plugin

inline bool processingPluginIsAvailable(std::size_t index) const

Check if a processing plugin is available.

For a given processing plugin identified by index, which must run from 0 to one less than the count returned by processingPluginCount(), determine if the plugin is available on this system.

A plugin may not be available on the current system due to an incompatibility. For example, a plugin that provides an AVX2 implementation will not be available on a CPU that doesn’t support AVX2, or on an operating system that doesn’t support it, even if the CPU does.

Parameters

index – The index of the plugin, must be between 0 and one less than the total number of plugins. The index will remain stable for the lifetime of the fluxEngine handle

Returns

Whether the processing plugin is available.

inline bool processingPluginIsEnabled(std::size_t index) const

Check if a processing plugin is enabled.

For a given processing plugin identified by index, which must run from 0 to one less than the count returned by processingPluginCount(), determine if the plugin is currently enabled.

All plugins are enabled by default, but if a plugin is not available (e.g. a plugin that provides an AVX2 implementation is not available on a CPU that doesn’t support AVX2), the fact that the plugin is enabled will have no effect. See processingPluginIsAvailable() to check if a plugin is available.

Plugins are only disabled if they are explicitly disabled by the user.

Parameters

index – The index of the plugin, must be between 0 and one less than the total number of plugins. The index will remain stable for the lifetime of the fluxEngine handle

Returns

Whether the processing plugin is enabled.

inline void processingPluginSetEnabled(std::size_t index, bool enabled)

Enable or disable a processing plugin.

For a given processing plugin identified by index, which must run from 0 to one less than the count returned by processingPluginCount(), enable or disable it.

All plugins are enabled by default, but if a plugin is available (e.g. a plugin that provides an AVX2 implementation is not available on a CPU that doesn’t support AVX2), the fact that the plugin is enabled will have no effect. See processingPluginIsAvailable() to check if a plugin is available.

Plugins are only disabled if they are explicitly disabled by the user by this method.

If processing contexts have already been created a call to this function will have no effect on the already existing contexts, it will only affect newly created contexts. (The old contexts will still work though.)

Parameters
  • index – The index of the plugin, must be between 0 and one less than the total number of plugins. The index will remain stable for the lifetime of the fluxEngine handle

  • enabled – Whether the processing plugin should be enabled or not

inline std::size_t processingPluginIndexById(std::string const &id)

Find a processing plugin based on its id.

Attempt to determine the index of a processing plugin that has a specific id that was specified by the user.

If the plugin could not be found, an exception will be thrown.

Parameters

id – The id of the plugin, must be parseable as a UUID.

Returns

The index of the plugin, which may be used to address the plugin using the other plugin related methods.

Processing Queue Sets

class ProcessingQueueSet

fluxEngine Processing Queue Set

This class represents a processing queue set. A processing queue set is used for processing queues.

The default constructor does not actually create an object, the user must use one of the other constructors to actually create and initialize a processing queue set.

Each processing queue set is associated with a number of processing threads that the user can manage. A processing queue set may only be used to process a single model at the same time, but multiple processing queue sets may be created.

Public Functions

ProcessingQueueSet() = default

Default constructor.

This does not actually create a processing queue set, but allows the user to create variables that can be used to move a handle around.

inline ProcessingQueueSet(Handle &handle)

Create a new processing queue set for a given handle.

This constructor creates a new processing queue set. The following example shows a typical usage:

{
    fluxEngine::ProcessingQueueSet pqs(handle);
    // do something with processing queue set
}
// handle has fallen out of scope and is destroyed
The resulting processing queue set has move semantics, see the following example:
fluxEngine::ProcessingQueueSet pqs(handle);
// do some initial things
someObject.setProcessingQueueSet(std::move(pqs));
If an error occurs, an exception will be thrown. The following exceptions may be thrown by this constructor:

  • std::bad_alloc

  • std::invalid_argument

  • Error

Parameters

handle – A reference to the fluxEngine handle

inline ProcessingQueueSet(ProcessingQueueSet &&other) noexcept

Move constructor.

Parameters

other – The processing queue set to move into the newly created object

inline ProcessingQueueSet &operator=(ProcessingQueueSet &&other) noexcept

Move assignment operator.

Parameters

other – The processing queue set to move into this object

Returns

A reference to this object

inline ~ProcessingQueueSet()

Destructor.

Destroy a processing queue set, freeing its resources. All background threads will be stopped in the same manner as if stopProcessingThreads() had been called.

Any processing context associated with this processing queue set will be marked as invalid and may hence not be used anymore. However, some memory associated with remaining processing contexts that have not been freed previous to a call to this method may still be in use until each remaining processing context is freed by the user.

inline void createProcessingThreads(int count)

Create processing threads.

fluxEngine may use parallization to speed up processing. In order to achieve this background threads must be created to run on additional CPU cores.

Calling this method is optional: by default processing will be single-threaded.

This method will start count - 1 threads when called, as the thread that asks for processing is always considered to be the first thread (with id 0). For example, if 4 is supplied to count this function will start 3 threads that run in the background. The thread that the user uses to call ProcessingContext::processNext() will be considered the thread with id 0, making processing use a total of 4 threads, which is the value supplied for count.

This method may only be called if there are currently no background threads associated with this processing queue set. Otherwise stopProcessingThreads() must be called first to change the number of threads.

Any processing context associated with this processing queue set that was created before a call to this method was made is marked as invalid and can only be destroyed, but not used anymore.

If an error occurs, an exception will be thrown. The following exceptions may be thrown by this method:

Parameters

count – The number of threads to use for parallel processing (one less than this number will be created by this method, see the description for details)

template<typename T>
inline void createProcessingThreads(int count, T &&initFunction)

Create processing threads (extended version)

Please read the documentation of the primary overload of createProcessingThreads() for a general overview.

This extended method allows the user to supply a thread initialization function that will be called at the beginning of the newly created background threads. This allows the user to customize the thread properties (such as the thread priority or the CPU affinity) themselves.

This function will only return once all thread initialization functions have run.

The thread initialization functions are only called for the backgronud threads that are started by this method; this means that for a count of 4 the initialization function will be called in 3 background threads, and it is up to the user to alter the thread in which they call ProcessingContext::processNext() to process data with fluxEngine.

The threads will be created sequentially, the next thread being created only after the previous thread’s initialization function has completed. This allows the user to directly modify global data structures in the initialization functions without the need for locking.

An example call to this method could be something like this:

handle.createProcessingThreads(4, [&] (int threadId, int threadCount) {
    (void) threadCount;
    // Run thread 1 on core 1, thread 2 on core 2, etc.
    int cpuCoreId = threadId;
    pinCurrentThreadCPUCoreWithId(cpuCoreId);
    setCurrentThreadPriority(ThreadPriority::High);
    // The thread 0 will be the thread that calls
    // processNext() on a processing context, and will have
    // to have these settings applied elsewhere
});
(The functions called within the initialization function are just example names. It would be up to the user to implement them.)

If the user detects an error condition during thread initialization and wants to abort, they should throw an exception in the initialization function they supplied, which will be propagated out of the call to this method.

Important: any attempt to call a method that accesses this handle inside the initialization functions will create a deadlock.

This method may only be called if there are currently no background threads associated with this processing queue set. Otherwise stopProcessingThreads() must be called first to change the number of threads.

Any processing context associated with this processing queue set that was created before a call to this method was made is marked as invalid and can only be destroyed, but not used anymore.

If an error occurs, an exception will be thrown. The following exceptions may be thrown by this method:

Parameters
  • count – The number of threads to use for parallel processing (one less than this number will be created by this method, see the description for details)

  • initFunction – The thread initialization function. This may be a lambda function

inline void stopProcessingThreads() noexcept

Stop all existing processing threads.

This will stop any background threads that are currently associated with a given processing queue set. If processing is currently active on the processing queue set, it will be aborted, as if ProcessingContext::abort() had been called. In that case this method may take a bit of time, as abort operations are not immediate, and this method will wait until the abort has completed.

Any processing context associated with this processing queue set that was created before a call to this method was made is marked as invalid and can only be destroyed, but not used anymore.

This method is always successful: the only errors that could occur when calling this method would be non-recoverable.

This method may safely be called on an invalid processing queue set, that would have no effect.

inline explicit operator bool() const noexcept

Boolean conversion operator.

This allows the user to easily check if a variable of this type currently holds a valid processing queue set. For example:

if (processingQueueSet) {
    // the processing queue set is valid
}