Resolve DCTL for Gamut Rec2020 Transfer Function rec2100-hlg

Hello,
I’m trying to create a DCTL to use as an IDT for footage with the rec2020 gamut and the rec2100-hlg transfer function. I used http://acesidtdctl.tcolorscience.com/ as a starting point, and then used Apps (Colour - Dash) | Colour Science for the Rec2020 → AP0 matrix with the Bradford CAT. For the transfer function, I referenced https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf on page 9 (HLG Reference EOTF).

From all of that, I ended up with the following DCTL code:

// ACES DIT rec2020 rec2100-hlg to ACES(AP0) DCTL

#if (__RESOLVE_VER_MAJOR__ >= 17)
DEFINE_ACES_PARAM(IS_PARAMETRIC_ACES_TRANSFORM: 0)
#endif

__DEVICE__ inline float Log_to_linear(float inv)
{
float outv;
float aspec = 0.17883277f;
float bspec = 1.0f - 4.0f * aspec;
float cspec = 0.5f - aspec * _logf(4.0f * aspec);
if (inv > 0.5f)
{
outv = (_expf((inv - cspec) / aspec) + bspec) / 12.0f;
}
else
{
outv = _powf(inv, 2.0f) / 3.0f;
}
return outv;
}

__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B)
{
const float mtx[9] = {0.6790856347f, 0.1577009146f, 0.1632134507f, 0.0460020031f, 0.8590546730f, 0.0949433240f, -0.0005739432f, 0.0284677684f, 0.9721061748f};

float r1 = Log_to_linear(p_R);
float g1 = Log_to_linear(p_G);
float b1 = Log_to_linear(p_B);
float r2 = r1 * mtx[0] + g1 * mtx[1] + b1 * mtx[2];
float g2 = r1 * mtx[3] + g1 * mtx[4] + b1 * mtx[5];
float b2 = r1 * mtx[6] + g1 * mtx[7] + b1 * mtx[8];

return make_float3(r2, g2, b2);
}

To me, this all looks correct, however when used as an IDT in Resolve, it results in a broken image with lots of hot pixels (I blacked out the faces of the subjects in the below image):
image

Looking for some pointers on how I can get a correct Rec2020 Rec2100-hlg → AP0 DCTL working.

The DCTL seems correct for “by the book” BT.2100 HLG image data. But there are a few things you need to bear in mind.

  1. HLG footage is rarely “by the book” scene-referred data. HLG settings in camera are normally tweaked in a display-referred way to create an image which looks as wanted on an HLG display. It may well have highlight knee baked in to extend the dynamic range, as HLG does not encode that far above mid grey.

  2. The inverse OETF from BT.2100 that you have used will map 38% HLG (BBC recommended mid grey exposure) to a linear value of 0.048, not 0.18, as it maps 0-100% HLG to 0.0-1.0 linear. So adding linear gain of 3.74 at the end of your DCTL will make 38% HLG map to 0.18.

  3. HLG is only strictly defined for positive values, but the output from a camera may well include small sub-black excursions (particularly if the settings have been adjusted for aesthetic preference). On a display those should be clipped off (note the max(0, ...) function in the BT.2100 HLG Reference EOTF). But you may want to do something like mirror the gamma portion of the HLG curve about zero to preserve negative values for grading. Without that you are trying to take the square root of a negative number, which I suspect is what is producing the NaN artefacts in your image.