New Features

AxisOp

“Look at” functionality has been added to AxisOp. AxisOp now inherits from the new class LookAt.

AxisOp::local_, the matrix filled by Axis_Knob, has been renamed to localtransform_. AxisOp::local_ now contains the local matrix after look-at has been performed.

The constructor for AxisOp has been updated to take an additional, optional integer argument for the default look-at axis.

AxisOp also overrides the lookat_input() function from LookAt to add the look-at pipe to input 1:

virtual Op* lookat_input() const { return lookAtEnabled() ? (Op*)(Op::input(1)) : 0; }

To enable the look-at pipe, you should override the new virtual function:

virtual bool lookAtEnabled() const { return false; }

When enabling this, you should also make sure to do the following:

  • Add the look-at knobs by calling LookAt::knobs().
  • Call LookAt::knobChanged so that the knobs will be disabled when the look-at pipe is not connected.

Allocators

Three new static global HeapAllocators have been added:

  • gNodeAllocator
  • gOpAllocator
  • gKnobAllocator

This is to facilitate tracking of memory allocations and deallocations for Nodes, Ops and Knobs (currently internal only).

Channel

A new function has been added to compare two channels and determine their order based on purpose, rather than Channel ID:

bool compareChannels(Channel a, Channel b);

For example, a channel with purpose Chan_Red will be ordered before one with purpose Chan_Green. compareChannels will return true when Channel a is ordered before Channel b.

ColorCurveEnum

Two new entries have been added to ColorCurveEnum in ReaderExtensions:

  • eColorCuveGamma18
  • eColorCurveGamma22

FileHandler

The FileHandler class has a new virtual function:

virtual void initDynamicKnobs() {}

This will re-evaluate any dynamic knobs, i.e. those that are created or destroyed in response to a change in settings, for example a change to the file format. The default version does nothing, but it could be used, for example, to update default values based on file parameters.

FileOp

In addition to the StripPrefix function, there is now a new public function:

static const char* StripSuffix(const char* filename, std::string* suffix = 0);

The file_types() function has an additional, optional argument, obsoleteIDs:

static const char* const* file_types(const char* suffix,
                                         const char* altsuffix,
                                         const char** prefixBlacklist,
                                         const char** extras,
                                         const char** obsoleteIDs = NULL);

obsoleteIDs is an (optional) pointer to a NULL-terminated list of strings to extend with obsoleteIDs. This allows file types to have synonyms. The format to use is:

id\t\t\tobsoleteID1,obsoleteID2,...

For example, to make ffmpeg load as mov64, you would pass in:

mov64\t\t\tffmpeg

FileRead

The function getFilename() has been changed from protected to public.

Filter

The filter enum has a new entry, NumFilters.

GeneralTile

GeneralTile has had a few minor updates to improve caching of ImagePlanes.

GPUContext

A new function has been added:

/// Bind a Matrix4 to the named variable.
bool bind(const std::string& name, const Matrix4& mat) const;

ImagePlane

A lock has been added to ImagePlane to ensure that allocation of the underlying data buffer will be thread-safe. The function makeWritable() now locks the ImagePlane and it is safe to call this from multiple threads; the first call will allocate the data buffer and subsequent calls will do nothing. (This is not to say that it’s good practice to call this function more than once, or to call it from multiple threads, but it does provide additional safety when using ImagePlanes in a multi-threaded context.)

A copy constructor and = operator have also been added.

Iop

A new function has been added to return whether or not an Iop is commutative with respect to Transforms:

virtual bool Iop::pass_transform() const;

This function should return true when a Transform that is before this Iop can be moved after it without affecting the result, and false otherwise. For example, this would be true for an operation such as a ColorCorrect, which only modifies the current pixel’s value and the calculated value does not depend on the pixel’s position or the surrounding values.

If the Iop implements gpuEngine(), the value returned by pass_transform() will be used to determine whether or not GPU shader concatenation is possible when this Iop is used in combination with other GPU nodes.

Two other GPU-related virtual functions have been added:

virtual void gpuEngine_useTextureTransform(bool useTransform) {}
virtual void gpuEngine_setupTextureTransform(const Matrix4& postOpTransform) {}

The details of how to implement these are somewhat involved, and might be subject to change. These will be more fully documented in future.

Knob

New Knob flags have been added:

  • NODEGRAPH_ONLY - For knobs that should not be shown in the Timeline.
  • MODIFIES_TIME - Should be set on all knobs which modify timing.
  • TOOLBAR_BUTTON_DRAWSTYLE - For knobs which must be drawn in the style of Viewer toolbar knobs.

A new Enumeration Knob flag has also been added:

  • EXPAND_TO_CONTENTS - Makes Enumeration knobs adjust their width to the size of the largest menu item.

A new class KnobUndoGroup has been added, to encapsulate the add_to_undo() and stop_add_to_undo() operations:

class DDImage_API KnobUndoGroup
 {
   Knob* _knob;

 public:
   KnobUndoGroup(Knob* knob)
   :_knob(knob)
   {
     _knob->add_to_undo();
   }

   ~KnobUndoGroup()
   {
     _knob->stop_add_to_undo();
   }
 };

add_to_undo() will be called automatically when a KnobUndoGroup is constructed for a particular Knob, and stop_add_to_undo() will be called when this KnobUndoGroup goes out of scope.

Additionally, the new and delete operators will be overridden when FN_TRACK_MEMORY_ALLOCATIONS is set to 1 in Memory.h. This is to enable tracking of Knob allocations and deallocations (this tracking information is currently not publicly accessible).

LUT

New entries have been added to the DataTypes enum:

  • GAMMA2_4
  • SLOG2
  • SLOG3
  • CLOG
  • PROTUNE

LookAt

LookAt is a new helper class that contains the look-at functionality previously in TransformGeo. TransformGeo has been updated to use this helper class. It is also used by AxisOp.

Mesh

Mesh now inherits from PolygonPrimitive and provides its own implementation of PolygonPrimitive::tessellateFace().

MetaData

The following string has been added to the MetaData namespace:

static const char* const FRAME = "input/frame";

The following strings have been added to the QuickTime namespace:

static const char* const CODEC_ID               = "quicktime/codec_id";
static const char* const REEL                   = "quicktime/reel";
static const char* const NCLC_PRIMARIES         = "quicktime/nclc_primaries";
static const char* const NCLC_TRANSFER          = "quicktime/nclc_transfer_function";
static const char* const NCLC_MATRIX            = "quicktime/nclc_matrix";
static const char* const QUICKTIME_PREFIX       = "quicktime/";

Op

A new function has been added for access to the root Op for an Op:

Op* rootOp() const;

More detailed performance timing information is now available:

void getPerformanceInfo(OpTimer::Category category, OpTimer::PerformanceInfo& info ) const;

For more informations about the performance timing updates, see OpTimer.

An Op can now be queried for its NodeContext, which will be either eNodeGraph or eTimeline, depending on whether it is in the node graph or timeline environment:

NodeContext nodeContext() const;

OpTimer

New performance profiling categories have been added to provide separate timings for the store, validate, request and engine calls, which all contribute to an Op’s overall processing time:

enum Category {
  eStore,
  eValidate,
  eRequest,
  eEngine
};

The constructor for OpTimer now takes one of these categories in addition to a pointer to the Op to be timed:

OpTimer(DD::Image::Op* op, Category category);

The new PerformanceInfo structure can contain CPU time and call count for the current category, in addition to the “wall time” that was previously available:

struct PerformanceInfo {
  long long _timeTakenCPU;
  long long _timeTakenWall;
  long long _callCount;
  void reset() { _timeTakenCPU = _timeTakenWall = _callCount = 0; }
  PerformanceInfo() : _timeTakenCPU(0), _timeTakenWall(0), _callCount(0) {}
};

“Wall time” is a measure of time as it would be measured by a clock on the wall, i.e. the time that a user would have to wait for the operation being timed to complete. CPU time (accurate on Linux only) does not take into account any time the CPU spends idly, for example time spent waiting on a lock while another thread finishes its work. Both CPU time and wall time are stored as aggregate values over all threads, so for the multi-threaded engine() call, these will generally be much longer than the actual time the user sees. If CPU time is much less than wall time, this can indicate a multi-threading inefficiency in your code, where not all threads are being kept active all of the time, for example because some are being kept waiting on locks.

ParticlesSprite

The helper function tessellateSprite(...) has been added to generate renderable primitives (rTriangles) for a particular instance of a particle sprite and add them into the scene:

bool tessellateSprite(unsigned int spriteIndex,
                      Scene* scene,
                      PrimitiveContext* ptx,  /*! Primitive context */
                      PrimitiveContext* nptx,  /*! Primitive context with local transforms removed,
                                                             to use for adding sprites into the scene */
                      SpriteGenerator* spriteGenerator,
                      MBSceneSpriteGenerator* mbSceneGenerator,
                      ParticleSearchInfo& searchInfo) const;

This contains functionality that was previously hidden inside the tessellate() function and has been exposed in order to allow the tessellation to be done by multiple threads.

The Sprite structure has been moved outside the MBParticleSpriteGenerator so it can be used to store the information required to render a particular sprite instance.

The following helper classes have also been added to facilitate sprite tessellation:

  • SpriteGenerator - generate a particles sprite.
  • MBSceneSpriteGenerator - find a matching particle at another instance in time, for generating motion blur.
  • ParticleSearchInfo - used to store past search information and optimize future searches in the MBSceneSpriteGenerator class.

PlanarIop

A PlanarIop can now choose to render in full planes by overriding the following virtual function to return true:

virtual bool renderFullPlanes() const;

For example, a plugin that generates data such as motion vectors might choose to render in full planes, if the calculated motion channels are not independent of each other. In this case it would usually be more efficient to store all the motion channels even if only one had been requested, to save repeating the same calculation if extra channels are requested later.

Two other functions have been added to support full plane rendering:

// Expand a channel set to the full set of channels in the planes containing the channels passed in,
// and return as a channel set.
DD::Image::ChannelSet expandChannelsToFullPlanes(const DD::Image::ChannelSet& channelSet);

// Expand a channel set to the full set of channels in the planes containing the channels passed in,
// and return as a set of planes.
PlanarI::PlaneSet expandToFullPlanes(ChannelSet channelSet);

Plugin Load

Two new functions have been added to handle loading of plugins, to allow shipped and custom plugins to be loaded separately:

  • getApplicationPluginPath() returns the path of the Nuke plugins directory. This enables shipped plugins to be loaded explicitly.
  • plugin_load_one() loads a single plugin. If this calls plugin_addpath() to add new load paths for plugins, these paths will be inserted at the same indices as they would for a call to plugin_load_all() (this is not the case for plugin_load, which always appends new paths to the end).

Polygon

Now inherits from PolygonPrimitive.

PolygonPrimitive

PolygonPrimitive is a new base class for polygon-based primitives which inherits from Primitive and provides a generic implementation of the tessellate() function, to turn polygon-based primitives into renderable triangles (rTriangles). It adds the virtual function tessellateFace():

virtual void tessellateFace(int faceIndex, Scene* scene, PrimitiveContext* ptx, VertexContext *vtx) const;

This can be overridden to customize the way in which each face of the primitive will be tessellated. The default implementation of PolygonPrimitive::tessellate() will iterate over all faces of the primitive and call tessellateFace() on each one.

Triangle, Mesh, Polygon and PolyMesh classes have been updated to derive from PolygonPrimitive instead of Primitive, and where possible now use the generic tessellate() and tessellateFace() functions from PolygonPrimitive instead of providing their own versions.

PolyMesh

Now inherits from PolygonPrimitive and uses the generic tessellate() function.

Primitive

Primitive now has an additional implementation of vertex_shader(). This reuses an existing VertexContext to avoid the overhead of creating a temporary one if vertex_shader() is being called many times:

virtual void vertex_shader(int v, Scene*, PrimitiveContext*, VertexContext&, VArray& out, const Vector3* normal = NULL) const;

PrimitiveContext

Some changes have been made to PrimitiveContext to enable memory for render primitives to be allocated more efficiently, and to speed up their generation by making it easier to create them in multiple threads.

A render primitive can now be added into a Scene via the PrimitiveContext, rather than directly:

virtual void addToScene(rPrimitive *primitiveToAdd, Scene *scene);

The default version of addToScene() adds primitives directly into the scene, but sub-classes can override this to delay adding the render primitives, for example until all threads which create them have finished their work.

PrimitiveContext now also contains virtual functions for allocating rTriangles:

virtual DD::Image::rTriangle *newRenderTriangle(const GeoInfo * info, const Primitive * p);
virtual DD::Image::rTriangle *newRenderTriangle(const rTriangle &t);

These can be overridden to change the allocator for rTriangles, for example in order to allocate them from a memory pool. Typically, many thousands of rTriangles will be allocated for rendering a scene, so a using a pool allocator can be more efficient than allocating memory for these on demand.

The deleteRenderPrimitive() function has also been added to support alternative memory allocation for render primitives:

virtual void deleteRenderPrimitive(DD::Image::rPrimitive *p);

For example, it could be overridden to free a space in a memory pool on deletion rather than actually deallocating the memory.

RIP

The function destroyRIPContext() has been added to provide safe shutdown of Blink code on exiting Nuke. Do not call this - it will be called by Nuke as appropriate.

Raycast

The Raycast interface has been extended to accept GeometryLists as well as DrawableGeoLists. This allows rays to be collided with custom geometry.

Read

A recursive lock has been added to make_reader(), to prevent multiple threads from accessing this simultaneously.

On Mac OS X only, a function has been added to disable OS file caching for a particular read:

void disable_OSX_caching() { _disable_OSX_caching = true; }

By default, on Mac OS X all files are cached by the OS which can drastically increase the amount of memory being used. However, disabling this caching can severely affect performance for some reads on Mac OS X, so the ability to enable or disable this has been exposed.

Reader

Access to the LUT has been made virtual to allow readers to override it if they need to perform additional processing:

virtual LUT* lut();

An additional virtual function has been added for setting the LUT:

virtual void setLUT(LUT* lut);

Render

The texture_filter_ variable is now of type TextureFilter rather than Filter, to enable the new mip-map filtering options to be used. These can now be enabled on construction of a Render:

Render(Node* node, bool enable_mip_filter =  false);

The initialize() function has been made virtual, to allow sub-classes more control over this part of the processing:

virtual void initialize();

RenderScene

The new mip-map filtering options can be enabled on construction of a RenderScene:

RenderScene(Node * node, bool enable_mip_filter = false);

Scene

The filter_ variable is now of type TextureFilter rather than Filter, to enable the new mip-map filtering options to be used. The accessor functions for filter_ have been updated accordingly:

TextureFilter* filter() const { return filter_; }
void filter(TextureFilter* v) { filter_ = v; }

A new virtual function has been added to allow the list of render primitives stored within a Scene to be cleared, without deleting the render primitives themselves:

virtual void clear_render_primitives() { render_primitives_.clear(); }

This gives users of a Scene more control over how and when render primitives are deleted, which can be useful when multiple threads are working on the same Scene.

TextureFilter

TextureFilter is a new class based on Filter, which adds additional mip-map filter options to the standard filter options. These options are specific to texture-sampling in the context of a 3D render.

The new options are, in order of decreasing speed and increasing accuracy:

  • Nearest - takes the nearest value from the closest mip-map level.
  • Bilinear - takes a bilinearly interpolated value from the closest mip-map level.
  • Trilinear - takes the interpolated values from the two closest mip-map levels and interpolates between them.
  • Anisotropic - uses an elliptical filter area rather than a parallelogram to give the most accurate results.

Thread

Lock and SignalLock now have virtual destructors. These were added to fix a memory leak in code that used them.

TransformGeo

TransformGeo has been updated to use the new helper class LookAt to provide its look-at functionality.

Triangle

Now inherits from PolygonPrimitive and no longer requires its own implementation of tessellate().

VertexContext

A scale function has been added to VArray, the array of floating point numbers inside a VertexContext which will be interpolated across a primitive:

/*! Scale the VArray. */
void scale(float s)
{
  foreach(z, channels) chan[z] *= s;
}

Additionally, a callback has been added to enable the function call used to sample from an input texture to be redefined:

typedef void (*TextureSampler)(Iop* material, const Vector2& center, const Vector2& dU, const Vector2& dV, TextureFilter* f, Pixel& out, void* context);

This was done to enable the new mip-map filtering methods to be used when sampling from a texture.

ViewerCache

The read_from_cache() and write_to_cache() functions have been made virtual:

virtual void read_from_cache(DD::Image::Hash viewer_hash, StartType what);
virtual void write_to_cache(DD::Image::Hash hash);

ViewerContext

A new selection option has been added to allow occlusion testing to be disabled in the 3D Viewer:

enum SelectionOptions3D
{
  eOptionDisableOcclusionTesting = eNumberOfSelectionModes3D
};

Writer

A new virtual function has been added for setting the LUT:

virtual void setLUT(LUT* lut);

This is so that writers can override the default version if they need to perform additional processing.