Implementation of curve mapping in GLSL
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 9 Oct 2013 15:57:32 +0000 (15:57 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 9 Oct 2013 15:57:32 +0000 (15:57 +0000)
The title says it all, now having curve mapping
enabled in color management settings wouldn't
force fallback from GLSL to CPU based color space
conversion.

14 files changed:
SConstruct
intern/opencolorio/CMakeLists.txt
intern/opencolorio/SConscript
intern/opencolorio/fallback_impl.cc
intern/opencolorio/gpu_shader_display_transform.glsl [new file with mode: 0644]
intern/opencolorio/ocio_capi.cc
intern/opencolorio/ocio_capi.h
intern/opencolorio/ocio_impl.h
intern/opencolorio/ocio_impl_glsl.cc
source/blender/editors/render/render_internal.c
source/blender/editors/screen/glutil.c
source/blender/imbuf/IMB_colormanagement.h
source/blender/imbuf/intern/colormanagement.c
source/blender/makesrna/intern/rna_render.c

index 539414d6fba98173019da83884974b7ed5b9135a..f9fa289a2b06712c2475bb9b9815edd7784ea217 100644 (file)
@@ -552,6 +552,7 @@ if B.targets != ['cudakernels']:
     data_to_c_simple("source/blender/gpu/shaders/gpu_shader_vertex.glsl")
     data_to_c_simple("source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl")
     data_to_c_simple("source/blender/gpu/shaders/gpu_shader_vsm_store_vert.glsl")
+    data_to_c_simple("intern/opencolorio/gpu_shader_display_transform.glsl")
 
     # --- blender ---
     data_to_c_simple("release/datafiles/bfont.pfb")
index 30a74baa646b17a3c64a71df641b942015c4bbab..3a139dc64cf5a6115909a7aba2cc691a81f33e9b 100644 (file)
@@ -62,6 +62,8 @@ if(WITH_OPENCOLORIO)
                        ${BOOST_INCLUDE_DIR}
                )
        endif()
+
+       data_to_c_simple(gpu_shader_display_transform.glsl SRC)
 endif()
 
 
index 7f050f25cae13deae60ec6daf19dde4d913ba791..70aa900071915ad9f199208a37db875e4ade87b5 100644 (file)
@@ -39,6 +39,13 @@ if env['WITH_BF_OCIO']:
 
     if env['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
         incs += ' ' + env['BF_BOOST_INC']
+
+    # generated data files
+    import os
+    sources.extend((
+        os.path.join(env['DATA_SOURCES'], "gpu_shader_display_transform.glsl.c"),
+    ))
+
 else:
     sources.remove('ocio_impl.cc')
     sources.remove('ocio_impl_glsl.cc')
index 36dac689287df926f1539596454b77769d101ce3..ca999eab569cd1dea77d61209a33175d82a610d2 100644 (file)
@@ -418,7 +418,8 @@ bool FallbackImpl::supportGLSLDraw(void)
        return false;
 }
 
-bool FallbackImpl::setupGLSLDraw(struct OCIO_GLSLDrawState ** /*state_r*/, OCIO_ConstProcessorRcPtr * /*processor*/, bool /*predivide*/)
+bool FallbackImpl::setupGLSLDraw(struct OCIO_GLSLDrawState ** /*state_r*/, OCIO_ConstProcessorRcPtr * /*processor*/,
+                                 OCIO_CurveMappingSettings * /*curve_mapping_settings*/, bool /*predivide*/)
 {
        return false;
 }
diff --git a/intern/opencolorio/gpu_shader_display_transform.glsl b/intern/opencolorio/gpu_shader_display_transform.glsl
new file mode 100644 (file)
index 0000000..6ba3fa5
--- /dev/null
@@ -0,0 +1,123 @@
+uniform sampler2D image_texture;
+uniform sampler3D lut3d_texture;
+uniform bool predivide;
+
+#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 ivec4 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;
+
+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 texture1D(curve_mapping_texture, texture_index) [table];
+}
+
+float curvemap_calc_extend(int table, float x, vec2 first, vec2 last)
+{
+       if (x <= first[0]) {
+               if (use_curve_mapping_extend_extrapolate[table] == 0) {
+                       /* no extrapolate */
+                       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];
+               }
+       }
+       else if (x >= last[0]) {
+               if (use_curve_mapping_extend_extrapolate[table] == 0) {
+                       /* no extrapolate */
+                       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];
+               }
+       }
+       return 0.0;
+}
+
+float curvemap_evaluateF(int table, float value)
+{
+       float mintable_ = curve_mapping_mintable[table];
+       float range = curve_mapping_range[table];
+       float mintable = 0.0;
+       int CM_TABLE = curve_mapping_lut_size - 1;
+
+       float fi;
+       int i;
+
+       /* index in table */
+       fi = (value - mintable) * range;
+       i = int(fi);
+
+       /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */
+       if (fi < 0.0 || fi > float(CM_TABLE)) {
+               return curvemap_calc_extend(table, value,
+                                           vec2(curve_mapping_first_x[table], curve_mapping_first_y[table]),
+                                           vec2(curve_mapping_last_x[table], curve_mapping_last_y[table]));
+       }
+       else {
+               if (i < 0) return read_curve_mapping(table, 0);
+               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);
+       }
+}
+
+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];
+       return result;
+}
+#endif
+
+void main()
+{
+       vec4 col = texture2D(image_texture, gl_TexCoord[0].st);
+#ifdef USE_CURVE_MAPPING
+       col = curvemapping_evaluate_premulRGBF(col);
+#endif
+       if (predivide && 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;
+       }
+
+       /* 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
+        */
+       gl_FragColor = OCIODisplay(col, lut3d_texture);
+}
index 1656ad9cbc0166f47372d5a17fb568803e0cd421..30668dff245d3d402de9e6b87c9dbfb84dd06e99 100644 (file)
@@ -323,9 +323,10 @@ int OCIO_supportGLSLDraw(void)
        return (int) impl->supportGLSLDraw();
 }
 
-int OCIO_setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor, int predivide)
+int OCIO_setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
+                       OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide)
 {
-       return (int) impl->setupGLSLDraw(state_r, processor, (bool) predivide);
+       return (int) impl->setupGLSLDraw(state_r, processor, curve_mapping_settings, predivide);
 }
 
 void OCIO_finishGLSLDraw(struct OCIO_GLSLDrawState *state)
index 0846b8ff7b36a9e92ee7b2d644439f4799616c3e..5532ade1f3a1d9b1f2311c5b1e524bffeab2d6a2 100644 (file)
@@ -54,6 +54,62 @@ OCIO_DECLARE_HANDLE(OCIO_ExponentTransformRcPtr);
 OCIO_DECLARE_HANDLE(OCIO_MatrixTransformRcPtr);
 OCIO_DECLARE_HANDLE(OCIO_ConstLookRcPtr);
 
+/* This structure is used to pass curve mapping settings from
+ * blender's DNA structure stored in view transform settings
+ * to a generic OpenColorIO C-API.
+ */
+typedef struct OCIO_CurveMappingSettings {
+       /* This is a LUT which contain values for all 4 curve mapping tables
+        * (combined, R, G and B).
+        *
+        * Element I for table T is stored at I * 4 + T element of this LUT.
+        *
+        * This array is usually returned by curvemapping_table_RGBA().
+        */
+       float *lut;
+
+       /* Size of single curve mapping table, 1/4 size of lut array. */
+       int lut_size;
+
+       /* Extend extrapolation flags for all the tables.
+        * if use_extend_extrapolate[T] != 0 means extrapolation for
+        * table T is needed.
+        */
+       int use_extend_extrapolate[4];
+
+       /* Minimal X value of the curve mapping tables. */
+       float mintable[4];
+
+       /* Per curve mapping table range. */
+       float range[4];
+
+       /* Lower extension value, stored as per-component arrays. */
+       float ext_in_x[4], ext_in_y[4];
+
+       /* Higher extension value, stored as per-component arrays. */
+       float ext_out_x[4], ext_out_y[4];
+
+       /* First points of the tables, both X and Y values.
+        * Needed for easier and faster access when extrapolating.
+        */
+       float first_x[4], first_y[4];
+
+       /* Last points of the tables, both X and Y values.
+        * Needed for easier and faster access when extrapolating.
+        */
+       float last_x[4], last_y[4];
+
+       /* Premultiplication settings: black level and scale to match
+        * with white level.
+        */
+       float black[3], bwmul[3];
+
+       /* Cache id of the original curve mapping, used to detect when
+        * upload of new settings to GPU is needed.
+        */
+       size_t cache_id;
+} OCIO_CurveMappingSettings;
+
 void OCIO_init(void);
 void OCIO_exit(void);
 
@@ -132,7 +188,8 @@ void OCIO_matrixTransformRelease(OCIO_MatrixTransformRcPtr *mt);
 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, int predivide);
+int OCIO_setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
+                       OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide);
 void OCIO_finishGLSLDraw(struct OCIO_GLSLDrawState *state);
 void OCIO_freeOGLState(struct OCIO_GLSLDrawState *state);
 
index 48d18fa78ead3e67e04cc036ae4287a0199c2a99..8b666e8ae1446ba4eba65928d9189c69d3ca4ec9 100644 (file)
@@ -106,7 +106,8 @@ public:
        virtual void matrixTransformScale(float * m44, float * offset4, const float * scale4) = 0;
 
        virtual bool supportGLSLDraw(void) = 0;
-       virtual bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor, bool predivide) = 0;
+       virtual bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
+                                  OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide) = 0;
        virtual void finishGLSLDraw(struct OCIO_GLSLDrawState *state) = 0;
        virtual void freeGLState(struct OCIO_GLSLDrawState *state_r) = 0;
 };
@@ -189,7 +190,8 @@ public:
        void matrixTransformScale(float *m44, float *offset4, const float *scale4);
 
        bool supportGLSLDraw(void);
-       bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor, bool predivide);
+       bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
+                          OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide);
        void finishGLSLDraw(struct OCIO_GLSLDrawState *state);
        void freeGLState(struct OCIO_GLSLDrawState *state_r);
 };
@@ -273,7 +275,8 @@ public:
        void matrixTransformScale(float * m44, float * offset4, const float * scale4);
 
        bool supportGLSLDraw(void);
-       bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor, bool predivide);
+       bool setupGLSLDraw(struct OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
+                          OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide);
        void finishGLSLDraw(struct OCIO_GLSLDrawState *state);
        void freeGLState(struct OCIO_GLSLDrawState *state_r);
 };
index 1a3132fa4b3de800e436c2c79466ac430063000c..a10afcad200d5d95453535f0e78c4c9856b676f9 100644 (file)
@@ -48,6 +48,8 @@ using namespace OCIO_NAMESPACE;
 
 static const int LUT3D_EDGE_SIZE = 64;
 
+extern char datatoc_gpu_shader_display_transform_glsl[];
+
 /* **** OpenGL drawing routines using GLSL for color space transform ***** */
 
 typedef struct OCIO_GLSLDrawState {
@@ -60,42 +62,24 @@ typedef struct OCIO_GLSLDrawState {
 
        float *lut3d;  /* 3D LUT table */
 
+       bool curve_mapping_used;
+       bool curve_mapping_texture_allocated;
+       bool curve_mapping_texture_valid;
+       GLuint curve_mapping_texture;
+       size_t curve_mapping_cache_id;
+
        /* Cache */
        std::string lut3dcacheid;
        std::string shadercacheid;
 
        /* GLSL stuff */
-       GLuint fragShader;
+       GLuint ocio_shader;
        GLuint program;
 
        /* Previous OpenGL state. */
        GLint last_texture, last_texture_unit;
 } OCIO_GLSLDrawState;
 
-/* Hardcoded to do alpha predivide before color space conversion */
-/* 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
- */
-static const char *g_fragShaderText = ""
-"\n"
-"uniform sampler2D tex1;\n"
-"uniform sampler3D tex2;\n"
-"uniform bool predivide;\n"
-"\n"
-"void main()\n"
-"{\n"
-"    vec4 col = texture2D(tex1, gl_TexCoord[0].st);\n"
-"    if (predivide && col[3] > 0.0 && col[3] < 1.0) {\n"
-"        float inv_alpha = 1.0 / col[3];\n"
-"        col[0] *= inv_alpha;\n"
-"        col[1] *= inv_alpha;\n"
-"        col[2] *= inv_alpha;\n"
-"    }\n"
-"    gl_FragColor = OCIODisplay(col, tex2);\n"
-"\n"
-"}\n";
-
 static GLuint compileShaderText(GLenum shaderType, const char *text)
 {
        GLuint shader;
@@ -117,15 +101,14 @@ static GLuint compileShaderText(GLenum shaderType, const char *text)
        return shader;
 }
 
-static GLuint linkShaders(GLuint fragShader)
+static GLuint linkShaders(GLuint ocio_shader)
 {
-       if (!fragShader)
+       if (!ocio_shader)
                return 0;
 
        GLuint program = glCreateProgram();
 
-       if (fragShader)
-               glAttachShader(program, fragShader);
+       glAttachShader(program, ocio_shader);
 
        glLinkProgram(program);
 
@@ -197,6 +180,37 @@ static bool ensureLUT3DAllocated(OCIO_GLSLDrawState *state)
        return state->lut3d_texture_valid;
 }
 
+static bool ensureCurveMappingAllocated(OCIO_GLSLDrawState *state, OCIO_CurveMappingSettings *curve_mapping_settings)
+{
+       if (state->curve_mapping_texture_allocated)
+               return state->curve_mapping_texture_valid;
+
+       glGenTextures(1, &state->curve_mapping_texture);
+
+       glActiveTexture(GL_TEXTURE2);
+       glBindTexture(GL_TEXTURE_1D, state->curve_mapping_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) {}
+
+       glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F_ARB, curve_mapping_settings->lut_size,
+                    0, GL_RGBA, GL_FLOAT, curve_mapping_settings->lut);
+
+       state->curve_mapping_texture_allocated = true;
+
+       /* GL_RGB16F_ARB could be not supported at some drivers
+        * in this case we could not use GLSL display
+        */
+       state->curve_mapping_texture_valid = glGetError() == GL_NO_ERROR;
+
+       return state->curve_mapping_texture_valid;
+}
+
 /* Detect if we can support GLSL drawing */
 bool OCIOImpl::supportGLSLDraw()
 {
@@ -214,9 +228,11 @@ bool OCIOImpl::supportGLSLDraw()
  * When all drawing is finished, finishGLSLDraw shall be called to
  * restore OpenGL context to it's pre-GLSL draw state.
  */
-bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor, bool predivide)
+bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRcPtr *processor,
+                             OCIO_CurveMappingSettings *curve_mapping_settings, bool predivide)
 {
        ConstProcessorRcPtr ocio_processor = *(ConstProcessorRcPtr *) processor;
+       bool use_curve_mapping = curve_mapping_settings != NULL;
 
        /* Create state if needed. */
        OCIO_GLSLDrawState *state;
@@ -234,12 +250,36 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRc
                return false;
        }
 
+       if (use_curve_mapping) {
+               if (!ensureCurveMappingAllocated(state, curve_mapping_settings)) {
+                       glActiveTexture(state->last_texture_unit);
+                       glBindTexture(GL_TEXTURE_2D, state->last_texture);
+
+                       return false;
+               }
+       }
+       else {
+               if (state->curve_mapping_texture_allocated) {
+                       glDeleteTextures(1, &state->curve_mapping_texture);
+                       state->curve_mapping_texture_allocated = false;
+               }
+       }
+
        /* Step 1: Create a GPU Shader Description */
        GpuShaderDesc shaderDesc;
        shaderDesc.setLanguage(GPU_LANGUAGE_GLSL_1_0);
        shaderDesc.setFunctionName("OCIODisplay");
        shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE);
 
+       if (use_curve_mapping) {
+               if (state->curve_mapping_cache_id != curve_mapping_settings->cache_id) {
+                       glActiveTexture(GL_TEXTURE2);
+                       glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
+                       glTexSubImage1D(GL_TEXTURE_1D, 0, 0, curve_mapping_settings->lut_size,
+                                       GL_RGBA, GL_FLOAT, curve_mapping_settings->lut);
+               }
+       }
+
        /* Step 2: Compute the 3D LUT */
        std::string lut3dCacheID = ocio_processor->getGpuLut3DCacheID(shaderDesc);
        if (lut3dCacheID != state->lut3dcacheid) {
@@ -255,37 +295,73 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r, OCIO_ConstProcessorRc
 
        /* Step 3: Compute the Shader */
        std::string shaderCacheID = ocio_processor->getGpuShaderTextCacheID(shaderDesc);
-       if (state->program == 0 || shaderCacheID != state->shadercacheid) {
+       if (state->program == 0 ||
+           shaderCacheID != state->shadercacheid ||
+           use_curve_mapping != state->curve_mapping_used)
+       {
                state->shadercacheid = shaderCacheID;
 
+               if (state->program) {
+                       glDeleteProgram(state->program);
+               }
+
+               if (state->ocio_shader) {
+                       glDeleteShader(state->ocio_shader);
+               }
+
                std::ostringstream os;
-               os << ocio_processor->getGpuShaderText(shaderDesc) << "\n";
-               os << g_fragShaderText;
 
-               if (state->fragShader)
-                       glDeleteShader(state->fragShader);
+               if (use_curve_mapping) {
+                       os << "#define USE_CURVE_MAPPING\n";
+               }
 
-               state->fragShader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str());
+               os << ocio_processor->getGpuShaderText(shaderDesc) << "\n";
+               os << datatoc_gpu_shader_display_transform_glsl;
 
-               if (state->fragShader) {
-                       if (state->program)
-                               glDeleteProgram(state->program);
+               state->ocio_shader = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str());
 
-                       state->program = linkShaders(state->fragShader);
+               if (state->ocio_shader) {
+                       state->program = linkShaders(state->ocio_shader);
                }
+
+               state->curve_mapping_used = use_curve_mapping;
        }
 
        if (state->program) {
                glActiveTexture(GL_TEXTURE1);
                glBindTexture(GL_TEXTURE_3D, state->lut3d_texture);
 
+               if (use_curve_mapping) {
+                       glActiveTexture(GL_TEXTURE2);
+                       glBindTexture(GL_TEXTURE_1D, state->curve_mapping_texture);
+               }
+
                glActiveTexture(GL_TEXTURE0);
 
                glUseProgram(state->program);
-               glUniform1i(glGetUniformLocation(state->program, "tex1"), 0);
-               glUniform1i(glGetUniformLocation(state->program, "tex2"), 1);
+
+               glUniform1i(glGetUniformLocation(state->program, "image_texture"), 0);
+               glUniform1i(glGetUniformLocation(state->program, "lut3d_texture"), 1);
                glUniform1i(glGetUniformLocation(state->program, "predivide"), predivide);
 
+               if (use_curve_mapping) {
+                       glUniform1i(glGetUniformLocation(state->program, "curve_mapping_texture"), 2);
+                       glUniform1i(glGetUniformLocation(state->program, "curve_mapping_lut_size"), curve_mapping_settings->lut_size);
+                       glUniform4iv(glGetUniformLocation(state->program, "use_curve_mapping_extend_extrapolate"), 1, curve_mapping_settings->use_extend_extrapolate);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_mintable"), 1, curve_mapping_settings->mintable);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_range"), 1, curve_mapping_settings->range);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_in_x"), 1, curve_mapping_settings->ext_in_x);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_in_y"), 1, curve_mapping_settings->ext_in_y);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_out_x"), 1, curve_mapping_settings->ext_out_x);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_ext_out_y"), 1, curve_mapping_settings->ext_out_y);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_first_x"), 1, curve_mapping_settings->first_x);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_first_y"), 1, curve_mapping_settings->first_y);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_last_x"), 1, curve_mapping_settings->last_x);
+                       glUniform4fv(glGetUniformLocation(state->program, "curve_mapping_last_y"), 1, curve_mapping_settings->last_y);
+                       glUniform3fv(glGetUniformLocation(state->program, "curve_mapping_black"), 1, curve_mapping_settings->black);
+                       glUniform3fv(glGetUniformLocation(state->program, "curve_mapping_bwmul"), 1, curve_mapping_settings->bwmul);
+               }
+
                return true;
        }
        else {
@@ -316,8 +392,8 @@ void OCIOImpl::freeGLState(struct OCIO_GLSLDrawState *state)
        if (state->program)
                glDeleteProgram(state->program);
 
-       if (state->fragShader)
-               glDeleteShader(state->fragShader);
+       if (state->ocio_shader)
+               glDeleteShader(state->ocio_shader);
 
        state->lut3dcacheid.~string();
        state->shadercacheid.~string();
index 39d6f836815926f71417e60bb245bab58a1b7b03..df8d5ec4e84b74a8ad89a2555126f4ed0581ee13 100644 (file)
@@ -1152,7 +1152,7 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C)
 
                /* Try using GLSL display transform. */
                if (force_fallback == false) {
-                       if (IMB_colormanagement_setup_glsl_draw(&scene->view_settings, &scene->display_settings, true, false)) {
+                       if (IMB_colormanagement_setup_glsl_draw(&scene->view_settings, &scene->display_settings, true)) {
                                glEnable(GL_BLEND);
                                glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
                                glaDrawPixelsTex(rres.xof, rres.yof, rres.rectx, rres.recty, GL_RGBA, GL_FLOAT,
index 70e5aab66281a3aaff090d5d4d1c3276a4fbe576..b70b06f2aa465c1684ae23179447c6df4af52a70 100644 (file)
@@ -1091,18 +1091,15 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter,
                if (ibuf->rect_float) {
                        if (ibuf->float_colorspace) {
                                ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
-                                                                                   ibuf->float_colorspace,
-                                                                                   true, false);
+                                                                                   ibuf->float_colorspace, true);
                        }
                        else {
-                               ok = IMB_colormanagement_setup_glsl_draw(view_settings, display_settings,
-                                                                        true, false);
+                               ok = IMB_colormanagement_setup_glsl_draw(view_settings, display_settings, true);
                        }
                }
                else {
                        ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
-                                                                           ibuf->rect_colorspace,
-                                                                           false, false);
+                                                                           ibuf->rect_colorspace, false);
                }
 
                if (ok) {
index 64a2bbb72d9c0e59e4c2082893a17ef815a993c1..8af86389db30a367baebeb178dd710a37de33cc4 100644 (file)
@@ -174,19 +174,16 @@ void IMB_colormanagement_processor_free(struct ColormanageProcessor *cm_processo
 /* ** OpenGL drawing routines using GLSL for color space transform ** */
 
 /* Test if GLSL drawing is supported for combination of graphics card and this configuration */
-bool IMB_colormanagement_support_glsl_draw(const struct ColorManagedViewSettings *view_settings,
-                                           bool skip_curves);
+bool IMB_colormanagement_support_glsl_draw(const struct ColorManagedViewSettings *view_settings);
 /* Configures GLSL shader for conversion from scene linear to display space */
 bool IMB_colormanagement_setup_glsl_draw(const struct ColorManagedViewSettings *view_settings,
                                          const struct ColorManagedDisplaySettings *display_settings,
-                                         bool predivide,
-                                         bool skip_curves);
+                                         bool predivide);
 /* Same as above, but display space conversion happens from a specified space */
 bool IMB_colormanagement_setup_glsl_draw_from_space(const struct ColorManagedViewSettings *view_settings,
                                                     const struct ColorManagedDisplaySettings *display_settings,
                                                     struct ColorSpace *colorspace,
-                                                    bool predivide,
-                                                    bool skip_curves);
+                                                    bool predivide);
 /* Same as setup_glsl_draw, but color management settings are guessing from a given context */
 bool IMB_colormanagement_setup_glsl_draw_ctx(const struct bContext *C, bool predivide);
 /* Same as setup_glsl_draw_from_space, but color management settings are guessing from a given context */
index 61051464c7f53c1be455789830abe1ec97b43ef5..cf9cc0c2e6fa2b1e4f060f999c18748ee46a7df4 100644 (file)
@@ -119,6 +119,11 @@ static struct global_glsl_state {
        char input[MAX_COLORSPACE_NAME];
        float exposure, gamma;
 
+       CurveMapping *curve_mapping, *orig_curve_mapping;
+       bool use_curve_mapping;
+       int curve_mapping_timestamp;
+       OCIO_CurveMappingSettings curve_mapping_settings;
+
        /* Container for GLSL state needed for OCIO module. */
        struct OCIO_GLSLDrawState *ocio_glsl_state;
        struct OCIO_GLSLDrawState *transform_ocio_glsl_state;
@@ -663,6 +668,12 @@ void colormanagement_exit(void)
        if (global_glsl_state.processor)
                OCIO_processorRelease(global_glsl_state.processor);
 
+       if (global_glsl_state.curve_mapping)
+               curvemapping_free(global_glsl_state.curve_mapping);
+
+       if (global_glsl_state.curve_mapping_settings.lut)
+               MEM_freeN(global_glsl_state.curve_mapping_settings.lut);
+
        if (global_glsl_state.ocio_glsl_state)
                OCIO_freeOGLState(global_glsl_state.ocio_glsl_state);
 
@@ -2937,16 +2948,61 @@ static bool check_glsl_display_processor_changed(const ColorManagedViewSettings
                 STREQ(global_glsl_state.input, from_colorspace));
 }
 
+static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping,
+                                           OCIO_CurveMappingSettings *curve_mapping_settings)
+{
+       int i;
+
+       curvemapping_initialize(curve_mapping);
+       curvemapping_premultiply(curve_mapping, false);
+       curvemapping_table_RGBA(curve_mapping,
+                               &curve_mapping_settings->lut,
+                               &curve_mapping_settings->lut_size);
+
+       for (i = 0; i < 4; i++) {
+               CurveMap *cuma = curve_mapping->cm + i;
+               curve_mapping_settings->use_extend_extrapolate[i] = (cuma->flag & CUMA_EXTEND_EXTRAPOLATE) != 0;
+               curve_mapping_settings->range[i] = cuma->range;
+               curve_mapping_settings->mintable[i] = cuma->mintable;
+               curve_mapping_settings->ext_in_x[i] = cuma->ext_in[0];
+               curve_mapping_settings->ext_in_y[i] = cuma->ext_in[1];
+               curve_mapping_settings->ext_out_x[i] = cuma->ext_out[0];
+               curve_mapping_settings->ext_out_y[i] = cuma->ext_out[1];
+               curve_mapping_settings->first_x[i] = cuma->table[0].x;
+               curve_mapping_settings->first_y[i] = cuma->table[0].y;
+               curve_mapping_settings->last_x[i] = cuma->table[CM_TABLE].x;
+               curve_mapping_settings->last_y[i] = cuma->table[CM_TABLE].y;
+       }
+
+       copy_v3_v3(curve_mapping_settings->black, curve_mapping->black);
+       copy_v3_v3(curve_mapping_settings->bwmul, curve_mapping->bwmul);
+
+       curve_mapping_settings->cache_id = (size_t) curve_mapping;
+}
+
 static void update_glsl_display_processor(const ColorManagedViewSettings *view_settings,
                                           const ColorManagedDisplaySettings *display_settings,
                                           const char *from_colorspace)
 {
+       bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0;
+       bool need_update = false;
+
+       need_update = global_glsl_state.processor == NULL ||
+                     check_glsl_display_processor_changed(view_settings, display_settings, from_colorspace) ||
+                     use_curve_mapping != global_glsl_state.use_curve_mapping;
+
+       if (use_curve_mapping && need_update == false) {
+               need_update |= view_settings->curve_mapping->changed_timestamp != global_glsl_state.curve_mapping_timestamp ||
+                              view_settings->curve_mapping != global_glsl_state.orig_curve_mapping;
+       }
+
        /* Update state if there's no processor yet or
         * processor settings has been changed.
         */
-       if (global_glsl_state.processor == NULL ||
-           check_glsl_display_processor_changed(view_settings, display_settings, from_colorspace))
-       {
+       if (need_update) {
+               OCIO_CurveMappingSettings *curve_mapping_settings = &global_glsl_state.curve_mapping_settings;
+               CurveMapping *new_curve_mapping = NULL;
+
                /* Store settings of processor for further comparison. */
                BLI_strncpy(global_glsl_state.look, view_settings->look, MAX_COLORSPACE_NAME);
                BLI_strncpy(global_glsl_state.view, view_settings->view_transform, MAX_COLORSPACE_NAME);
@@ -2955,6 +3011,35 @@ static void update_glsl_display_processor(const ColorManagedViewSettings *view_s
                global_glsl_state.exposure = view_settings->exposure;
                global_glsl_state.gamma = view_settings->gamma;
 
+               /* We're using curve mapping's address as a acache ID,
+                * so we need to make sure re-allocation gives new address here.
+                * We do this by allocating new curve mapping before freeing ol one.
+                */
+               if (use_curve_mapping) {
+                       new_curve_mapping = curvemapping_copy(view_settings->curve_mapping);
+               }
+
+               if (global_glsl_state.curve_mapping) {
+                       curvemapping_free(global_glsl_state.curve_mapping);
+                       MEM_freeN(curve_mapping_settings->lut);
+                       global_glsl_state.curve_mapping = NULL;
+                       curve_mapping_settings->lut = NULL;
+               }
+
+               /* Fill in OCIO's curve mapping settings. */
+               if (use_curve_mapping) {
+                       curve_mapping_to_ocio_settings(new_curve_mapping, &global_glsl_state.curve_mapping_settings);
+
+                       global_glsl_state.curve_mapping = new_curve_mapping;
+                       global_glsl_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp;
+                       global_glsl_state.orig_curve_mapping = view_settings->curve_mapping;
+                       global_glsl_state.use_curve_mapping = true;
+               }
+               else {
+                       global_glsl_state.orig_curve_mapping = NULL;
+                       global_glsl_state.use_curve_mapping = false;
+               }
+
                /* Free old processor, if any. */
                if (global_glsl_state.processor)
                        OCIO_processorRelease(global_glsl_state.processor);
@@ -2970,14 +3055,8 @@ static void update_glsl_display_processor(const ColorManagedViewSettings *view_s
        }
 }
 
-bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *view_settings,
-                                           bool skip_curves)
+bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *UNUSED(view_settings))
 {
-       /* curves not supported yet */
-       if (!skip_curves)
-               if (view_settings && (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES))
-                       return 0;
-
        return OCIO_supportGLSLDraw();
 }
 
@@ -2996,8 +3075,7 @@ bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *view_
  */
 bool IMB_colormanagement_setup_glsl_draw_from_space(const ColorManagedViewSettings *view_settings,
                                                     const ColorManagedDisplaySettings *display_settings,
-                                                    struct ColorSpace *from_colorspace, bool predivide,
-                                                    bool skip_curves)
+                                                    struct ColorSpace *from_colorspace, bool predivide)
 {
        ColorManagedViewSettings default_view_settings;
        const ColorManagedViewSettings *applied_view_settings;
@@ -3014,25 +3092,22 @@ bool IMB_colormanagement_setup_glsl_draw_from_space(const ColorManagedViewSettin
                applied_view_settings = &default_view_settings;
        }
 
-       /* RGB curves mapping is not supported on GPU yet. */
-       if (!skip_curves)
-               if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES)
-                       return false;
-
        /* Make sure OCIO processor is up-to-date. */
        update_glsl_display_processor(applied_view_settings, display_settings,
                                      from_colorspace ? from_colorspace->name : global_role_scene_linear);
 
-       return OCIO_setupGLSLDraw(&global_glsl_state.ocio_glsl_state, global_glsl_state.processor, predivide);
+       return OCIO_setupGLSLDraw(&global_glsl_state.ocio_glsl_state, global_glsl_state.processor,
+                                 global_glsl_state.use_curve_mapping ? &global_glsl_state.curve_mapping_settings : NULL,
+                                 predivide);
 }
 
 /* Configures GLSL shader for conversion from scene linear to display space */
 bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings,
                                          const ColorManagedDisplaySettings *display_settings,
-                                         bool predivide, bool skip_curves)
+                                         bool predivide)
 {
        return IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
-                                                             NULL, predivide, skip_curves);
+                                                             NULL, predivide);
 }
 
 /* Same as setup_glsl_draw_from_space, but color management settings are guessing from a given context */
@@ -3043,7 +3118,7 @@ bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const struct bContext *C
 
        IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
 
-       return IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings, from_colorspace, predivide, false);
+       return IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings, from_colorspace, predivide);
 }
 
 /* Same as setup_glsl_draw, but color management settings are guessing from a given context */
@@ -3082,7 +3157,7 @@ bool IMB_colormanagement_setup_transform_from_role_glsl(int role, bool predivide
 
        processor = colorspace_to_scene_linear_processor(colorspace);
 
-       return OCIO_setupGLSLDraw(&global_glsl_state.transform_ocio_glsl_state, processor, predivide);
+       return OCIO_setupGLSLDraw(&global_glsl_state.transform_ocio_glsl_state, processor, NULL, predivide);
 }
 
 /* Finish GLSL-based color space conversion */
index 5b809a5170505109890745062dabff525321adc1..7ebbcf7b39b2de85ec897a4f289d810d54135e1c 100644 (file)
@@ -66,14 +66,14 @@ static void engine_tag_update(RenderEngine *engine)
 
 static int engine_support_display_space_shader(RenderEngine *UNUSED(engine), Scene *scene)
 {
-       return IMB_colormanagement_support_glsl_draw(&scene->view_settings, true);
+       return IMB_colormanagement_support_glsl_draw(&scene->view_settings);
 }
 
 static void engine_bind_display_space_shader(RenderEngine *UNUSED(engine), Scene *scene)
 {
        IMB_colormanagement_setup_glsl_draw(&scene->view_settings,
                                            &scene->display_settings,
-                                           false, true);
+                                           false);
 }
 
 static void engine_unbind_display_space_shader(RenderEngine *UNUSED(engine))