More usage of GLSL for color managed image drawing
authorSergey Sharybin <sergey.vfx@gmail.com>
Tue, 2 Apr 2013 17:28:37 +0000 (17:28 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Tue, 2 Apr 2013 17:28:37 +0000 (17:28 +0000)
Uses GLSL for drawing image in Image Editor space.

This requires change in image_buffer_rect_update, so
original float buffer is being updated as well. This
is unlikely be something bad, but will keep an eye
on this change.

Also no byte buffer allocation happens there, this
is so because byte buffer used for display only
and in case of GLSL display such allocation and
partial update is just waste of time.

Also switched OpenGL render from using CPU color
space linearization to GLSL color space transform.
Makes OpenGL rendering pretty much faster (but
still slower than in 2.60).

internal changes:

- Added functions to setup GLSL shader for color
  space conversion in colormanagement.c. Currently
  conversion form a colorspace defined by a role to
  linear space is implemented. Easy to extend to
  other cases.

- Added helper functions to glutil.c which does
  smarter image buffer draw (calling all needed OCIO
  stuff, editors now could draw image buffer with a
  single function call -- all the checks are done in
  glutil.c).

- Also added helper function for buffer linearization
  from a given role to glutil.c. Everyone now able to
  linearize buffer with a single call.

  This function will do nothing is GLSL routines fails
  or not supported.

  And one last this: this function uses offscreen
  drawing, could potentially give issues on some
  cards, also will keep an eye on this.

source/blender/editors/include/BIF_glutil.h
source/blender/editors/render/render_internal.c
source/blender/editors/render/render_opengl.c
source/blender/editors/screen/CMakeLists.txt
source/blender/editors/screen/glutil.c
source/blender/editors/space_clip/clip_draw.c
source/blender/editors/space_image/image_draw.c
source/blender/imbuf/IMB_colormanagement.h
source/blender/imbuf/intern/colormanagement.c

index 5774a057eb5f0d1aa753307817340dd8240912eb..650e4527196f4f4cd82c31b3434d431138e60ed3 100644 (file)
@@ -33,6 +33,9 @@
 struct rcti;
 struct rctf;
 
+struct ImBuf;
+struct bContext;
+
 void fdrawbezier(float vec[4][3]);
 void fdrawline(float x1, float y1, float x2, float y2);
 void fdrawbox(float x1, float y1, float x2, float y2);
@@ -223,5 +226,13 @@ typedef struct bglMats {
 } bglMats;
 void bgl_get_mats(bglMats *mats);
 
+/* **** Color management helper functions for GLSL display/transform ***** */
+
+/* Draw imbuf on a screen, preferably using GLSL display transform */
+void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter);
+
+/* Transform buffer from role to scene linear space using GLSL OCIO conversion */
+int glaBufferTransformFromRole_glsl(float *buffer, int width, int height, int role);
+
 #endif /* __BIF_GLUTIL_H__ */
 
index 365ac02d15b116d506ef9ac43040730e86548c1c..de8accf0180656dced1b02fcdbbcde3f6ce1ed15 100644 (file)
@@ -140,15 +140,23 @@ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volat
                }
        }
        if (rectf == NULL) return;
-
-       if (ibuf->rect == NULL)
-               imb_addrectImBuf(ibuf);
        
        rectf += 4 * (rr->rectx * ymin + xmin);
 
-       IMB_partial_display_buffer_update(ibuf, rectf, NULL, rr->rectx, rxmin, rymin,
-                                         &scene->view_settings, &scene->display_settings,
-                                         rxmin, rymin, rxmin + xmax, rymin + ymax, TRUE);
+       if (ibuf->rect) {
+               IMB_partial_display_buffer_update(ibuf, rectf, NULL, rr->rectx, rxmin, rymin,
+                                                 &scene->view_settings, &scene->display_settings,
+                                                 rxmin, rymin, rxmin + xmax, rymin + ymax, TRUE);
+       }
+
+       /* update float buffer as well, so fast GLSL display could use it
+        *
+        * TODO(sergey): not actually sure it is nice thing to modify something here
+        *               but ibuf->rect used to be modified here
+        */
+       IMB_buffer_float_from_float(ibuf->rect_float + 4 * (ibuf->x * rymin + rxmin), rectf,
+                                   4, IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, FALSE,
+                                   xmax, ymax, ibuf->x, rr->rectx);
 }
 
 /* ****************************** render invoking ***************** */
index f47d737beca8897e697168d7d392e05fe1ba3acf..9d443fab5529fbbdb2ad2d4e51438ec402a6fc1e 100644 (file)
@@ -69,6 +69,8 @@
 #include "RNA_access.h"
 #include "RNA_define.h"
 
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
 
 #include "GPU_extensions.h"
 
@@ -261,11 +263,14 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
         */
 
        if (!oglrender->is_sequencer) {
-               /* sequencer has got tricker ocnversion happened above */
-
-               IMB_buffer_float_from_float(rr->rectf, rr->rectf,
-                                           4, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, TRUE,
-                                           oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex);
+               /* sequencer has got trickier conversion happened above
+                * also assume opengl's space matches byte buffer color space
+                */
+               if (!glaBufferTransformFromRole_glsl(rr->rectf, oglrender->sizex, oglrender->sizey, COLOR_ROLE_DEFAULT_BYTE)) {
+                       IMB_buffer_float_from_float(rr->rectf, rr->rectf,
+                                                   4, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, TRUE,
+                                                   oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex);
+               }
        }
 
        /* rr->rectf is now filled with image data */
index 33373354aa482a737b490dc7e194fee936039d3c..c9c4c253e28ae605c563af4d1bbc86a4023583e9 100644 (file)
@@ -25,6 +25,7 @@ set(INC
        ../../blenlib
        ../../blenloader
        ../../bmesh
+       ../../gpu
        ../../imbuf
        ../../makesdna
        ../../makesrna
index 89315e041de7e1ab858ab2b5f5c7b3c51ba6e31c..fecb3c58a2d311cf8a5147676fed5454cf8c6218 100644 (file)
 
 #include "BKE_blender.h"
 #include "BKE_colortools.h"
+#include "BKE_context.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
 
+#include "GPU_extensions.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf_types.h"
+
 #ifndef GL_CLAMP_TO_EDGE
 #define GL_CLAMP_TO_EDGE                        0x812F
 #endif
@@ -983,3 +989,89 @@ void bglFlush(void)
 #endif
 }
 #endif
+
+/* **** Color management helper functions for GLSL display/transform ***** */
+
+/* Draw given image buffer on a screen using GLSL for display transform */
+void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter)
+{
+       bool need_fallback = true;
+
+       /* Bytes and dithering are not supported on GLSL yet */
+       if (ibuf->rect_float && ibuf->dither == 0.0f) {
+               if (IMB_colormanagement_setup_glsl_draw_from_ctx(C)) {
+                       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+                       glColor4f(1.0, 1.0, 1.0, 1.0);
+
+                       glaDrawPixelsTex(x, y, ibuf->x, ibuf->y, GL_FLOAT, zoomfilter, ibuf->rect_float);
+
+                       IMB_colormanagement_finish_glsl_draw();
+
+                       need_fallback = false;
+               }
+       }
+
+       if (need_fallback) {
+               unsigned char *display_buffer;
+               void *cache_handle;
+
+               display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
+
+               if (display_buffer)
+                       glaDrawPixelsAuto(x, y, ibuf->x, ibuf->y, GL_UNSIGNED_BYTE, zoomfilter, display_buffer);
+
+               IMB_display_buffer_release(cache_handle);
+       }
+}
+
+/* Transform buffer from role to scene linear space using GLSL OCIO conversion
+ *
+ * See IMB_colormanagement_setup_transform_from_role_glsl description for
+ * some more details
+ */
+int glaBufferTransformFromRole_glsl(float *buffer, int width, int height, int role)
+{
+       GPUOffScreen *ofs;
+       char err_out[256];
+       rcti display_rect;
+
+       ofs = GPU_offscreen_create(width, height, err_out);
+
+       if (!ofs)
+               return FALSE;
+
+       GPU_offscreen_bind(ofs);
+
+       if (!IMB_colormanagement_setup_transform_from_role_glsl(role)) {
+               GPU_offscreen_unbind(ofs);
+               GPU_offscreen_free(ofs);
+               return FALSE;
+       }
+
+       BLI_rcti_init(&display_rect, 0, width, 0, height);
+
+       glMatrixMode(GL_PROJECTION);
+       glPushMatrix();
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+
+       glaDefine2DArea(&display_rect);
+       glLoadIdentity();
+
+       glaDrawPixelsTex(0, 0, width, height, GL_FLOAT, GL_NEAREST, buffer);
+
+       glMatrixMode(GL_PROJECTION);
+       glPopMatrix();
+       glMatrixMode(GL_MODELVIEW);
+       glPopMatrix();
+
+       GPU_offscreen_read_pixels(ofs, GL_FLOAT, buffer);
+
+       IMB_colormanagement_finish_glsl_transform();
+
+       /* unbind */
+       GPU_offscreen_unbind(ofs);
+       GPU_offscreen_free(ofs);
+
+       return TRUE;
+}
index cbca2f0c46ebb568267aa1433c9671f987763950..774220764e74e38ebd3a7aef928718762cf8ce25 100644 (file)
@@ -248,53 +248,6 @@ static void draw_movieclip_notes(SpaceClip *sc, ARegion *ar)
                ED_region_info_draw(ar, str, block, 0.6f);
 }
 
-static void draw_movieclip_buffer_glsl(SpaceClip *sc, ImBuf *ibuf, int x, int y,
-                                       float zoomx, float zoomy)
-{
-       MovieClip *clip = ED_space_clip_get_clip(sc);
-       int filter = GL_LINEAR;
-
-       glPushMatrix();
-       glTranslatef(x, y, 0.0f);
-       glScalef(zoomx, zoomy, 1.0f);
-
-       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-
-       glColor4f(1.0, 1.0, 1.0, 1.0);
-
-       /* non-scaled proxy shouldn;t use diltering */
-       if ((clip->flag & MCLIP_USE_PROXY) == 0 ||
-           ELEM(sc->user.render_size, MCLIP_PROXY_RENDER_SIZE_FULL, MCLIP_PROXY_RENDER_SIZE_100))
-       {
-               filter = GL_NEAREST;
-       }
-
-       glaDrawPixelsTex(0, 0, ibuf->x, ibuf->y, GL_FLOAT, filter, ibuf->rect_float);
-
-       glPopMatrix();
-}
-
-static void draw_movieclip_buffer_fallback(const bContext *C, ImBuf *ibuf, int x, int y,
-                                           int width, int height, float zoomx, float zoomy)
-{
-       unsigned char *display_buffer;
-       void *cache_handle;
-
-       display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
-
-       if (display_buffer) {
-               /* set zoom */
-               glPixelZoom(zoomx * width / ibuf->x, zoomy * height / ibuf->y);
-
-               glaDrawPixelsAuto(x, y, ibuf->x, ibuf->y, GL_UNSIGNED_BYTE, GL_NEAREST, display_buffer);
-
-               /* reset zoom */
-               glPixelZoom(1.0f, 1.0f);
-       }
-
-       IMB_display_buffer_release(cache_handle);
-}
-
 static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar, ImBuf *ibuf,
                                   int width, int height, float zoomx, float zoomy)
 {
@@ -308,7 +261,8 @@ static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar,
                glRectf(x, y, x + zoomx * width, y + zoomy * height);
        }
        else {
-               bool need_fallback = true;
+               MovieClip *clip = ED_space_clip_get_clip(sc);
+               int filter = GL_LINEAR;
 
                /* checkerboard for case alpha */
                if (ibuf->planes == 32) {
@@ -318,19 +272,14 @@ static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar,
                        fdrawcheckerboard(x, y, x + zoomx * ibuf->x, y + zoomy * ibuf->y);
                }
 
-               /* GLSL display transform for byte buffers is not supported yet */
-               if (ibuf->rect_float && IMB_coloemanagement_setup_glsl_draw_from_ctx(C)) {
-                       draw_movieclip_buffer_glsl(sc, ibuf, x, y, zoomx, zoomy);
-
-                       IMB_coloemanagement_finish_glsl_draw();
-
-                       need_fallback = false;
-               }
+               /* non-scaled proxy shouldn't use filtering */
+               if ((clip->flag & MCLIP_USE_PROXY) == 0 ||
+                   ELEM(sc->user.render_size, MCLIP_PROXY_RENDER_SIZE_FULL, MCLIP_PROXY_RENDER_SIZE_100))
+                       {
+                               filter = GL_NEAREST;
+                       }
 
-               /* if GLSL display failed, fallback to regular glaDrawPixelsAuto method */
-               if (need_fallback) {
-                       draw_movieclip_buffer_fallback(C, ibuf, x, y, width, height, zoomx, zoomy);
-               }
+               glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST);
 
                if (ibuf->planes == 32)
                        glDisable(GL_BLEND);
index f27a99ac44b40e09731ce28a5bf19dc6e6475f8d..7fc83809b601d44b5202e74caf0552a3aacf2b84 100644 (file)
@@ -505,9 +505,6 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar,
                        sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->rect_float);
        }
        else {
-               unsigned char *display_buffer;
-               void *cache_handle;
-
                if (sima->flag & SI_USE_ALPHA) {
                        glEnable(GL_BLEND);
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -515,12 +512,7 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar,
                        fdrawcheckerboard(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy);
                }
 
-               display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
-
-               if (display_buffer)
-                       glaDrawPixelsAuto(x, y, ibuf->x, ibuf->y, GL_UNSIGNED_BYTE, GL_NEAREST, display_buffer);
-
-               IMB_display_buffer_release(cache_handle);
+               glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST);
 
                if (sima->flag & SI_USE_ALPHA)
                        glDisable(GL_BLEND);
index 473bd7d0c7ae3fdb07e9f860e063487967c80bc4..a758cfa04f045e7a5aeff181efd51d3377660552 100644 (file)
@@ -150,11 +150,18 @@ void IMB_colormanagement_processor_free(struct ColormanageProcessor *cm_processo
 
 /* ** OpenGL drawing routines using GLSL for color space transform ** */
 
-int IMB_coloemanagement_setup_glsl_draw(const struct ColorManagedViewSettings *view_settings,
+/* Configures GLSL shader for conversion from scene linear to display space */
+int IMB_colormanagement_setup_glsl_draw(const struct ColorManagedViewSettings *view_settings,
                                         const struct ColorManagedDisplaySettings *display_settings);
-
-int IMB_coloemanagement_setup_glsl_draw_from_ctx(const struct bContext *C);
-void IMB_coloemanagement_finish_glsl_draw(void);
+/* Same as above, but color management settings are guessing from a given context */
+int IMB_colormanagement_setup_glsl_draw_from_ctx(const struct bContext *C);
+/* Finish GLSL-based display space conversion */
+void IMB_colormanagement_finish_glsl_draw(void);
+
+/* Configures GLSL shader for conversion from space defined by role to scene linear space */
+int IMB_colormanagement_setup_transform_from_role_glsl(int role);
+/* Finish GLSL-based color space conversion */
+void IMB_colormanagement_finish_glsl_transform(void);
 
 /* Roles */
 enum {
index e9447d255cb210b7a0fdd04a163573f01c865ff9..1aa8c5af342bffb10e9359c5816d946a478037f2 100644 (file)
@@ -113,6 +113,7 @@ static struct global_glsl_state {
 
        /* Container for GLSL state needed for OCIO module. */
        struct OCIO_GLSLDrawState *ocio_glsl_state;
+       struct OCIO_GLSLDrawState *transform_ocio_glsl_state;
 } global_glsl_state;
 
 /*********************** Color managed cache *************************/
@@ -626,6 +627,9 @@ void colormanagement_exit(void)
        if (global_glsl_state.ocio_glsl_state)
                OCIO_freeOGLState(global_glsl_state.ocio_glsl_state);
 
+       if (global_glsl_state.transform_ocio_glsl_state)
+               OCIO_freeOGLState(global_glsl_state.transform_ocio_glsl_state);
+
        colormanage_free_config();
 }
 
@@ -2750,7 +2754,20 @@ static void update_glsl_display_processor(const ColorManagedViewSettings *view_s
        }
 }
 
-int IMB_coloemanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings,
+/**
+ * Configures GLSL shader for conversion from scene linear
+ * to display space
+ *
+ * Will create appropriate OCIO processor and setup GLSL shader,
+ * so further 2D texture usage will use this conversion.
+ *
+ * When there's no need to apply transform on 2D textures, use
+ * IMB_colormanagement_finish_glsl_draw().
+ *
+ * This is low-level function, use glaDrawImBuf_glsl_ctx if you
+ * only need to display given image buffer
+ */
+int IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings,
                                         const ColorManagedDisplaySettings *display_settings)
 {
        ColorManagedViewSettings default_view_settings;
@@ -2778,17 +2795,52 @@ int IMB_coloemanagement_setup_glsl_draw(const ColorManagedViewSettings *view_set
        return OCIO_setupGLSLDraw(&global_glsl_state.ocio_glsl_state, global_glsl_state.processor);
 }
 
-int IMB_coloemanagement_setup_glsl_draw_from_ctx(const bContext *C)
+/* Same as above, but color management settings are guessing from a given context */
+int IMB_colormanagement_setup_glsl_draw_from_ctx(const bContext *C)
 {
        ColorManagedViewSettings *view_settings;
        ColorManagedDisplaySettings *display_settings;
 
        display_transform_get_from_ctx(C, &view_settings, &display_settings);
 
-       return IMB_coloemanagement_setup_glsl_draw(view_settings, display_settings);
+       return IMB_colormanagement_setup_glsl_draw(view_settings, display_settings);
 }
 
-void IMB_coloemanagement_finish_glsl_draw(void)
+/* Finish GLSL-based display space conversion */
+void IMB_colormanagement_finish_glsl_draw(void)
 {
        OCIO_finishGLSLDraw(global_glsl_state.ocio_glsl_state);
 }
+
+/* ** Color space conversion using GLSL shader  ** */
+
+/**
+ * Configures GLSL shader for conversion from space defined by role
+ * to scene linear space
+ *
+ * Will create appropriate OCIO processor and setup GLSL shader,
+ * so further 2D texture usage will use this conversion.
+ *
+ * Role is an pseudonym for a color space, see bottom of file
+ * IMB_colormanagement.h for list of available roles.
+ *
+ * When there's no need to apply transform on 2D textures, use
+ * IMB_colormanagement_finish_glsl_transform().
+ */
+int IMB_colormanagement_setup_transform_from_role_glsl(int role)
+{
+       OCIO_ConstProcessorRcPtr *processor;
+       ColorSpace *colorspace;
+
+       colorspace = colormanage_colorspace_get_roled(role);
+
+       processor = colorspace_to_scene_linear_processor(colorspace);
+
+       return OCIO_setupGLSLDraw(&global_glsl_state.transform_ocio_glsl_state, processor);
+}
+
+/* Finish GLSL-based color space conversion */
+void IMB_colormanagement_finish_glsl_transform(void)
+{
+       OCIO_finishGLSLDraw(global_glsl_state.transform_ocio_glsl_state);
+}