From 804e90b42d728ecb1073af8d0bae15a91b13a469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 11 Feb 2020 15:18:55 +0100 Subject: DRW: Color Management improvement Reviewed By: brecht sergey jbakker Differential Revision: http://developer.blender.org/D6729 --- intern/opencolorio/fallback_impl.cc | 43 +- .../opencolorio/gpu_shader_display_transform.glsl | 183 ++--- intern/opencolorio/ocio_capi.cc | 51 +- intern/opencolorio/ocio_capi.h | 17 +- intern/opencolorio/ocio_impl.cc | 45 ++ intern/opencolorio/ocio_impl.h | 46 +- intern/opencolorio/ocio_impl_glsl.cc | 819 +++++++++++++-------- 7 files changed, 780 insertions(+), 424 deletions(-) (limited to 'intern/opencolorio') diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc index ec63dccf147..8dc95d22233 100644 --- a/intern/opencolorio/fallback_impl.cc +++ b/intern/opencolorio/fallback_impl.cc @@ -594,6 +594,43 @@ void FallbackImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) MEM_freeN(id); } +OCIO_GroupTransformRcPtr *FallbackImpl::createGroupTransform(void) +{ + FallbackTransform *transform = new FallbackTransform(); + transform->type = TRANSFORM_UNKNOWN; + return (OCIO_GroupTransformRcPtr *)transform; +} + +void FallbackImpl::groupTransformSetDirection(OCIO_GroupTransformRcPtr * /*gt*/, + const bool /*forward */) +{ +} + +void FallbackImpl::groupTransformPushBack(OCIO_GroupTransformRcPtr * /*gt*/, + OCIO_ConstTransformRcPtr * /*transform*/) +{ +} + +void FallbackImpl::groupTransformRelease(OCIO_GroupTransformRcPtr * /*gt*/) +{ +} + +OCIO_ColorSpaceTransformRcPtr *FallbackImpl::createColorSpaceTransform(void) +{ + FallbackTransform *transform = new FallbackTransform(); + transform->type = TRANSFORM_UNKNOWN; + return (OCIO_ColorSpaceTransformRcPtr *)transform; +} + +void FallbackImpl::colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr * /*ct*/, + const char * /*name*/) +{ +} + +void FallbackImpl::colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr * /*ct*/) +{ +} + OCIO_ExponentTransformRcPtr *FallbackImpl::createExponentTransform(void) { FallbackTransform *transform = new FallbackTransform(); @@ -658,10 +695,12 @@ bool FallbackImpl::supportGLSLDraw(void) } bool FallbackImpl::setupGLSLDraw(struct OCIO_GLSLDrawState ** /*state_r*/, - OCIO_ConstProcessorRcPtr * /*processor*/, + OCIO_ConstProcessorRcPtr * /*ocio_processor_scene_to_ui*/, + OCIO_ConstProcessorRcPtr * /*ocio_processor_ui_to_display*/, OCIO_CurveMappingSettings * /*curve_mapping_settings*/, float /*dither*/, - bool /*predivide*/) + bool /*predivide*/, + bool /*overlay*/) { return false; } diff --git a/intern/opencolorio/gpu_shader_display_transform.glsl b/intern/opencolorio/gpu_shader_display_transform.glsl index 9787398e2ae..f1f77cf7df2 100644 --- a/intern/opencolorio/gpu_shader_display_transform.glsl +++ b/intern/opencolorio/gpu_shader_display_transform.glsl @@ -1,70 +1,68 @@ +/* Blender OpenColorIO implementation */ + +uniform sampler1D curve_mapping_texture; uniform sampler2D image_texture; +uniform sampler2D overlay_texture; uniform sampler3D lut3d_texture; +uniform sampler3D lut3d_display_texture; -#ifdef USE_DITHER uniform float dither; -#endif - -in vec2 texCoord_interp; -out vec4 fragColor; +uniform bool predivide; +uniform bool curve_mapping; +uniform bool overlay; -#ifdef USE_CURVE_MAPPING -/* Curve mapping parameters - * - * See documentation for OCIO_CurveMappingSettings to get fields descriptions. - * (this ones pretyt much copies stuff from C structure.) - */ -uniform sampler1D curve_mapping_texture; -uniform int curve_mapping_lut_size; -uniform int use_curve_mapping_extend_extrapolate; -uniform vec4 curve_mapping_mintable; -uniform vec4 curve_mapping_range; -uniform vec4 curve_mapping_ext_in_x; -uniform vec4 curve_mapping_ext_in_y; -uniform vec4 curve_mapping_ext_out_x; -uniform vec4 curve_mapping_ext_out_y; -uniform vec4 curve_mapping_first_x; -uniform vec4 curve_mapping_first_y; -uniform vec4 curve_mapping_last_x; -uniform vec4 curve_mapping_last_y; -uniform vec3 curve_mapping_black; -uniform vec3 curve_mapping_bwmul; +layout(std140) uniform OCIO_GLSLCurveMappingParameters +{ + /* Curve mapping parameters + * + * See documentation for OCIO_CurveMappingSettings to get fields descriptions. + * (this ones pretty much copies stuff from C structure.) + */ + vec4 curve_mapping_mintable; + vec4 curve_mapping_range; + vec4 curve_mapping_ext_in_x; + vec4 curve_mapping_ext_in_y; + vec4 curve_mapping_ext_out_x; + vec4 curve_mapping_ext_out_y; + vec4 curve_mapping_first_x; + vec4 curve_mapping_first_y; + vec4 curve_mapping_last_x; + vec4 curve_mapping_last_y; + vec4 curve_mapping_black; + vec4 curve_mapping_bwmul; + int curve_mapping_lut_size; + int curve_mapping_use_extend_extrapolate; +}; float read_curve_mapping(int table, int index) { - /* TODO(sergey): Without -1 here image is getting darken after applying unite curve. - * But is it actually correct to subtract 1 here? - */ - float texture_index = float(index) / float(curve_mapping_lut_size - 1); - return texture(curve_mapping_texture, texture_index)[table]; + return texelFetch(curve_mapping_texture, index, 0)[table]; } float curvemap_calc_extend(int table, float x, vec2 first, vec2 last) { if (x <= first[0]) { - if (use_curve_mapping_extend_extrapolate == 0) { + if (curve_mapping_use_extend_extrapolate == 0) { /* horizontal extrapolation */ return first[1]; } else { - if (curve_mapping_ext_in_x[table] == 0.0) - return first[1] + curve_mapping_ext_in_y[table] * 10000.0; - else - return first[1] + - curve_mapping_ext_in_y[table] * (x - first[0]) / curve_mapping_ext_in_x[table]; + float fac = (curve_mapping_ext_in_x[table] != 0.0) ? + ((x - first[0]) / curve_mapping_ext_in_x[table]) : + 10000.0; + return first[1] + curve_mapping_ext_in_y[table] * fac; } } else if (x >= last[0]) { - if (use_curve_mapping_extend_extrapolate == 0) { + if (curve_mapping_use_extend_extrapolate == 0) { /* horizontal extrapolation */ return last[1]; } else { - if (curve_mapping_ext_out_x[table] == 0.0) - return last[1] - curve_mapping_ext_out_y[table] * 10000.0; - else - return last[1] + - curve_mapping_ext_out_y[table] * (x - last[0]) / curve_mapping_ext_out_x[table]; + float fac = (curve_mapping_ext_out_x[table] != 0.0) ? + ((x - last[0]) / curve_mapping_ext_out_x[table]) : + -10000.0; + return last[1] + curve_mapping_ext_out_y[table] * fac; } } return 0.0; @@ -92,80 +90,91 @@ float curvemap_evaluateF(int table, float value) vec2(curve_mapping_last_x[table], curve_mapping_last_y[table])); } else { - if (i < 0) + if (i < 0) { return read_curve_mapping(table, 0); - if (i >= CM_TABLE) + } + if (i >= CM_TABLE) { return read_curve_mapping(table, CM_TABLE); - + } fi = fi - float(i); - return (1.0 - fi) * read_curve_mapping(table, i) + fi * read_curve_mapping(table, i + 1); + float cm1 = read_curve_mapping(table, i); + float cm2 = read_curve_mapping(table, i + 1); + return mix(cm1, cm2, fi); } } vec4 curvemapping_evaluate_premulRGBF(vec4 col) { - vec4 result = col; - result[0] = curvemap_evaluateF(0, (col[0] - curve_mapping_black[0]) * curve_mapping_bwmul[0]); - result[1] = curvemap_evaluateF(1, (col[1] - curve_mapping_black[1]) * curve_mapping_bwmul[1]); - result[2] = curvemap_evaluateF(2, (col[2] - curve_mapping_black[2]) * curve_mapping_bwmul[2]); - result[3] = col[3]; + col.rgb = (col.rgb - curve_mapping_black.rgb) * curve_mapping_bwmul.rgb; + + vec4 result; + result.r = curvemap_evaluateF(0, col.r); + result.g = curvemap_evaluateF(1, col.g); + result.b = curvemap_evaluateF(2, col.b); + result.a = col.a; return result; } -#endif -#ifdef USE_DITHER float dither_random_value(vec2 co) { return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453) * 0.005 * dither; } -vec2 round_to_pixel(vec2 st) +vec2 round_to_pixel(sampler2D tex, vec2 uv) { - vec2 result; - vec2 size = textureSize(image_texture, 0); - result.x = float(int(st.x * size.x)) / size.x; - result.y = float(int(st.y * size.y)) / size.y; - return result; + vec2 size = textureSize(tex, 0); + return vec2(ivec2(uv * size)) / size; } -vec4 apply_dither(vec2 st, vec4 col) +vec4 apply_dither(vec4 col, vec2 uv) { - vec4 result; - float random_value = dither_random_value(round_to_pixel(st)); - result.r = col.r + random_value; - result.g = col.g + random_value; - result.b = col.b + random_value; - result.a = col.a; - return result; + col.rgb += dither_random_value(uv); + return col; } -#endif -void main() +vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv) { - vec4 col = texture(image_texture, texCoord_interp.st); -#ifdef USE_CURVE_MAPPING - col = curvemapping_evaluate_premulRGBF(col); -#endif - -#ifdef USE_PREDIVIDE - if (col[3] > 0.0 && col[3] < 1.0) { - float inv_alpha = 1.0 / col[3]; - col[0] *= inv_alpha; - col[1] *= inv_alpha; - col[2] *= inv_alpha; + if (curve_mapping) { + col = curvemapping_evaluate_premulRGBF(col); + } + + if (predivide) { + if (col.a > 0.0 && col.a < 1.0) { + col.rgb *= 1.0 / col.a; + } } -#endif /* NOTE: This is true we only do de-premul here and NO premul * and the reason is simple -- opengl is always configured * for straight alpha at this moment */ - vec4 result = OCIODisplay(col, lut3d_texture); + col = OCIO_to_display_linear_with_look(col, lut3d_texture); + + if (dither > 0.0) { + col = apply_dither(col, noise_uv); + } + + if (overlay) { + col *= 1.0 - col_overlay.a; + col += col_overlay; /* Assumed unassociated alpha. */ + } -#ifdef USE_DITHER - result = apply_dither(texCoord_interp.st, result); -#endif + col = OCIO_to_display_encoded(col, lut3d_display_texture); - fragColor = result; + return col; } + +/* ------------------------------------------------------------------------ */ + +in vec2 texCoord_interp; +out vec4 fragColor; + +void main() +{ + vec4 col = texture(image_texture, texCoord_interp.st); + vec4 col_overlay = texture(overlay_texture, texCoord_interp.st); + vec2 noise_uv = round_to_pixel(image_texture, texCoord_interp.st); + + fragColor = OCIO_ProcessColor(col, col_overlay, noise_uv); +} \ No newline at end of file diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc index d259ba73e45..84c36de364c 100644 --- a/intern/opencolorio/ocio_capi.cc +++ b/intern/opencolorio/ocio_capi.cc @@ -307,6 +307,41 @@ void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) impl->OCIO_PackedImageDescRelease(id); } +OCIO_GroupTransformRcPtr *OCIO_createGroupTransform(void) +{ + return impl->createGroupTransform(); +} + +void OCIO_groupTransformSetDirection(OCIO_GroupTransformRcPtr *gt, const bool forward) +{ + impl->groupTransformSetDirection(gt, forward); +} + +void OCIO_groupTransformPushBack(OCIO_GroupTransformRcPtr *gt, OCIO_ConstTransformRcPtr *tr) +{ + impl->groupTransformPushBack(gt, tr); +} + +void OCIO_groupTransformRelease(OCIO_GroupTransformRcPtr *gt) +{ + impl->groupTransformRelease(gt); +} + +OCIO_ColorSpaceTransformRcPtr *OCIO_createColorSpaceTransform(void) +{ + return impl->createColorSpaceTransform(); +} + +void OCIO_colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr *ct, const char *name) +{ + impl->colorSpaceTransformSetSrc(ct, name); +} + +void OCIO_colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr *ct) +{ + impl->colorSpaceTransformRelease(ct); +} + OCIO_ExponentTransformRcPtr *OCIO_createExponentTransform(void) { return impl->createExponentTransform(); @@ -350,12 +385,20 @@ int OCIO_supportGLSLDraw(void) } int OCIO_setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *processor, + OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, + OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, OCIO_CurveMappingSettings *curve_mapping_settings, float dither, - bool predivide) -{ - return (int)impl->setupGLSLDraw(state_r, processor, curve_mapping_settings, dither, predivide); + bool predivide, + bool overlay) +{ + return (int)impl->setupGLSLDraw(state_r, + ocio_processor_scene_to_ui, + ocio_processor_ui_to_display, + curve_mapping_settings, + dither, + predivide, + overlay); } void OCIO_finishGLSLDraw(struct OCIO_GLSLDrawState *state) diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h index 5670b37f892..57799222788 100644 --- a/intern/opencolorio/ocio_capi.h +++ b/intern/opencolorio/ocio_capi.h @@ -46,8 +46,10 @@ OCIO_DECLARE_HANDLE(OCIO_ConstContextRcPtr); OCIO_DECLARE_HANDLE(OCIO_PackedImageDesc); OCIO_DECLARE_HANDLE(OCIO_DisplayTransformRcPtr); OCIO_DECLARE_HANDLE(OCIO_ConstTransformRcPtr); +OCIO_DECLARE_HANDLE(OCIO_ColorSpaceTransformRcPtr); OCIO_DECLARE_HANDLE(OCIO_ExponentTransformRcPtr); OCIO_DECLARE_HANDLE(OCIO_MatrixTransformRcPtr); +OCIO_DECLARE_HANDLE(OCIO_GroupTransformRcPtr); OCIO_DECLARE_HANDLE(OCIO_ConstLookRcPtr); /* Standard XYZ to linear sRGB transform, for fallback. */ @@ -198,6 +200,15 @@ OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *p); +OCIO_GroupTransformRcPtr *OCIO_createGroupTransform(void); +void OCIO_groupTransformSetDirection(OCIO_GroupTransformRcPtr *gt, const bool forward); +void OCIO_groupTransformPushBack(OCIO_GroupTransformRcPtr *gt, OCIO_ConstTransformRcPtr *tr); +void OCIO_groupTransformRelease(OCIO_GroupTransformRcPtr *gt); + +OCIO_ColorSpaceTransformRcPtr *OCIO_createColorSpaceTransform(void); +void OCIO_colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr *ct, const char *name); +void OCIO_colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr *ct); + OCIO_ExponentTransformRcPtr *OCIO_createExponentTransform(void); void OCIO_exponentTransformSetValue(OCIO_ExponentTransformRcPtr *et, const float *exponent); void OCIO_exponentTransformRelease(OCIO_ExponentTransformRcPtr *et); @@ -212,10 +223,12 @@ void OCIO_matrixTransformScale(float *m44, float *offset4, const float *scale4); int OCIO_supportGLSLDraw(void); int OCIO_setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *processor, + OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, + OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, OCIO_CurveMappingSettings *curve_mapping_settings, float dither, - bool predivide); + bool predivide, + bool overlay); void OCIO_finishGLSLDraw(struct OCIO_GLSLDrawState *state); void OCIO_freeOGLState(struct OCIO_GLSLDrawState *state); diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc index b838f0e979f..fd749bb4050 100644 --- a/intern/opencolorio/ocio_impl.cc +++ b/intern/opencolorio/ocio_impl.cc @@ -822,6 +822,51 @@ void OCIOImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) OBJECT_GUARDED_DELETE((PackedImageDesc *)id, PackedImageDesc); } +OCIO_GroupTransformRcPtr *OCIOImpl::createGroupTransform(void) +{ + GroupTransformRcPtr *gt = OBJECT_GUARDED_NEW(GroupTransformRcPtr); + + *gt = GroupTransform::Create(); + + return (OCIO_GroupTransformRcPtr *)gt; +} + +void OCIOImpl::groupTransformSetDirection(OCIO_GroupTransformRcPtr *gt, const bool forward) +{ + TransformDirection dir = forward ? TRANSFORM_DIR_FORWARD : TRANSFORM_DIR_INVERSE; + (*(GroupTransformRcPtr *)gt)->setDirection(dir); +} + +void OCIOImpl::groupTransformPushBack(OCIO_GroupTransformRcPtr *gt, OCIO_ConstTransformRcPtr *tr) +{ + (*(GroupTransformRcPtr *)gt)->push_back(*(ConstTransformRcPtr *)tr); +} + +void OCIOImpl::groupTransformRelease(OCIO_GroupTransformRcPtr *gt) +{ + OBJECT_GUARDED_DELETE((GroupTransformRcPtr *)gt, GroupTransformRcPtr); +} + +OCIO_ColorSpaceTransformRcPtr *OCIOImpl::createColorSpaceTransform(void) +{ + ColorSpaceTransformRcPtr *ct = OBJECT_GUARDED_NEW(ColorSpaceTransformRcPtr); + + *ct = ColorSpaceTransform::Create(); + (*ct)->setDirection(TRANSFORM_DIR_FORWARD); + + return (OCIO_ColorSpaceTransformRcPtr *)ct; +} + +void OCIOImpl::colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr *ct, const char *name) +{ + (*(ColorSpaceTransformRcPtr *)ct)->setSrc(name); +} + +void OCIOImpl::colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr *ct) +{ + OBJECT_GUARDED_DELETE((ColorSpaceTransformRcPtr *)ct, ColorSpaceTransformRcPtr); +} + OCIO_ExponentTransformRcPtr *OCIOImpl::createExponentTransform(void) { ExponentTransformRcPtr *et = OBJECT_GUARDED_NEW(ExponentTransformRcPtr); diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h index 082aa4a091e..3ffc0a4a475 100644 --- a/intern/opencolorio/ocio_impl.h +++ b/intern/opencolorio/ocio_impl.h @@ -117,6 +117,16 @@ class IOCIOImpl { virtual void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *p) = 0; + virtual OCIO_GroupTransformRcPtr *createGroupTransform(void) = 0; + virtual void groupTransformSetDirection(OCIO_GroupTransformRcPtr *gt, const bool forward) = 0; + virtual void groupTransformPushBack(OCIO_GroupTransformRcPtr *gt, + OCIO_ConstTransformRcPtr *transform) = 0; + virtual void groupTransformRelease(OCIO_GroupTransformRcPtr *gt) = 0; + + virtual OCIO_ColorSpaceTransformRcPtr *createColorSpaceTransform(void) = 0; + virtual void colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr *ct, const char *name) = 0; + virtual void colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr *ct) = 0; + virtual OCIO_ExponentTransformRcPtr *createExponentTransform(void) = 0; virtual void exponentTransformSetValue(OCIO_ExponentTransformRcPtr *et, const float *exponent) = 0; @@ -132,10 +142,12 @@ class IOCIOImpl { virtual bool supportGLSLDraw(void) = 0; virtual bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *processor, + OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, + OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, OCIO_CurveMappingSettings *curve_mapping_settings, float dither, - bool predivide) = 0; + bool predivide, + bool overlay) = 0; virtual void finishGLSLDraw(struct OCIO_GLSLDrawState *state) = 0; virtual void freeGLState(struct OCIO_GLSLDrawState *state_r) = 0; @@ -229,6 +241,15 @@ class FallbackImpl : public IOCIOImpl { void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *p); + OCIO_GroupTransformRcPtr *createGroupTransform(void); + void groupTransformSetDirection(OCIO_GroupTransformRcPtr *gt, const bool forward); + void groupTransformPushBack(OCIO_GroupTransformRcPtr *gt, OCIO_ConstTransformRcPtr *transform); + void groupTransformRelease(OCIO_GroupTransformRcPtr *gt); + + OCIO_ColorSpaceTransformRcPtr *createColorSpaceTransform(void); + void colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr *ct, const char *name); + void colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr *ct); + OCIO_ExponentTransformRcPtr *createExponentTransform(void); void exponentTransformSetValue(OCIO_ExponentTransformRcPtr *et, const float *exponent); void exponentTransformRelease(OCIO_ExponentTransformRcPtr *et); @@ -243,10 +264,12 @@ class FallbackImpl : public IOCIOImpl { bool supportGLSLDraw(void); bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *processor, + OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, + OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, OCIO_CurveMappingSettings *curve_mapping_settings, float dither, - bool predivide); + bool predivide, + bool overlay); void finishGLSLDraw(struct OCIO_GLSLDrawState *state); void freeGLState(struct OCIO_GLSLDrawState *state_r); @@ -339,6 +362,15 @@ class OCIOImpl : public IOCIOImpl { void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *p); + OCIO_GroupTransformRcPtr *createGroupTransform(void); + void groupTransformSetDirection(OCIO_GroupTransformRcPtr *gt, const bool forward); + void groupTransformPushBack(OCIO_GroupTransformRcPtr *gt, OCIO_ConstTransformRcPtr *transform); + void groupTransformRelease(OCIO_GroupTransformRcPtr *gt); + + OCIO_ColorSpaceTransformRcPtr *createColorSpaceTransform(void); + void colorSpaceTransformSetSrc(OCIO_ColorSpaceTransformRcPtr *ct, const char *name); + void colorSpaceTransformRelease(OCIO_ColorSpaceTransformRcPtr *ct); + OCIO_ExponentTransformRcPtr *createExponentTransform(void); void exponentTransformSetValue(OCIO_ExponentTransformRcPtr *et, const float *exponent); void exponentTransformRelease(OCIO_ExponentTransformRcPtr *et); @@ -353,10 +385,12 @@ class OCIOImpl : public IOCIOImpl { bool supportGLSLDraw(void); bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *processor, + OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, + OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, OCIO_CurveMappingSettings *curve_mapping_settings, float dither, - bool predivide); + bool predivide, + bool overlay); void finishGLSLDraw(struct OCIO_GLSLDrawState *state); void freeGLState(struct OCIO_GLSLDrawState *state_r); diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index a80e29a2dec..df6adc8f34b 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -56,62 +56,108 @@ using namespace OCIO_NAMESPACE; #include "ocio_impl.h" static const int LUT3D_EDGE_SIZE = 64; +static const int LUT3D_TEXTURE_SIZE = sizeof(float) * 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * + LUT3D_EDGE_SIZE; static const int SHADER_CACHE_SIZE = 4; +#define UBO_BIND_LOC 0 + extern "C" char datatoc_gpu_shader_display_transform_glsl[]; extern "C" char datatoc_gpu_shader_display_transform_vertex_glsl[]; /* **** OpenGL drawing routines using GLSL for color space transform ***** */ -typedef struct OCIO_GLSLShader { - /* Cache ID */ - std::string lut3dCacheID; - std::string shaderCacheID; - - /* LUT */ - bool lut3d_texture_allocated; /* boolean flag indicating whether - * lut texture is allocated - */ - bool lut3d_texture_valid; - - GLuint lut3d_texture; /* OGL texture ID for 3D LUT */ - - float *lut3d; /* 3D LUT table */ - - /* Dither */ - bool use_dither; - - /* Curve Mapping */ - bool use_curve_mapping; - bool curve_mapping_texture_allocated; - bool curve_mapping_texture_valid; - GLuint curve_mapping_texture; - size_t curve_mapping_cache_id; - - /* Alpha Predivide */ - bool use_predivide; - - /* GLSL stuff */ - GLuint ocio_shader; - GLuint vert_shader; +/* Curve mapping parameters + * + * See documentation for OCIO_CurveMappingSettings to get fields descriptions. + * (this ones pretty much copies stuff from C structure.) + */ +struct OCIO_GLSLCurveMappingParameters { + float curve_mapping_mintable[4]; + float curve_mapping_range[4]; + float curve_mapping_ext_in_x[4]; + float curve_mapping_ext_in_y[4]; + float curve_mapping_ext_out_x[4]; + float curve_mapping_ext_out_y[4]; + float curve_mapping_first_x[4]; + float curve_mapping_first_y[4]; + float curve_mapping_last_x[4]; + float curve_mapping_last_y[4]; + float curve_mapping_black[4]; + float curve_mapping_bwmul[4]; + int curve_mapping_lut_size; + int curve_mapping_use_extend_extrapolate; + int _pad[2]; + /** WARNING: Needs to be 16byte aligned. Used as UBO data. */ +}; + +struct OCIO_GLSLShader { + /** Cache IDs */ + std::string cacheId; + /** TODO(fclem): Remove. IMM shader interface. */ + struct GPUShaderInterface *interface; + /** OpenGL Shader objects handles. */ + GLuint frag; + GLuint vert; GLuint program; - GPUShaderInterface *shader_interface; -} GLSLDrawState; - -typedef struct OCIO_GLSLDrawState { + /** Uniform locations. */ + GLint dither_loc; + GLint overlay_loc; + GLint overlay_tex_loc; + GLint predivide_loc; + GLint curve_mapping_loc; + /** Error checking. */ + bool valid; +}; + +struct OCIO_GLSLLut3d { + /** Cache IDs */ + std::string cacheId; + /** OpenGL Texture handles. 0 if not allocated. */ + GLuint texture; + GLuint texture_display; + /** Error checking. */ + bool valid; +}; + +struct OCIO_GLSLCurveMappping { + /** Cache IDs */ + size_t cacheId; + /** OpenGL Uniform Buffer handle. 0 if not allocated. */ + GLuint buffer; + /** OpenGL Texture handles. 0 if not allocated. */ + GLuint texture; + /** Error checking. */ + bool valid; +}; + +struct OCIO_GLSLCacheHandle { + size_t cache_id; + void *data; +}; + +struct OCIO_GLSLDrawState { /* Shader Cache */ - OCIO_GLSLShader *shader_cache[SHADER_CACHE_SIZE]; + OCIO_GLSLCacheHandle shader_cache[SHADER_CACHE_SIZE]; + OCIO_GLSLCacheHandle lut3d_cache[SHADER_CACHE_SIZE]; + OCIO_GLSLCacheHandle curvemap_cache[SHADER_CACHE_SIZE]; +}; + +static OCIO_GLSLDrawState *allocateOpenGLState(void) +{ + return (OCIO_GLSLDrawState *)MEM_callocN(sizeof(OCIO_GLSLDrawState), "OCIO OpenGL State struct"); +} - /* Previous OpenGL state. */ - GLint last_texture, last_texture_unit; -} OCIO_GLSLDrawState; +/* -------------------------------------------------------------------- */ +/** \name Shader + * \{ */ -static GLuint compileShaderText(GLenum shaderType, const char *text) +static GLuint compileShaderText(GLenum shader_type, const char *text) { GLuint shader; GLint stat; - shader = glCreateShader(shaderType); + shader = glCreateShader(shader_type); glShaderSource(shader, 1, (const GLchar **)&text, NULL); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &stat); @@ -127,15 +173,16 @@ static GLuint compileShaderText(GLenum shaderType, const char *text) return shader; } -static GLuint linkShaders(GLuint ocio_shader, GLuint vert_shader) +static GLuint linkShaders(GLuint frag, GLuint vert) { - if (!ocio_shader || !vert_shader) + if (!frag || !vert) { return 0; + } GLuint program = glCreateProgram(); - glAttachShader(program, ocio_shader); - glAttachShader(program, vert_shader); + glAttachShader(program, frag); + glAttachShader(program, vert); glLinkProgram(program); @@ -155,133 +202,379 @@ static GLuint linkShaders(GLuint ocio_shader, GLuint vert_shader) return program; } -static OCIO_GLSLDrawState *allocateOpenGLState(void) +static void updateGLSLShader(OCIO_GLSLShader *shader, + ConstProcessorRcPtr *processor_scene_to_ui, + ConstProcessorRcPtr *processpr_ui_to_display, + GpuShaderDesc *shader_desc, + const std::string &cache_id) { - return (OCIO_GLSLDrawState *)MEM_callocN(sizeof(OCIO_GLSLDrawState), "OCIO OpenGL State struct"); + if (shader->cacheId == cache_id) { + return; + } + + /* Delete any previous shader. */ + glDeleteProgram(shader->program); + glDeleteShader(shader->frag); + glDeleteShader(shader->vert); + + if (shader->interface) { + GPU_shaderinterface_discard(shader->interface); + } + + { + /* Vertex shader */ + std::ostringstream osv; + + osv << "#version 330\n"; + osv << datatoc_gpu_shader_display_transform_vertex_glsl; + + shader->vert = compileShaderText(GL_VERTEX_SHADER, osv.str().c_str()); + } + { + /* Fragment shader */ + std::ostringstream os; + + os << "#version 330\n"; + /* Work around OpenColorIO not supporting latest GLSL yet. */ + os << "#define texture2D texture\n"; + os << "#define texture3D texture\n"; + + shader_desc->setFunctionName("OCIO_to_display_linear_with_look"); + os << (*processor_scene_to_ui)->getGpuShaderText(*shader_desc) << "\n"; + + shader_desc->setFunctionName("OCIO_to_display_encoded"); + os << (*processpr_ui_to_display)->getGpuShaderText(*shader_desc) << "\n"; + + os << datatoc_gpu_shader_display_transform_glsl; + + shader->frag = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str()); + } + + /* shader_Program */ + if (shader->frag && shader->vert) { + shader->program = linkShaders(shader->frag, shader->vert); + } + + if (shader->program) { + shader->dither_loc = glGetUniformLocation(shader->program, "dither"); + shader->overlay_tex_loc = glGetUniformLocation(shader->program, "overlay_texture"); + shader->overlay_loc = glGetUniformLocation(shader->program, "overlay"); + shader->predivide_loc = glGetUniformLocation(shader->program, "predivide"); + shader->curve_mapping_loc = glGetUniformLocation(shader->program, "curve_mapping"); + + glUseProgram(shader->program); + /* Set texture bind point uniform once. This is saved by the shader. */ + glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0); + glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2); + glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3); + glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4); + + /* Set UBO binding location. */ + GLuint index = glGetUniformBlockIndex(shader->program, "OCIO_GLSLCurveMappingParameters"); + glUniformBlockBinding(shader->program, index, UBO_BIND_LOC); + + /* TODO(fclem) Remove this. Make caller always assume viewport space and + * specify texco via vertex attribs. */ + shader->interface = GPU_shaderinterface_create(shader->program); + } + + shader->cacheId = cache_id; + shader->valid = (shader->program != 0); } -/* Ensure LUT texture and array are allocated */ -static bool ensureLUT3DAllocated(OCIO_GLSLShader *shader) +static void ensureGLSLShader(OCIO_GLSLShader **shader_ptr, + ConstProcessorRcPtr *processor_scene_to_ui, + ConstProcessorRcPtr *processpr_ui_to_display, + GpuShaderDesc *shader_desc, + const std::string &cache_id) { - int num_3d_entries = 3 * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE * LUT3D_EDGE_SIZE; + if (*shader_ptr != NULL) { + return; + } - if (shader->lut3d_texture_allocated) - return shader->lut3d_texture_valid; + OCIO_GLSLShader *shader = OBJECT_GUARDED_NEW(OCIO_GLSLShader); - glGenTextures(1, &shader->lut3d_texture); + updateGLSLShader(shader, processor_scene_to_ui, processpr_ui_to_display, shader_desc, cache_id); - shader->lut3d = (float *)MEM_callocN(sizeof(float) * num_3d_entries, "OCIO GPU 3D LUT"); + *shader_ptr = shader; +} - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_3D, shader->lut3d_texture); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +static void freeGLSLShader(OCIO_GLSLShader *shader) +{ + glDeleteProgram(shader->program); + glDeleteShader(shader->frag); + glDeleteShader(shader->vert); - /* clean glError buffer */ - while (glGetError() != GL_NO_ERROR) { + if (shader->interface) { + GPU_shaderinterface_discard(shader->interface); } - glTexImage3D(GL_TEXTURE_3D, - 0, - GL_RGB16F_ARB, - LUT3D_EDGE_SIZE, - LUT3D_EDGE_SIZE, - LUT3D_EDGE_SIZE, - 0, - GL_RGB, - GL_FLOAT, - shader->lut3d); - - shader->lut3d_texture_allocated = true; - - /* GL_RGB16F_ARB could be not supported at some drivers - * in this case we could not use GLSL display - */ - shader->lut3d_texture_valid = glGetError() == GL_NO_ERROR; - - return shader->lut3d_texture_valid; + OBJECT_GUARDED_DELETE(shader, OCIO_GLSLShader); } -static bool ensureCurveMappingAllocated(OCIO_GLSLShader *shader, - OCIO_CurveMappingSettings *curve_mapping_settings) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lut3D + * \{ */ + +static void updateGLSLLut3d(OCIO_GLSLLut3d *lut3d, + ConstProcessorRcPtr *processor_scene_to_ui, + ConstProcessorRcPtr *processpr_ui_to_display, + GpuShaderDesc *shader_desc, + const std::string &cache_id) { - if (shader->curve_mapping_texture_allocated) - return shader->curve_mapping_texture_valid; + if (lut3d->cacheId == cache_id) + return; + + float *lut_data = (float *)MEM_mallocN(LUT3D_TEXTURE_SIZE, __func__); + + ConstProcessorRcPtr *ocio_processors[2] = {processor_scene_to_ui, processpr_ui_to_display}; - glGenTextures(1, &shader->curve_mapping_texture); + for (int i = 0; i < 2; i++) { + ConstProcessorRcPtr *processor = ocio_processors[i]; + GLuint texture = (&lut3d->texture)[i]; + + (*processor)->getGpuLut3D(lut_data, *shader_desc); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_3D, texture); + + glTexSubImage3D(GL_TEXTURE_3D, + 0, + 0, + 0, + 0, + LUT3D_EDGE_SIZE, + LUT3D_EDGE_SIZE, + LUT3D_EDGE_SIZE, + GL_RGB, + GL_FLOAT, + lut_data); + } + + MEM_freeN(lut_data); + + lut3d->cacheId = cache_id; +} + +static void ensureGLSLLut3d(OCIO_GLSLLut3d **lut3d_ptr, + ConstProcessorRcPtr *processor_scene_to_ui, + ConstProcessorRcPtr *processpr_ui_to_display, + GpuShaderDesc *shaderDesc, + const std::string &cache_id) +{ + if (*lut3d_ptr != NULL) { + return; + } + + OCIO_GLSLLut3d *lut3d = OBJECT_GUARDED_NEW(OCIO_GLSLLut3d); + + glGenTextures(3, &lut3d->texture); + + for (int i = 0; i < 2; i++) { + GLuint texture = (&lut3d->texture)[i]; + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_3D, texture); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + glTexImage3D(GL_TEXTURE_3D, + 0, + GL_RGB16F, + LUT3D_EDGE_SIZE, + LUT3D_EDGE_SIZE, + LUT3D_EDGE_SIZE, + 0, + GL_RGB, + GL_FLOAT, + NULL); + } + + updateGLSLLut3d(lut3d, processor_scene_to_ui, processpr_ui_to_display, shaderDesc, cache_id); + + lut3d->valid = (lut3d->texture != 0); + + *lut3d_ptr = lut3d; +} + +static void freeGLSLLut3d(OCIO_GLSLLut3d *lut3d) +{ + glDeleteTextures(1, &lut3d->texture); + + OBJECT_GUARDED_DELETE(lut3d, OCIO_GLSLLut3d); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Mapping + * \{ */ +static void allocateCurveMappingTexture(OCIO_GLSLCurveMappping *curvemap, + OCIO_CurveMappingSettings *curve_mapping_settings) +{ + glGenTextures(1, &curvemap->texture); glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, shader->curve_mapping_texture); + glBindTexture(GL_TEXTURE_1D, curvemap->texture); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - /* clean glError buffer */ - while (glGetError() != GL_NO_ERROR) { + /* Do not initialize. Only if used. */ + int lut_size = curve_mapping_settings ? curve_mapping_settings->lut_size : 1; + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F, lut_size, 0, GL_RGBA, GL_FLOAT, NULL); +} + +/* curve_mapping_settings can be null. In this case we alloc a dummy curvemap. */ +static void ensureGLSLCurveMapping(OCIO_GLSLCurveMappping **curvemap_ptr, + OCIO_CurveMappingSettings *curve_mapping_settings) +{ + if (*curvemap_ptr != NULL) { + return; } - glTexImage1D(GL_TEXTURE_1D, - 0, - GL_RGBA16F, - curve_mapping_settings->lut_size, - 0, - GL_RGBA, - GL_FLOAT, - curve_mapping_settings->lut); + OCIO_GLSLCurveMappping *curvemap = OBJECT_GUARDED_NEW(OCIO_GLSLCurveMappping); + + /* Texture. */ + allocateCurveMappingTexture(curvemap, curve_mapping_settings); - shader->curve_mapping_texture_allocated = true; + /* Uniform buffer object. */ + glGenBuffers(1, &curvemap->buffer); + glBindBuffer(GL_UNIFORM_BUFFER, curvemap->buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(OCIO_GLSLCurveMappingParameters), 0, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); - /* GL_RGB16F_ARB could be not supported at some drivers - * in this case we could not use GLSL display - */ - shader->curve_mapping_texture_valid = glGetError() == GL_NO_ERROR; + curvemap->valid = (curvemap->texture != 0); + curvemap->cacheId = 0; - return shader->curve_mapping_texture_valid; + *curvemap_ptr = curvemap; } -static void freeGLSLShader(OCIO_GLSLShader *shader) +static void freeGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap) { - if (shader->curve_mapping_texture_allocated) { - glDeleteTextures(1, &shader->curve_mapping_texture); - } + glDeleteTextures(1, &curvemap->texture); + glDeleteBuffers(1, &curvemap->buffer); - if (shader->lut3d_texture_allocated) { - glDeleteTextures(1, &shader->lut3d_texture); - } + OBJECT_GUARDED_DELETE(curvemap, OCIO_GLSLCurveMappping); +} + +static void updateGLSLCurveMapping(OCIO_GLSLCurveMappping *curvemap, + OCIO_CurveMappingSettings *curve_mapping_settings, + size_t cacheId) +{ + /* No need to continue if curvemapping is not used. Just use whatever is in this cache. */ + if (curve_mapping_settings == NULL) + return; + + if (curvemap->cacheId == cacheId) + return; - if (shader->lut3d) { - MEM_freeN(shader->lut3d); + if (curvemap->cacheId == 0) { + /* This cache was previously used as dummy. Recreate the texture. */ + glDeleteTextures(1, &curvemap->texture); + allocateCurveMappingTexture(curvemap, curve_mapping_settings); } - if (shader->program) { - glDeleteProgram(shader->program); + /* Update texture. */ + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_1D, curvemap->texture); + glTexSubImage1D(GL_TEXTURE_1D, + 0, + 0, + curve_mapping_settings->lut_size, + GL_RGBA, + GL_FLOAT, + curve_mapping_settings->lut); + + /* Update uniforms. */ + OCIO_GLSLCurveMappingParameters data; + for (int i = 0; i < 4; i++) { + data.curve_mapping_range[i] = curve_mapping_settings->range[i]; + data.curve_mapping_mintable[i] = curve_mapping_settings->mintable[i]; + data.curve_mapping_ext_in_x[i] = curve_mapping_settings->ext_in_x[i]; + data.curve_mapping_ext_in_y[i] = curve_mapping_settings->ext_in_y[i]; + data.curve_mapping_ext_out_x[i] = curve_mapping_settings->ext_out_x[i]; + data.curve_mapping_ext_out_y[i] = curve_mapping_settings->ext_out_y[i]; + data.curve_mapping_first_x[i] = curve_mapping_settings->first_x[i]; + data.curve_mapping_first_y[i] = curve_mapping_settings->first_y[i]; + data.curve_mapping_last_x[i] = curve_mapping_settings->last_x[i]; + data.curve_mapping_last_y[i] = curve_mapping_settings->last_y[i]; + } + for (int i = 0; i < 3; i++) { + data.curve_mapping_black[i] = curve_mapping_settings->black[i]; + data.curve_mapping_bwmul[i] = curve_mapping_settings->bwmul[i]; } + data.curve_mapping_lut_size = curve_mapping_settings->lut_size; + data.curve_mapping_use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate; + + glBindBuffer(GL_UNIFORM_BUFFER, curvemap->buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(OCIO_GLSLCurveMappingParameters), &data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + curvemap->cacheId = cacheId; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LRU cache + * \{ */ - if (shader->shader_interface) { - GPU_shaderinterface_discard(shader->shader_interface); +static size_t hash_string(const char *str) +{ + size_t i = 0, c; + while ((c = *str++)) { + i = i * 37 + c; } + return i; +} - if (shader->ocio_shader) { - glDeleteShader(shader->ocio_shader); +static OCIO_GLSLCacheHandle *cacheSearch(OCIO_GLSLCacheHandle cache[SHADER_CACHE_SIZE], + size_t cache_id) +{ + OCIO_GLSLCacheHandle *cached_item = &cache[0]; + for (int i = 0; i < SHADER_CACHE_SIZE; i++, cached_item++) { + if (cached_item->data == NULL) { + continue; + } + else if (cached_item->cache_id == cache_id) { + /* LRU cache, so move to front. */ + OCIO_GLSLCacheHandle found_item = *cached_item; + for (int j = i; j > 0; j--) { + cache[j] = cache[j - 1]; + } + cache[0] = found_item; + return &cache[0]; + } + } + /* LRU cache, shift other items back so we can insert at the front. */ + OCIO_GLSLCacheHandle last_item = cache[SHADER_CACHE_SIZE - 1]; + for (int j = SHADER_CACHE_SIZE - 1; j > 0; j--) { + cache[j] = cache[j - 1]; } + /* Copy last to front and let the caller initialize it. */ + cache[0] = last_item; + return &cache[0]; +} - using std::string; - shader->lut3dCacheID.~string(); - shader->shaderCacheID.~string(); +/** \} */ - MEM_freeN(shader); -} +/* -------------------------------------------------------------------- */ +/** \name OCIO GLSL Implementation + * \{ */ /* Detect if we can support GLSL drawing */ bool OCIOImpl::supportGLSLDraw() { - /* uses GL_RGB16F_ARB */ - return GLEW_VERSION_3_0 || GLEW_ARB_texture_float; + /* Minimum supported version 3.3 does meet all requirements. */ + return true; } /** @@ -295,14 +588,21 @@ bool OCIOImpl::supportGLSLDraw() * restore OpenGL context to it's pre-GLSL draw state. */ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, - OCIO_ConstProcessorRcPtr *processor, + OCIO_ConstProcessorRcPtr *ocio_processor_scene_to_ui, + OCIO_ConstProcessorRcPtr *ocio_processor_ui_to_display, OCIO_CurveMappingSettings *curve_mapping_settings, float dither, - bool use_predivide) + bool use_predivide, + bool use_overlay) { - ConstProcessorRcPtr ocio_processor = *(ConstProcessorRcPtr *)processor; + ConstProcessorRcPtr processor_scene_to_ui = *(ConstProcessorRcPtr *)ocio_processor_scene_to_ui; + ConstProcessorRcPtr processpr_ui_to_display = *( + ConstProcessorRcPtr *)ocio_processor_ui_to_display; bool use_curve_mapping = curve_mapping_settings != NULL; - bool use_dither = dither > std::numeric_limits::epsilon(); + + if (!processor_scene_to_ui || !processor_scene_to_ui) { + return false; + } /* Create state if needed. */ OCIO_GLSLDrawState *state; @@ -310,235 +610,108 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, *state_r = allocateOpenGLState(); state = *state_r; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &state->last_texture); - glGetIntegerv(GL_ACTIVE_TEXTURE, &state->last_texture_unit); - /* Compute cache IDs. */ GpuShaderDesc shaderDesc; shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_3); shaderDesc.setFunctionName("OCIODisplay"); shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE); - std::string lut3dCacheID = ocio_processor->getGpuLut3DCacheID(shaderDesc); - std::string shaderCacheID = ocio_processor->getGpuShaderTextCacheID(shaderDesc); + const char *shader_cache_str = processor_scene_to_ui->getGpuShaderTextCacheID(shaderDesc); + const char *lut3d_cache_str = processor_scene_to_ui->getGpuLut3DCacheID(shaderDesc); + /* Used for comparison. */ + std::string shaderCacheID = shader_cache_str; + std::string lut3dCacheID = lut3d_cache_str; - /* Find matching cached shader. */ - OCIO_GLSLShader *shader = NULL; - for (int i = 0; i < SHADER_CACHE_SIZE; i++) { - OCIO_GLSLShader *cached_shader = state->shader_cache[i]; - if (cached_shader == NULL) { - continue; - } + size_t shader_cache_id = hash_string(shader_cache_str); + size_t lut3d_cache_id = hash_string(lut3d_cache_str); + size_t curvemap_cache_id = curve_mapping_settings ? curve_mapping_settings->cache_id : 0; - if (cached_shader->lut3dCacheID == lut3dCacheID && - cached_shader->shaderCacheID == shaderCacheID && - cached_shader->use_predivide == use_predivide && - cached_shader->use_curve_mapping == use_curve_mapping && - cached_shader->use_dither == use_dither) { - /* LRU cache, so move to front. */ - for (int j = i; j > 0; j--) { - state->shader_cache[j] = state->shader_cache[j - 1]; - } - state->shader_cache[0] = cached_shader; + OCIO_GLSLCacheHandle *shader_handle = cacheSearch(state->shader_cache, shader_cache_id); + OCIO_GLSLCacheHandle *lut3d_handle = cacheSearch(state->lut3d_cache, lut3d_cache_id); + /* We cannot keep more than one cache for curvemap because their cache id is a pointer. + * The pointer cannot be the same for one update but can be the same after a second update. */ + OCIO_GLSLCacheHandle *curvemap_handle = &state->curvemap_cache[0]; - shader = cached_shader; - break; - } - } - - if (shader == NULL) { - /* LRU cache, shift other items back so we can insert at the front. */ - OCIO_GLSLShader *last_shader = state->shader_cache[SHADER_CACHE_SIZE - 1]; - if (last_shader) { - freeGLSLShader(last_shader); - } - for (int j = SHADER_CACHE_SIZE - 1; j > 0; j--) { - state->shader_cache[j] = state->shader_cache[j - 1]; - } - - /* Allocate memory for shader. */ - shader = (OCIO_GLSLShader *)MEM_callocN(sizeof(OCIO_GLSLShader), "OCIO GLSL Shader"); - state->shader_cache[0] = shader; - - new (&shader->lut3dCacheID) std::string(); - new (&shader->shaderCacheID) std::string(); - - shader->lut3dCacheID = lut3dCacheID; - shader->shaderCacheID = shaderCacheID; - shader->use_curve_mapping = use_curve_mapping; - shader->use_dither = use_dither; - shader->use_predivide = use_predivide; - - bool valid = true; - - /* Compute 3D LUT. */ - if (valid && ensureLUT3DAllocated(shader)) { - ocio_processor->getGpuLut3D(shader->lut3d, shaderDesc); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_3D, shader->lut3d_texture); - glTexSubImage3D(GL_TEXTURE_3D, - 0, - 0, - 0, - 0, - LUT3D_EDGE_SIZE, - LUT3D_EDGE_SIZE, - LUT3D_EDGE_SIZE, - GL_RGB, - GL_FLOAT, - shader->lut3d); - } - else { - valid = false; - } - - /* Allocate curve mapping texture. */ - if (valid && use_curve_mapping) { - if (!ensureCurveMappingAllocated(shader, curve_mapping_settings)) { - valid = false; - } - } - - if (valid) { - /* Vertex shader */ - std::ostringstream osv; - - osv << "#version 330\n"; - osv << datatoc_gpu_shader_display_transform_vertex_glsl; - - shader->vert_shader = compileShaderText(GL_VERTEX_SHADER, osv.str().c_str()); - - /* Fragment shader */ - std::ostringstream os; + OCIO_GLSLShader **shader_ptr = (OCIO_GLSLShader **)&shader_handle->data; + OCIO_GLSLLut3d **lut3d_ptr = (OCIO_GLSLLut3d **)&lut3d_handle->data; + OCIO_GLSLCurveMappping **curvemap_ptr = (OCIO_GLSLCurveMappping **)&curvemap_handle->data; - os << "#version 330\n"; + ensureGLSLShader( + shader_ptr, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, shaderCacheID); + ensureGLSLLut3d( + lut3d_ptr, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, shaderCacheID); + ensureGLSLCurveMapping(curvemap_ptr, curve_mapping_settings); - /* Work around OpenColorIO not supporting latest GLSL yet. */ - os << "#define texture2D texture\n"; - os << "#define texture3D texture\n"; + OCIO_GLSLShader *shader = (OCIO_GLSLShader *)shader_handle->data; + OCIO_GLSLLut3d *shader_lut = (OCIO_GLSLLut3d *)lut3d_handle->data; + OCIO_GLSLCurveMappping *shader_curvemap = (OCIO_GLSLCurveMappping *)curvemap_handle->data; - if (use_predivide) { - os << "#define USE_PREDIVIDE\n"; - } + updateGLSLShader( + shader, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, shaderCacheID); + updateGLSLLut3d( + shader_lut, &processor_scene_to_ui, &processpr_ui_to_display, &shaderDesc, lut3dCacheID); + updateGLSLCurveMapping(shader_curvemap, curve_mapping_settings, curvemap_cache_id); - if (use_dither) { - os << "#define USE_DITHER\n"; - } + /* Update handles cache keys. */ + shader_handle->cache_id = shader_cache_id; + lut3d_handle->cache_id = lut3d_cache_id; + curvemap_handle->cache_id = curvemap_cache_id; - if (use_curve_mapping) { - os << "#define USE_CURVE_MAPPING\n"; - } + if (shader->valid && shader_lut->valid && shader_curvemap->valid) { + /* Bind textures to sampler units. Texture 0 is set by caller. + * Uniforms have already been set for texture bind points.*/ - os << ocio_processor->getGpuShaderText(shaderDesc) << "\n"; - os << datatoc_gpu_shader_display_transform_glsl; + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_3D, shader_lut->texture); - shader->ocio_shader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str()); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_3D, shader_lut->texture_display); - /* Program */ - if (shader->ocio_shader && shader->vert_shader) { - shader->program = linkShaders(shader->ocio_shader, shader->vert_shader); - } - - if (shader->program) { - if (shader->shader_interface) { - GPU_shaderinterface_discard(shader->shader_interface); - } - shader->shader_interface = GPU_shaderinterface_create(shader->program); - } - } - } - - /* Update curve mapping texture. */ - if (use_curve_mapping && shader->curve_mapping_texture_allocated) { - if (shader->curve_mapping_cache_id != curve_mapping_settings->cache_id) { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, shader->curve_mapping_texture); - glTexSubImage1D(GL_TEXTURE_1D, - 0, - 0, - curve_mapping_settings->lut_size, - GL_RGBA, - GL_FLOAT, - curve_mapping_settings->lut); - } - } - - /* Bind Shader. */ - if (shader->program) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_3D, shader->lut3d_texture); - - if (use_curve_mapping) { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, shader->curve_mapping_texture); - } + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_1D, shader_curvemap->texture); glActiveTexture(GL_TEXTURE0); - /* IMM needs vertex format even if we don't draw with it. - * - * NOTE: The only reason why it's here is because of Cycles viewport. - * All other areas are managing their own vertex formats. - * Doing it here is probably harmless, but kind of stupid. - * - * TODO(sergey): Look into some nicer solution. - */ - GPUVertFormat *format = immVertexFormat(); - GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindProgram(shader->program, shader->shader_interface); - - immUniform1i("image_texture", 0); - immUniform1i("lut3d_texture", 1); - - if (use_dither) { - immUniform1f("dither", dither); - } + /* Bind UBO. */ + glBindBufferBase(GL_UNIFORM_BUFFER, 0, shader_curvemap->buffer); - if (use_curve_mapping) { - immUniform1i("curve_mapping_texture", 2); - immUniform1i("curve_mapping_lut_size", curve_mapping_settings->lut_size); - immUniform1i("use_curve_mapping_extend_extrapolate", - curve_mapping_settings->use_extend_extrapolate); - immUniform4fv("curve_mapping_mintable", curve_mapping_settings->mintable); - immUniform4fv("curve_mapping_range", curve_mapping_settings->range); - immUniform4fv("curve_mapping_ext_in_x", curve_mapping_settings->ext_in_x); - immUniform4fv("curve_mapping_ext_in_y", curve_mapping_settings->ext_in_y); - immUniform4fv("curve_mapping_ext_out_x", curve_mapping_settings->ext_out_x); - immUniform4fv("curve_mapping_ext_out_y", curve_mapping_settings->ext_out_y); - immUniform4fv("curve_mapping_first_x", curve_mapping_settings->first_x); - immUniform4fv("curve_mapping_first_y", curve_mapping_settings->first_y); - immUniform4fv("curve_mapping_last_x", curve_mapping_settings->last_x); - immUniform4fv("curve_mapping_last_y", curve_mapping_settings->last_y); - immUniform3fv("curve_mapping_black", curve_mapping_settings->black); - immUniform3fv("curve_mapping_bwmul", curve_mapping_settings->bwmul); - } + /* TODO(fclem) remove remains of IMM. */ + immBindProgram(shader->program, shader->interface); + + /* Bind Shader and set uniforms. */ + // glUseProgram(shader->program); + glUniform1f(shader->dither_loc, dither); + glUniform1i(shader->overlay_tex_loc, use_overlay ? 1 : 0); + glUniform1i(shader->overlay_loc, use_overlay); + glUniform1i(shader->predivide_loc, use_predivide); + glUniform1i(shader->curve_mapping_loc, use_curve_mapping); return true; } - else { - glActiveTexture(state->last_texture_unit); - glBindTexture(GL_TEXTURE_2D, state->last_texture); - return false; - } + return false; } -void OCIOImpl::finishGLSLDraw(OCIO_GLSLDrawState *state) +void OCIOImpl::finishGLSLDraw(OCIO_GLSLDrawState * /*state*/) { - glActiveTexture(state->last_texture_unit); - glBindTexture(GL_TEXTURE_2D, state->last_texture); immUnbindProgram(); } void OCIOImpl::freeGLState(OCIO_GLSLDrawState *state) { for (int i = 0; i < SHADER_CACHE_SIZE; i++) { - if (state->shader_cache[i]) { - freeGLSLShader(state->shader_cache[i]); + if (state->shader_cache[i].data) { + freeGLSLShader((OCIO_GLSLShader *)state->shader_cache[i].data); + } + if (state->lut3d_cache[i].data) { + freeGLSLLut3d((OCIO_GLSLLut3d *)state->lut3d_cache[i].data); + } + if (state->curvemap_cache[i].data) { + freeGLSLCurveMapping((OCIO_GLSLCurveMappping *)state->curvemap_cache[i].data); } } MEM_freeN(state); } + +/** \} */ -- cgit v1.2.3