GPU: Add Image property to allow high bitdepth support on a per image basis
authorClément Foucault <foucault.clem@gmail.com>
Tue, 25 Feb 2020 14:05:53 +0000 (15:05 +0100)
committerClément Foucault <foucault.clem@gmail.com>
Tue, 25 Feb 2020 14:14:32 +0000 (15:14 +0100)
This adds the `Half Float Precision` option in the image property panel.
This option is only available on float textures and is enabled by default.

Adding a flag inside the imbuf (IB_halffloat) on load is done for EXR and PSD formats that can store half floating point (16bits/channels).
The option is then not displayed in this case and forced.

Related task T73086

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D6891

source/blender/blenloader/intern/versioning_280.c
source/blender/editors/space_image/image_buttons.c
source/blender/gpu/GPU_draw.h
source/blender/gpu/intern/gpu_draw.c
source/blender/imbuf/IMB_imbuf_types.h
source/blender/imbuf/intern/oiio/openimageio_api.cpp
source/blender/imbuf/intern/openexr/openexr_api.cpp
source/blender/makesdna/DNA_image_types.h
source/blender/makesrna/intern/rna_image.c

index d34c15161fb6c25dff0e8d9c366532c31c530d73..d0fad1aafab4299eb6e1a19cdfd289bf5eaae83b 100644 (file)
@@ -3364,7 +3364,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
     }
 
     for (Image *image = bmain->images.first; image; image = image->id.next) {
-      image->flag &= ~(IMA_FLAG_UNUSED_0 | IMA_FLAG_UNUSED_1 | IMA_FLAG_UNUSED_4 |
+      image->flag &= ~(IMA_HIGH_BITDEPTH | IMA_FLAG_UNUSED_1 | IMA_FLAG_UNUSED_4 |
                        IMA_FLAG_UNUSED_6 | IMA_FLAG_UNUSED_8 | IMA_FLAG_UNUSED_15 |
                        IMA_FLAG_UNUSED_16);
     }
index 270fe0c59dcd0e419e88aa8713cf622070a5b013..f4688ce17fd6b49f96675290354b68593c19e578 100644 (file)
@@ -980,6 +980,16 @@ void uiTemplateImage(uiLayout *layout,
           bool is_data = IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name);
           uiLayoutSetActive(sub, !is_data);
         }
+
+        if (ima && iuser) {
+          void *lock;
+          ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
+
+          if (ibuf->rect_float && (ibuf->flags & IB_halffloat) == 0) {
+            uiItemR(col, &imaptr, "use_half_precision", 0, NULL, ICON_NONE);
+          }
+          BKE_image_release_ibuf(ima, ibuf, lock);
+        }
       }
 
       uiItemR(col, &imaptr, "use_view_as_render", 0, NULL, ICON_NONE);
index f89a76cf49cc8131cf4b0919c7b6358ebe44cd62..cc681c02009f52b0333d1a54fb112cccc016a3cb 100644 (file)
@@ -71,6 +71,7 @@ void GPU_create_gl_tex(unsigned int *bind,
                        int recth,
                        int textarget,
                        bool mipmap,
+                       bool half_float,
                        bool use_srgb,
                        struct Image *ima);
 void GPU_create_gl_tex_compressed(unsigned int *bind,
index 6afff4f68f15997b43aaadef8821f6131d2df9f8..62a5de7ebe67a2e8812078597293a89c8a781534 100644 (file)
@@ -328,7 +328,9 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
   GLenum data_type, internal_format;
   if (main_ibuf->rect_float) {
     data_type = GL_FLOAT;
-    internal_format = GL_RGBA16F;
+    internal_format = (!(main_ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)) ?
+                          GL_RGBA32F :
+                          GL_RGBA16F;
   }
   else {
     data_type = GL_UNSIGNED_BYTE;
@@ -472,6 +474,7 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget)
 {
   uint bindcode = 0;
   const bool mipmap = GPU_get_mipmap();
+  const bool half_float = (ibuf->flags & IB_halffloat) != 0;
 
 #ifdef WITH_DDS
   if (ibuf->ftype == IMB_FTYPE_DDS) {
@@ -536,6 +539,7 @@ static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget)
                     ibuf->y,
                     textarget,
                     mipmap,
+                    half_float,
                     compress_as_srgb,
                     ima);
 
@@ -1043,6 +1047,7 @@ void GPU_create_gl_tex(uint *bind,
                        int recth,
                        int textarget,
                        bool mipmap,
+                       bool half_float,
                        bool use_srgb,
                        Image *ima)
 {
@@ -1072,7 +1077,8 @@ void GPU_create_gl_tex(uint *bind,
   glGenTextures(1, (GLuint *)bind);
   glBindTexture(textarget, *bind);
 
-  GLenum internal_format = (frect) ? GL_RGBA16F : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
+  GLenum float_format = (!half_float && ima->flag & IMA_HIGH_BITDEPTH) ? GL_RGBA32F : GL_RGBA16F;
+  GLenum internal_format = (frect) ? float_format : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
 
   if (textarget == GL_TEXTURE_2D) {
     if (frect) {
@@ -1236,18 +1242,21 @@ void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima,
   const bool use_srgb = !(IMB_colormanagement_space_is_data(ibuf->rect_colorspace) ||
                           IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
   const bool mipmap = GPU_get_mipmap();
+  const bool half_float = (ibuf->flags & IB_halffloat) != 0;
 
 #ifndef WITH_DDS
   (void)ibuf;
   /* Fall back to uncompressed if DDS isn't enabled */
-  GPU_create_gl_tex(bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, use_srgb, ima);
+  GPU_create_gl_tex(
+      bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima);
 #else
   glGenTextures(1, (GLuint *)bind);
   glBindTexture(textarget, *bind);
 
   if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf, use_srgb) == 0) {
     glDeleteTextures(1, (GLuint *)bind);
-    GPU_create_gl_tex(bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, use_srgb, ima);
+    GPU_create_gl_tex(
+        bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima);
   }
 
   glBindTexture(textarget, 0);
index 61aa1f401a0be2e9d98d036ee166a11bfc36f9c9..0568c425e78305e6842c2fa4f47f975c339820ad 100644 (file)
@@ -291,6 +291,7 @@ enum {
   IB_alphamode_ignore = 1 << 15,
   IB_thumbnail = 1 << 16,
   IB_multiview = 1 << 17,
+  IB_halffloat = 1 << 18,
 };
 
 /** \} */
index e001b8b21c4b84f399949d555bc8969a597f4915..4746cec41d457a140f6fc6cb69d258a2666c6ce4 100644 (file)
@@ -194,7 +194,7 @@ struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspac
 {
   struct ImBuf *ibuf = NULL;
   int width, height, components;
-  bool is_float, is_alpha;
+  bool is_float, is_alpha, is_half;
   int basesize;
   char file_colorspace[IM_MAX_SPACE];
   const bool is_colorspace_manually_set = (colorspace[0] != '\0');
@@ -243,6 +243,7 @@ struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspac
   is_alpha = spec.alpha_channel != -1;
   basesize = spec.format.basesize();
   is_float = basesize > 1;
+  is_half = spec.format == TypeDesc::HALF;
 
   /* we only handle certain number of components */
   if (!(components >= 1 && components <= 4)) {
@@ -271,6 +272,7 @@ struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspac
   ibuf->ftype = IMB_FTYPE_PSD;
   ibuf->channels = 4;
   ibuf->planes = (3 + (is_alpha ? 1 : 0)) * 4 << basesize;
+  ibuf->flags |= (is_float && is_half) ? IB_halffloat : 0;
 
   try {
     return ibuf;
index e1513169736bb95d91ac6d69dd753670a60ec6ec..9fa2c7a28c5908254fd53172fcb855a320ce14e3 100644 (file)
@@ -1757,6 +1757,18 @@ static bool exr_has_alpha(MultiPartInputFile &file)
   return !(file.header(0).channels().findChannel("A") == NULL);
 }
 
+static bool exr_is_half_float(MultiPartInputFile &file)
+{
+  const ChannelList &channels = file.header(0).channels();
+  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
+    const Channel &channel = i.channel();
+    if (channel.type != HALF) {
+      return false;
+    }
+  }
+  return true;
+}
+
 static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
 {
   const ChannelList &channels = file.header(0).channels();
@@ -1909,6 +1921,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
       const int is_alpha = exr_has_alpha(*file);
 
       ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
+      ibuf->flags |= exr_is_half_float(*file) ? IB_halffloat : 0;
 
       if (hasXDensity(file->header(0))) {
         ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f;
index 73a9e7f8a0a374323a511b5c7477ee292c6fa017..03de4fb04739698d879af624863f7176a11bd221 100644 (file)
@@ -192,7 +192,7 @@ typedef struct Image {
 
 /* Image.flag */
 enum {
-  IMA_FLAG_UNUSED_0 = (1 << 0), /* cleared */
+  IMA_HIGH_BITDEPTH = (1 << 0),
   IMA_FLAG_UNUSED_1 = (1 << 1), /* cleared */
 #ifdef DNA_DEPRECATED_ALLOW
   IMA_DO_PREMUL = (1 << 2),
index 9f5f1635536657ff9dcfc211d9caa96f5d3b6e75..94b5786665c9c114f267aed018850dba9592f615 100644 (file)
@@ -193,6 +193,17 @@ static char *rna_ImageUser_path(PointerRNA *ptr)
   return BLI_strdup("");
 }
 
+static void rna_Image_gpu_texture_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+  Image *ima = (Image *)ptr->owner_id;
+
+  if (!G.background) {
+    GPU_free_image(ima);
+  }
+
+  WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id);
+}
+
 static const EnumPropertyItem *rna_Image_source_itemf(bContext *UNUSED(C),
                                                       PointerRNA *ptr,
                                                       PropertyRNA *UNUSED(prop),
@@ -1119,6 +1130,13 @@ static void rna_def_image(BlenderRNA *brna)
                            "when saving and loading the image");
   RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_colormanage_update");
 
+  prop = RNA_def_property(srna, "use_half_precision", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", IMA_HIGH_BITDEPTH);
+  RNA_def_property_ui_text(prop,
+                           "Half Float Precision",
+                           "Use 16bits per channel to lower the memory usage during rendering");
+  RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_gpu_texture_update");
+
   /* multiview */
   prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
   RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);