Skip to content

Commit

Permalink
ACES2 Output Transforms (#1983)
Browse files Browse the repository at this point in the history
* ACES2 BuiltinTransforms

Signed-off-by: Rémi Achard <[email protected]>

* Fix NaNs and add bound checking on CPU gamut table sampling

Signed-off-by: Rémi Achard <[email protected]>

* Use powf instead of pow for consistency

Signed-off-by: Rémi Achard <[email protected]>

* Remove f suffix for floating-point literals in shaders

Signed-off-by: Rémi Achard <[email protected]>

* Address PR feedback around unit tests

Signed-off-by: Rémi Achard <[email protected]>

---------

Signed-off-by: Rémi Achard <[email protected]>
  • Loading branch information
remia authored Sep 25, 2024
1 parent 77e92a9 commit e0666e2
Show file tree
Hide file tree
Showing 27 changed files with 5,248 additions and 103 deletions.
30 changes: 17 additions & 13 deletions include/OpenColorIO/OpenColorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,19 +474,23 @@ enum RangeStyle
/// Enumeration of the :cpp:class:`FixedFunctionTransform` transform algorithms.
enum FixedFunctionStyle
{
FIXED_FUNCTION_ACES_RED_MOD_03 = 0, ///< Red modifier (ACES 0.3/0.7)
FIXED_FUNCTION_ACES_RED_MOD_10, ///< Red modifier (ACES 1.0)
FIXED_FUNCTION_ACES_GLOW_03, ///< Glow function (ACES 0.3/0.7)
FIXED_FUNCTION_ACES_GLOW_10, ///< Glow function (ACES 1.0)
FIXED_FUNCTION_ACES_DARK_TO_DIM_10, ///< Dark to dim surround correction (ACES 1.0)
FIXED_FUNCTION_REC2100_SURROUND, ///< Rec.2100 surround correction (takes one double for the gamma param)
FIXED_FUNCTION_RGB_TO_HSV, ///< Classic RGB to HSV function
FIXED_FUNCTION_XYZ_TO_xyY, ///< CIE XYZ to 1931 xy chromaticity coordinates
FIXED_FUNCTION_XYZ_TO_uvY, ///< CIE XYZ to 1976 u'v' chromaticity coordinates
FIXED_FUNCTION_XYZ_TO_LUV, ///< CIE XYZ to 1976 CIELUV colour space (D65 white)
FIXED_FUNCTION_ACES_GAMUTMAP_02, ///< ACES 0.2 Gamut clamping algorithm -- NOT IMPLEMENTED YET
FIXED_FUNCTION_ACES_GAMUTMAP_07, ///< ACES 0.7 Gamut clamping algorithm -- NOT IMPLEMENTED YET
FIXED_FUNCTION_ACES_GAMUT_COMP_13 ///< ACES 1.3 Parametric Gamut Compression (expects ACEScg values)
FIXED_FUNCTION_ACES_RED_MOD_03 = 0, ///< Red modifier (ACES 0.3/0.7)
FIXED_FUNCTION_ACES_RED_MOD_10, ///< Red modifier (ACES 1.0)
FIXED_FUNCTION_ACES_GLOW_03, ///< Glow function (ACES 0.3/0.7)
FIXED_FUNCTION_ACES_GLOW_10, ///< Glow function (ACES 1.0)
FIXED_FUNCTION_ACES_DARK_TO_DIM_10, ///< Dark to dim surround correction (ACES 1.0)
FIXED_FUNCTION_REC2100_SURROUND, ///< Rec.2100 surround correction (takes one double for the gamma param)
FIXED_FUNCTION_RGB_TO_HSV, ///< Classic RGB to HSV function
FIXED_FUNCTION_XYZ_TO_xyY, ///< CIE XYZ to 1931 xy chromaticity coordinates
FIXED_FUNCTION_XYZ_TO_uvY, ///< CIE XYZ to 1976 u'v' chromaticity coordinates
FIXED_FUNCTION_XYZ_TO_LUV, ///< CIE XYZ to 1976 CIELUV colour space (D65 white)
FIXED_FUNCTION_ACES_GAMUTMAP_02, ///< ACES 0.2 Gamut clamping algorithm -- NOT IMPLEMENTED YET
FIXED_FUNCTION_ACES_GAMUTMAP_07, ///< ACES 0.7 Gamut clamping algorithm -- NOT IMPLEMENTED YET
FIXED_FUNCTION_ACES_GAMUT_COMP_13, ///< ACES 1.3 Parametric Gamut Compression (expects ACEScg values)
FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20, ///< ACES 2.0 Display Rendering -- EXPERIMENTAL
FIXED_FUNCTION_ACES_RGB_TO_JMH_20, ///< ACES 2.0 RGB to JMh -- EXPERIMENTAL
FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20, ///< ACES 2.0 Tonescale and chroma compression -- EXPERIMENTAL
FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20, ///< ACES 2.0 Gamut compression -- EXPERIMENTAL
};

/// Enumeration of the :cpp:class:`ExposureContrastTransform` transform algorithms.
Expand Down
1 change: 1 addition & 0 deletions src/OpenColorIO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ set(SOURCES
ops/exposurecontrast/ExposureContrastOpData.cpp
ops/exposurecontrast/ExposureContrastOpGPU.cpp
ops/exposurecontrast/ExposureContrastOp.cpp
ops/fixedfunction/ACES2/Transform.cpp
ops/fixedfunction/FixedFunctionOpCPU.cpp
ops/fixedfunction/FixedFunctionOpData.cpp
ops/fixedfunction/FixedFunctionOpGPU.cpp
Expand Down
57 changes: 56 additions & 1 deletion src/OpenColorIO/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5233,7 +5233,38 @@ 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(), "CURVE - APPLE_LOG_to_LINEAR")
|| 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")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-300nit-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-500nit-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-1000nit-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-2000nit-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-4000nit-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-500nit-REC2020_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-1000nit-REC2020_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-2000nit-REC2020_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-4000nit-REC2020_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709-D60-in-REC709-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-P3-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-P3-D60-in-XYZ-E_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-108nit-P3-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-300nit-P3-D60-in-XYZ-E_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-500nit-P3-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-1000nit-P3-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-2000nit-P3-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-4000nit-P3-D60-in-P3-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-500nit-P3-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-1000nit-P3-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-2000nit-P3-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-4000nit-P3-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-500nit-REC2020-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-1000nit-REC2020-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-2000nit-REC2020-D60-in-REC2020-D65_2.0")
|| 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-4000nit-REC2020-D60-in-REC2020-D65_2.0") )
)
{
std::ostringstream os;
Expand Down Expand Up @@ -5311,6 +5342,30 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons
throw Exception("Only config version 2.1 (or higher) can have "
"FixedFunctionTransform style 'ACES_GAMUT_COMP_13'.");
}

if (m_majorVersion == 2 && m_minorVersion < 4 && ff->getStyle() == FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20)
{
throw Exception("Only config version 2.4 (or higher) can have "
"FixedFunctionTransform style 'ACES_OUTPUT_TRANSFORM_20'.");
}

if (m_majorVersion == 2 && m_minorVersion < 4 && ff->getStyle() == FIXED_FUNCTION_ACES_RGB_TO_JMH_20)
{
throw Exception("Only config version 2.4 (or higher) can have "
"FixedFunctionTransform style 'ACES_RGB_TO_JMH_20'.");
}

if (m_majorVersion == 2 && m_minorVersion < 4 && ff->getStyle() == FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20)
{
throw Exception("Only config version 2.4 (or higher) can have "
"FixedFunctionTransform style 'ACES_TONESCALE_COMPRESS_20'.");
}

if (m_majorVersion == 2 && m_minorVersion < 4 && ff->getStyle() == FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20)
{
throw Exception("Only config version 2.4 (or higher) can have "
"FixedFunctionTransform style 'ACES_GAMUT_COMPRESS_20'.");
}
}
else if (DynamicPtrCast<const GradingPrimaryTransform>(transform))
{
Expand Down
83 changes: 83 additions & 0 deletions src/OpenColorIO/GpuShaderUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,16 @@ std::string GpuShaderText::intKeywordConst() const
return str;
}

std::string GpuShaderText::intDecl(const std::string & name) const
{
if (name.empty())
{
throw Exception("GPU variable name is empty.");
}

return intKeyword() + " " + name;
}

std::string GpuShaderText::colorDecl(const std::string & name) const
{
if (name.empty())
Expand Down Expand Up @@ -617,6 +627,13 @@ std::string GpuShaderText::float2Keyword() const
return getVecKeyword<2>(m_lang);
}

std::string GpuShaderText::float2Const(const std::string& x, const std::string& y) const
{
std::ostringstream kw;
kw << float2Keyword() << "(" << x << ", " << y << ")";
return kw.str();
}

std::string GpuShaderText::float2Decl(const std::string & name) const
{
if (name.empty())
Expand Down Expand Up @@ -894,6 +911,72 @@ void GpuShaderText::declareUniformArrayInt(const std::string & uniformName, unsi
newLine() << (m_lang == GPU_LANGUAGE_MSL_2_0 ? "" : "uniform ") << intKeyword() << " " << uniformName << "[" << size << "];";
}

// Keep the method private as only float & double types are expected
template<typename T>
std::string matrix3Mul(const T * m3x3, const std::string & vecName, GpuLanguage lang)
{
if (vecName.empty())
{
throw Exception("GPU variable name is empty.");
}

std::ostringstream kw;
switch (lang)
{
case GPU_LANGUAGE_GLSL_1_2:
case GPU_LANGUAGE_GLSL_1_3:
case GPU_LANGUAGE_GLSL_4_0:
case GPU_LANGUAGE_GLSL_ES_1_0:
case GPU_LANGUAGE_GLSL_ES_3_0:
{
// OpenGL shader program requests a transposed matrix
kw << "mat3("
<< getMatrixValues<T, 3>(m3x3, lang, true) << ") * " << vecName;
break;
}
case GPU_LANGUAGE_CG:
{
kw << "mul(half3x3("
<< getMatrixValues<T, 3>(m3x3, lang, false) << "), " << vecName << ")";
break;
}
case GPU_LANGUAGE_HLSL_DX11:
{
kw << "mul(" << vecName
<< ", float3x3(" << getMatrixValues<T, 3>(m3x3, lang, true) << "))";
break;
}
case LANGUAGE_OSL_1:
{
kw << "matrix(" << getMatrixValues<T, 3>(m3x3, lang, false) << ") * " << vecName;
break;
}
case GPU_LANGUAGE_MSL_2_0:
{
kw << "float3x3(" << getMatrixValues<T, 3>(m3x3, lang, true) << ") * " << vecName;
break;
}

default:
{
throw Exception("Unknown GPU shader language.");
}
}
return kw.str();
}

std::string GpuShaderText::mat3fMul(const float * m3x3,
const std::string & vecName) const
{
return matrix3Mul<float>(m3x3, vecName, m_lang);
}

std::string GpuShaderText::mat3fMul(const double * m3x3,
const std::string & vecName) const
{
return matrix3Mul<double>(m3x3, vecName, m_lang);
}

// Keep the method private as only float & double types are expected
template<typename T>
std::string matrix4Mul(const T * m4x4, const std::string & vecName, GpuLanguage lang)
Expand Down
9 changes: 8 additions & 1 deletion src/OpenColorIO/GpuShaderUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class GpuShaderText

std::string intKeyword() const;
std::string intKeywordConst() const;
std::string intDecl(const std::string& name) const;

std::string colorDecl(const std::string& name) const;

Expand Down Expand Up @@ -105,6 +106,8 @@ class GpuShaderText
//

std::string float2Keyword() const;
// Get the string for creating constant vector with three elements
std::string float2Const(const std::string& x, const std::string& y) const;
std::string float2Decl(const std::string& name) const;

//
Expand Down Expand Up @@ -195,7 +198,11 @@ class GpuShaderText
// Matrix multiplication helpers
//

// Get the string for multiplying a 4x4 matrix and a four-element vector
// Get the string for multiplying a 3x3 matrix and a three-elements vector
std::string mat3fMul(const float * m3x3, const std::string & vecName) const;
std::string mat3fMul(const double * m3x3, const std::string & vecName) const;

// Get the string for multiplying a 4x4 matrix and a four-elements vector
std::string mat4fMul(const float * m4x4, const std::string & vecName) const;
std::string mat4fMul(const double * m4x4, const std::string & vecName) const;

Expand Down
27 changes: 26 additions & 1 deletion src/OpenColorIO/OCIOYaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1351,14 +1351,28 @@ inline void load(const YAML::Node& node, FixedFunctionTransformRcPtr& t)
{
std::vector<double> params;
load(iter->second, params);
t->setParams(&params[0], params.size());
if (!params.empty())
{
t->setParams(&params[0], params.size());
}
}
else if(key == "style")
{
std::string style;
load(iter->second, style);
t->setStyle( FixedFunctionStyleFromString(style.c_str()) );
styleFound = true;

const FixedFunctionStyle styleID = t->getStyle();
if (styleID == FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20
|| styleID == FIXED_FUNCTION_ACES_RGB_TO_JMH_20
|| styleID == FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20
|| styleID == FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20)
{
std::ostringstream os;
os << "FixedFunction style is experimental and may be removed in a future release: '" << style << "'.";
LogWarning(os.str());
}
}
else if(key == "direction")
{
Expand Down Expand Up @@ -1393,6 +1407,17 @@ inline void save(YAML::Emitter& out, ConstFixedFunctionTransformRcPtr t)
out << YAML::Key << "style";
out << YAML::Value << YAML::Flow << FixedFunctionStyleToString(t->getStyle());

const FixedFunctionStyle styleID = t->getStyle();
if (styleID == FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20
|| styleID == FIXED_FUNCTION_ACES_RGB_TO_JMH_20
|| styleID == FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20
|| styleID == FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20)
{
std::ostringstream os;
os << "FixedFunction style is experimental and may be removed in a future release: '" << FixedFunctionStyleToString(t->getStyle()) << "'.";
LogWarning(os.str());
}

const size_t numParams = t->getNumParams();
if(numParams>0)
{
Expand Down
Loading

0 comments on commit e0666e2

Please sign in to comment.