Skip to content

Commit

Permalink
Adsk Contrib - Add DCDM displays and improve HLG OOTF implementation (A…
Browse files Browse the repository at this point in the history
…cademySoftwareFoundation#2051)

* Add DCDM displays, improve Rec.2100 surround

Signed-off-by: Doug Walker <[email protected]>

* Fix Linux test

Signed-off-by: Doug Walker <[email protected]>

* Remove 48 nit scaling

Signed-off-by: Doug Walker <[email protected]>

* Remove D60 DCDM built-ins

Signed-off-by: Doug Walker <[email protected]>

* Add version check

Signed-off-by: Doug Walker <[email protected]>

---------

Signed-off-by: Doug Walker <[email protected]>
  • Loading branch information
doug-walker authored Sep 26, 2024
1 parent e0666e2 commit 5c80c0b
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 49 deletions.
2 changes: 2 additions & 0 deletions src/OpenColorIO/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5234,6 +5234,8 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons
if (m_majorVersion == 2 && m_minorVersion < 4
&& ( 0 == Platform::Strcasecmp(blt->getStyle(), "APPLE_LOG_to_ACES2065-1")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "CURVE - APPLE_LOG_to_LINEAR")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_DCDM-D65")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-108nit-P3-D65_2.0")
Expand Down
29 changes: 16 additions & 13 deletions src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class Renderer_REC2100_Surround : public OpCPU

protected:
float m_gamma;
float m_minLum;
};

class Renderer_RGB_TO_HSV : public OpCPU
Expand Down Expand Up @@ -1171,7 +1172,11 @@ Renderer_REC2100_Surround::Renderer_REC2100_Surround(ConstFixedFunctionOpDataRcP
: OpCPU()
{
const auto fwd = FixedFunctionOpData::REC2100_SURROUND_FWD == data->getStyle();
const float gamma = fwd ? (float)data->getParams()[0] : (float)(1. / data->getParams()[0]);
float gamma = (float)data->getParams()[0];

m_minLum = fwd ? 1e-4f : powf(1e-4f, gamma);

gamma = fwd ? gamma : 1.f / gamma;

m_gamma = gamma - 1.f; // compute Y^gamma / Y
}
Expand All @@ -1187,22 +1192,20 @@ void Renderer_REC2100_Surround::apply(const void * inImg, void * outImg, long nu
const float grn = in[1];
const float blu = in[2];

// Calculate luminance assuming input is Rec.2100 RGB.
float Y = 0.2627f * red + 0.6780f * grn + 0.0593f * blu;

// Mirror the function around the origin.
Y = std::abs(Y);

// Since the slope may approach infinity as Y approaches 0, limit the min value
// to avoid gaining up the RGB values (which may not be as close to 0).
//
// This threshold needs to be bigger than 1e-10 (used above) to prevent extreme
// gain in dark colors, yet smaller than 1e-2 to prevent distorting the shape of
// the HLG EOTF curve. Max gain = 1e-4 ** (0.78-1) = 7.6 for HLG min gamma of 0.78.
//
// TODO: Should have forward & reverse versions of this so the threshold can be
// adjusted correctly for the reverse direction.
constexpr float minLum = 1e-4f;
Y = std::max(m_minLum, Y);

// Calculate luminance assuming input is Rec.2100 RGB.
// TODO: Add another parameter to allow using other primaries.
const float Y = std::max( minLum, ( 0.2627f * red +
0.6780f * grn +
0.0593f * blu ) );

// TODO: Currently our fast approx. requires SSE registers.
// Either make this whole routine SSE or make fast scalar pow.
const float Ypow_over_Y = powf(Y, m_gamma);

out[0] = red * Ypow_over_Y;
Expand Down
25 changes: 18 additions & 7 deletions src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1663,15 +1663,24 @@ void Add_Surround_10_Fwd_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShader
ss.newLine() << pxl << ".rgb = " << pxl << ".rgb * Ypow_over_Y;";
}

void Add_Surround_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float gamma)
void Add_Rec2100_Surround_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss,
float gamma, bool isForward)
{
const std::string pxl(shaderCreator->getPixelName());

// TODO: -- add vector inner product to GPUShaderUtils
float minLum = 1e-4f;
if (!isForward)
{
minLum = powf(minLum, gamma);
gamma = 1.f / gamma;
}

ss.newLine() << ss.floatDecl("Y")
<< " = max( 1e-4, 0.2627 * " << pxl << ".rgb.r + "
<< "0.6780 * " << pxl << ".rgb.g + "
<< "0.0593 * " << pxl << ".rgb.b );";
<< " = 0.2627 * " << pxl << ".rgb.r + "
<< "0.6780 * " << pxl << ".rgb.g + "
<< "0.0593 * " << pxl << ".rgb.b;";

ss.newLine() << "Y = max( " << minLum << ", abs(Y) );";

ss.newLine() << ss.floatDecl("Ypow_over_Y") << " = pow( Y, " << (gamma - 1.f) << ");";

Expand Down Expand Up @@ -1965,12 +1974,14 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator,
}
case FixedFunctionOpData::REC2100_SURROUND_FWD:
{
Add_Surround_Shader(shaderCreator, ss, (float) func->getParams()[0]);
Add_Rec2100_Surround_Shader(shaderCreator, ss,
(float) func->getParams()[0], true);
break;
}
case FixedFunctionOpData::REC2100_SURROUND_INV:
{
Add_Surround_Shader(shaderCreator, ss, (float)(1. / func->getParams()[0]));
Add_Rec2100_Surround_Shader(shaderCreator, ss,
(float) func->getParams()[0], false);
break;
}
case FixedFunctionOpData::RGB_TO_HSV:
Expand Down
30 changes: 30 additions & 0 deletions src/OpenColorIO/transforms/builtins/Displays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,25 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept
CIE_XYZ_D65_to_P3_D60_BFD_Functor);
}

{
auto CIE_XYZ_D65_to_DCDM_D65_Functor = [](OpRcPtrVec & ops)
{
const double scale = 48.0 / 52.37;
const double scale4[4] = { scale, scale, scale, 1. };
CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD);

const GammaOpData::Params rgbParams = { 2.6 };
const GammaOpData::Params alphaParams = { 1.0 };
auto gammaData = std::make_shared<GammaOpData>(GammaOpData::BASIC_REV,
rgbParams, rgbParams, rgbParams, alphaParams);
CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD);
};

registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_DCDM-D65",
"Convert CIE XYZ (D65 white) to Gamma 2.6 (D65 white in XYZ-E encoding)",
CIE_XYZ_D65_to_DCDM_D65_Functor);
}

{
auto CIE_XYZ_D65_to_DisplayP3_Functor = [](OpRcPtrVec & ops)
{
Expand Down Expand Up @@ -285,6 +304,17 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept
CIE_XYZ_D65_to_ST2084_P3_D65_Functor);
}

{
auto CIE_XYZ_D65_to_ST2084_DCDM_D65_Functor = [](OpRcPtrVec & ops)
{
ST_2084::GenerateLinearToPQOps(ops);
};

registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65",
"Convert CIE XYZ (D65 white) to ST-2084 (PQ) (D65 white in XYZ-E encoding)",
CIE_XYZ_D65_to_ST2084_DCDM_D65_Functor);
}

{
auto CIE_XYZ_D65_to_REC2100_HLG_1000nit_Functor = [](OpRcPtrVec & ops)
{
Expand Down
97 changes: 68 additions & 29 deletions tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,44 +927,83 @@ OCIO_ADD_TEST(FixedFunctionOpCPU, aces_gamut_map_20)

OCIO_ADD_TEST(FixedFunctionOpCPU, rec2100_surround)
{
const unsigned num_samples = 4;
const unsigned num_samples = 5;

float input_32f[num_samples*4] = {
0.11f, 0.02f, 0.04f, 0.5f,
0.71f, 0.51f, 0.81f, 1.0f,
0.43f, 0.82f, 0.71f, 0.0f,
-1.0f, -0.001f, 1.2f, 0.0f
8.4e-5f, 2.4e-5f, 1.4e-4f, 0.1f,
0.11f, 0.02f, 0.04f, 0.5f,
0.71f, 0.51f, 0.81f, 1.0f,
0.43f, 0.82f, 0.71f, 0.0f,
-1.00f, -0.001f, 1.2f, 0.0f
};
{
OCIO::FixedFunctionOpData::Params params = { 0.78 };

float input2_32f[num_samples * 4];
memcpy(&input2_32f[0], &input_32f[0], sizeof(float)*num_samples * 4);
float output_32f[num_samples * 4];
memcpy(&output_32f[0], &input_32f[0], sizeof(float)*num_samples * 4);

const float expected_32f[num_samples*4] = {
0.21779590f, 0.03959925f, 0.07919850f, 0.5f,
0.80029451f, 0.57485944f, 0.91301214f, 1.0f,
0.46350446f, 0.88389223f, 0.76532131f, 0.0f,
-7.58577776f, -0.00758577f, 9.10293388f, 0.0f,
};
const float expected_32f[num_samples*4] = {
0.000637205163f, 0.000182058618f, 0.001062008605f, 0.1f,
0.21779590f, 0.03959925f, 0.07919850f, 0.5f,
0.80029451f, 0.57485944f, 0.91301214f, 1.0f,
0.46350446f, 0.88389223f, 0.76532131f, 0.0f,
-1.43735918f, -0.00143735918f, 1.72483102f, 0.0f
};

OCIO::FixedFunctionOpData::Params params = { 0.78 };
OCIO::ConstFixedFunctionOpDataRcPtr funcData
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD,
params);
// Forward transform -- input to expected.
OCIO::ConstFixedFunctionOpDataRcPtr funcData
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD,
params);

ApplyFixedFunction(&input_32f[0], &expected_32f[0], num_samples,
funcData,
1e-7f,
__LINE__);
ApplyFixedFunction(&output_32f[0], &expected_32f[0], num_samples,
funcData,
4e-7f,
__LINE__);

OCIO::FixedFunctionOpData::Params params_inv = { 1 / 0.78 };
OCIO::ConstFixedFunctionOpDataRcPtr funcData2
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV,
params_inv);
// Inverse transform -- output back to original.
OCIO::ConstFixedFunctionOpDataRcPtr funcDataInv
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV,
params);

ApplyFixedFunction(&input2_32f[0], &expected_32f[0], num_samples,
funcData2,
1e-7f,
__LINE__);
ApplyFixedFunction(&output_32f[0], &input_32f[0], num_samples,
funcDataInv,
3e-7f,
__LINE__);
}
{
OCIO::FixedFunctionOpData::Params params = { 1.2 };

float output_32f[num_samples * 4];
memcpy(&output_32f[0], &input_32f[0], sizeof(float)*num_samples * 4);

const float expected_32f[num_samples*4] = {
1.331310281667e-05f, 3.803743661907e-06f, 2.218850469446e-05f, 0.1f,
0.059115925805f, 0.010748350146f, 0.021496700293f, 0.5f,
0.636785774786f, 0.457409500198f, 0.726473912080f, 1.0f,
0.401647721515f, 0.765932864285f, 0.663185772735f, 0.0f,
-7.190495367684e-01f, -7.190495367684e-04f, 8.628594441221e-01f, 0.0f
};

// Forward transform -- input to expected.
OCIO::ConstFixedFunctionOpDataRcPtr funcData
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD,
params);

ApplyFixedFunction(&output_32f[0], &expected_32f[0], num_samples,
funcData,
2e-7f,
__LINE__);

// Inverse transform -- output back to original.
OCIO::ConstFixedFunctionOpDataRcPtr funcDataInv
= std::make_shared<OCIO::FixedFunctionOpData>(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV,
params);

ApplyFixedFunction(&output_32f[0], &input_32f[0], num_samples,
funcDataInv,
2e-7f,
__LINE__);
}
}

OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSV)
Expand Down
8 changes: 8 additions & 0 deletions tests/cpu/transforms/BuiltinTransform_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ AllValues UnitTestValues
{ { 0.5f, 0.4f, 0.3f }, { 0.896805202281f, 0.627254277624f, 0.608228132100f } } },
{ "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D60-BFD",
{ { 0.5f, 0.4f, 0.3f }, { 0.892433142142f, 0.627011653770f, 0.608093643982f } } },
{ "DISPLAY - CIE-XYZ-D65_to_DCDM-D65",
{ { 0.5f, 0.4f, 0.3f }, { 0.740738422348f, 0.679816639411f, 0.608609083713f } } },
{ "DISPLAY - CIE-XYZ-D65_to_DisplayP3",
{ { 0.5f, 0.4f, 0.3f }, { 0.882580907776f, 0.581526360743f, 0.5606367050000f } } },

Expand All @@ -549,6 +551,8 @@ AllValues UnitTestValues
{ { 0.5f, 0.4f, 0.3f }, { 0.464008302136f, 0.398157119110f, 0.384828370950f } } },
{ "DISPLAY - CIE-XYZ-D65_to_ST2084-P3-D65",
{ { 0.5f, 0.4f, 0.3f }, { 0.479939091128f, 0.392091860770f, 0.384886051856f } } },
{ "DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65",
{ { 0.5f, 0.4f, 0.3f }, { 0.440281573420f, 0.419284117712f, 0.392876186489f } } },
{ "DISPLAY - CIE-XYZ-D65_to_REC.2100-HLG-1000nit",
{ { 0.5f, 0.4f, 0.3f }, { 0.5649694f, 0.4038837f, 0.3751478f } } }
};
Expand Down Expand Up @@ -589,4 +593,8 @@ OCIO_ADD_TEST(Builtins, validate)
ValidateBuiltinTransform(name, values.first, values.second, __LINE__);
}
}

// The above checks if a test values is missing, but not if there are test values
// that don't have an associated built-in.
OCIO_CHECK_EQUAL(UnitTestValues.size(), reg->getNumBuiltins());
}

0 comments on commit 5c80c0b

Please sign in to comment.