AcademySoftwareFoundation / OpenColorIO

A color management framework for visual effects and animation.
https://opencolorio.org
BSD 3-Clause "New" or "Revised" License
1.74k stars 433 forks source link

Conditional branching in configs? #891

Open dbr opened 4 years ago

dbr commented 4 years ago

We often have transforms which need to vary, say, sequence to sequence.

For example: say most of the show converts to default_log to apply a client grade.. except one sequence which needs another_log.

We currently implement this using the "lut_search_path fallback" pattern. As in, we have a fallback folder containing a fixed filename (usually symlinked to the desired LUT), and create folders for the sequences requiring overrides:

/path/to/luts/pershot-fallback:
    per_shot.lut -> /path/to/default_log.lut
/path/to/luts/pershot-ZZZ:
    per_shot.lut -> /path/to/another_log.lut

then lut_search_path contains something like:

lut_search_path: /path/to/luts/pershot-$SEQ:/path/to/luts/pershot-fallback

So then when the colorspace definition (or look etc) references the pershot.lut:

!<FileTransform> {src: pershot.lut}

..it will either find the LUT in pershot-ZZZ if $SEQ is set to that, and for all others values of $SEQ it will end up at the pershot-fallback folder.

While this approach works, it is far from ideal. Main issues are:

  1. It's convoluted to setup, requiring two search-path entries per LUT which needs switched and at least the fallback LUT created on disk (and since it must have a generic filename like pershot, the config no longer inherently describes what this transform actually does)
  2. The config is somewhat opaque - to someone not familiar with the pattern, it's unclear what is going on.
  3. Switching more complex transforms is not practical. If there is multiple steps in the transform which need to vary, this becomes a mess (separate lut_search_path entries, dummy transforms etc)
  4. Switching transforms not based on files will not work (e.g MatrixTransform, ExponentTransform, ColorSpaceTransform, future things like the possible builtin-transforms), short of baking them to LUT's

One idea which was discussed a long time ago was to essentially take a ColorSpace definition config snippet and write this to a file - kind of like an "OCIO powered mega-LUT" file - then reference this in your config using the fallback pattern. This solves points 3 and 4, but not so much the other two points. It lets you switch multiple transforms, but still convoluted/opaque.

A different way this could be approached is to have a "meta-transform" (along the lines of the GroupTransform). This meta-transform would have a condition (e.g "is the SEQ context variable set to a specific value?"), then a transform to use if the condition is met, and and "else" transform

One possible way of encoding this might be (stealing inspiration from Shotgun API's filter conditions):

    to_reference: !<GroupTransform>
      children:
        - !<ConditionalTransform>
          context_var: SHOT
          operation: in # also things like equals, starts_with, not in etc?
          condition_value: ["abc123", "abc321", "def1212", "fed2323"]
          transform_true:
              !<FileTransform> {src: another_log.lut}
          transform_false:
              !<GroupTransform>
              children:
                - !<FileTransform> {src: default_log.lut}
                - ...

This would encode the psuedocode logic of:

if $SHOT in ('abc123', 'abc321', or ...):
    transform = FileTransform("another_log.lut")
else:
    transform = FileTransform("default_log.lut")

These ConditionalTransforms could be nested inside GroupTransforms or used at top-level of a ColorSpace defintion. It could also support branching from things other than context-vars (potentially things like if it's a CPU or GPU code path, host names, host applications etc etc)

While I'm not 100% happy with the above mockup config syntax, I feel the general approach of encoding these conditional explicitly in the config file could be very valuable. Thoughts?

michdolan commented 4 years ago

The first approach you describe ("OCIO powered mega-LUT") will be mostly supported by CLF/CTF in OCIO v2. You can even use Processor::write() to losslessly write any processor (comprising your transforms) to a file, which could be referenced by another config. It doesn't answer the cleaner logical switch suggestion, but is a step in the right direction.