Hint Mapping

From K-3D

Jump to: navigation, search

Overview

As described in Pipeline Hints, K-3D relies on "hints" to improve the performance of the Visualization Pipeline by communicating metadata that describes a change along with notification of the change itself. This page describes the process in more detail, particularly support for "mapping" from one hint type to another as hints travel down the pipeline.

First Use Case

To illustrate why hint-mapping is necessary, we will look at a pipeline containing an imaginary "BitmapLuminance" modifier that computes an average luminance value based on an input bitmap:

In this scenario, there are two changes that can take place in the state of bitmap input to the BitmapLuminance modifier:

  • The values (color + alpha channel) of the bitmap's pixels could change. In this case, BitmapLuminance will receive a k3d::hint::bitmap_pixels_changed hint.
  • The bitmap could be resized, which might-or-might-not cause the address of its pixels to change. BitmapLuminance will receive a k3d::hint::bitmap_dimensions_changed hint.

Note that while the hypothetical BitmapLuminance modifier takes a bitmap as input and receives bitmap-related hints when its input changes, it produces a scalar (double) value as output, and there are no hint types defined for scalar values. Thus, a table of input-to-output properties and hint types for BitmapLuminance might look like the following:

Input PropertyInput HintOutput PropertyOutput Hint
input_bitmapk3d::hint::bitmap_pixels_changedoutput_luminance(no hint)
input_bitmapk3d::hint::bitmap_dimensions_changedoutput_luminance(no hint)
input_bitmap(any other hint)output_luminance(no hint)

... note that, no matter the type of hint received (including NULL or "no hint"), the modifier emits "no hint" (NULL) as output.

Second Use Case

Now, consider a more complex example, with another hypothetical modifier, "BitmapBorder":

As before, there are two types of hint that BitmapBorder may receive for its input bitmap, k3d::hint::bitmap_pixels_changed and k3d::hint::bitmap_dimensions_changed, but BitmapBorder has two additional properties that affect its output:

  • One property ("border") that affects the size of the output bitmap (relative to the input bitmap).
  • One property ("color") that affects the content of the output pixels.

This leads to a more complex set of mappings between input and output hints:

Input PropertyInput HintOutput PropertyOutput Hint
input_bitmapk3d::hint::bitmap_pixels_changedoutput_bitmapk3d::hint::bitmap_pixels_changed
input_bitmapk3d::hint::bitmap_dimensions_changedoutput_bitmapk3d::hint::bitmap_dimensions_changed
input_bitmap(any other hint)output_bitmap(no hint)
border(any hint)output_bitmapk3d::hint::bitmap_dimensions_changed
color(any hint)output_bitmapk3d::hint::bitmap_pixels_changed


Requirements

Based on the above, we can identify the following requirements:

  • Hint-mapping is based on sets of input property / input hint / output property / output hint tuples. Because these sets of tuples can grow to become arbitrarily-complex, we need a compact, simple, declarative mechanism for specifying the mapping. This mechanism should make it possible to easily connect input properties to output properties and map hints all-at-once.
  • It must be possible to map between specific hint types.
  • It must be possible to map from special-cases such as "any" hint or "no" hint.
  • It must be possible to map to special-cases such as the "same" hint or "no" hint.
  • Because an arbitrary number of node inputs may change before the node output is recomputed, it is necessary to keep track of what has changed and adjust execution accordingly. In-general, it should be possible to keep-track of an arbitrary number of changes, then execute only as-much-as-necessary. This implies a mechanism for caching a list of hints that have been sent. This mechanism can be integrated into the properties themselves, and provides hooks for writing code that adjusts processing based on which hints are in-effect at the time execution takes place.

Mapping Design

To support hint mapping requirements, we have defined a set of hint "conversion" operators in k3dsdk/hints.h that support connecting properties and mapping their hints at the same time. Typically, this type of operation is performed in the constructor of any plugin that has properties (i.e. the overwhelming majority of them):

// BitmapBorder constructor ...
k3d::hint::connect(input_bitmap.changed_signal(), k3d::hint::converter<
  k3d::hint::convert<k3d::hint::bitmap_dimensions_changed, k3d::hint::unchanged,
  k3d::hint::convert<k3d::hint::bitmap_pixels_changed, k3d::hint::unchanged,
  k3d::hint::convert<k3d::hint::any, k3d::hint::none> > > >(output_bitmap.changed_signal().make_slot()));

k3d::hint::connect(border.changed_signal(), k3d::hint::converter<
  k3d::hint::convert<k3d::hint::any, k3d::hint::bitmap_dimensions_changed> >(output_bitmap.changed_signal().make_slot()));

k3d::hint::connect(color.changed_signal(), k3d::hint::converter<
  k3d::hint::convert<k3d::hint::any, k3d::hint::bitmap_pixels_changed> >(output_bitmap.changed_signal().make_slot()));

Hint Caching Design

The k3d::data::pointer_demand_storage and k3d::data::value_demand_storage policies fully-implement hint-caching. When these classes emit a change notification event they cache a copy of the associated hint. Anytime they need to execute, they pass the set of cached hints to the execution-handler callback so that it can adjust its behavior based on the hints that have been sent. Here is a simplified execution-handler based on the real-life handler from the k3d::bitmap_modifier base-class:

void execute(const std::vector<ihint*>& Hints, bitmap& Bitmap)
{
  bool resize_bitmap = false;
  bool assign_pixels = false;

  for(int i = 0; i != Hints.size(); ++i)
  {
    // Input pixels changed, so all we have to do is reassign ours ...
    if(dynamic_cast<hint::bitmap_pixels_changed*>(Hints[i]))
    {
      assign_pixels = true;
    }
    // In every other case (bitmap_dimensions_changed, unknown hint, or no hint),
    // we must assume the worst and recreate everything from scratch ...
    else
    {
      resize_bitmap = true;
      assign_pixels = true;
      break;
    }
  }

  if(resize_bitmap)
  {
    // Expensive operation here ...
  }

  if(assign_pixels)
  {
    // Lightweight operation here ...
  }
}

Observe how the hint-dispatch logic in this case is carefully crafted so that the resulting behavior is correct for known hints, unknown hints, and no hints. As is the case here, this type of code will often be hidden as an implementation detail in a convenience base-class in the SDK.

Current Status

  • As of this writing, all plugins that derive from k3d::bitmap_modifier, k3d::bitmap_source, k3d::mesh_reader, k3d::mesh_selection_modifier, and k3d::mesh_source have been converted to process hints correctly.
  • Due to the number of mappings involved, many of these plugins have very conservative mappings, e.g: <any> to <none> for all properties.
  • Legacy mesh filters and k3d::mesh_modifier-based modifiers have not been converted.
  • k3d::data::pointer_storage is deprecated and should be removed.
Personal tools