[Bf-blender-cvs] [7d8a1863354] blender-v2.82-release: Fix T73133: UDIM texture count in Eevee is limited by OpenGL

Lukas Stockner noreply at git.blender.org
Thu Jan 16 02:13:30 CET 2020


Commit: 7d8a186335400cb7ad69d24aab89e7a215a70348
Author: Lukas Stockner
Date:   Tue Jan 14 00:33:21 2020 +0100
Branches: blender-v2.82-release
https://developer.blender.org/rB7d8a186335400cb7ad69d24aab89e7a215a70348

Fix T73133: UDIM texture count in Eevee is limited by OpenGL

Based on @fclem's suggestion in D6421, this commit implements support for
storing all tiles of a UDIM texture in a single 2D array texture on the GPU.

Previously, Eevee was binding one OpenGL texture per tile, quickly running
into hardware limits with nontrivial UDIM texture sets.
Workbench meanwhile had no UDIM support at all, as reusing the per-tile
approach would require splitting the mesh by tile as well as texture.

With this commit, both Workbench as well as Eevee now support huge numbers
of tiles, with the eventual limits being GPU memory and ultimately
GL_MAX_ARRAY_TEXTURE_LAYERS, which tends to be in the 1000s on modern GPUs.

Initially my plan was to have one array texture per unique size, but managing
the different textures and keeping everything consistent ended up being way
too complex.

Therefore, we now use a simpler version that allocates a texture that
is large enough to fit the largest tile and then packs all tiles into as many
layers as necessary.

As a result, each UDIM texture only binds two textures (one for the actual
images, one for metadata) regardless of how many tiles are used.

Note that this rolls back per-tile GPUTextures, meaning that we again have
per-Image GPUTextures like we did before the original UDIM commit,
but now with four instead of two types.

Reviewed By: fclem

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

===================================================================

M	source/blender/blenkernel/intern/image.c
M	source/blender/blenlib/BLI_boxpack_2d.h
M	source/blender/blenlib/intern/boxpack_2d.c
M	source/blender/blenlib/intern/math_vector_inline.c
M	source/blender/blenloader/intern/readfile.c
M	source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
M	source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
M	source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
M	source/blender/draw/engines/workbench/workbench_deferred.c
M	source/blender/draw/engines/workbench/workbench_forward.c
M	source/blender/draw/engines/workbench/workbench_materials.c
M	source/blender/draw/engines/workbench/workbench_private.h
M	source/blender/draw/intern/draw_manager_data.c
M	source/blender/gpu/GPU_material.h
M	source/blender/gpu/intern/gpu_codegen.c
M	source/blender/gpu/intern/gpu_codegen.h
M	source/blender/gpu/intern/gpu_draw.c
M	source/blender/gpu/intern/gpu_texture.c
M	source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl
M	source/blender/makesdna/DNA_image_types.h
M	source/blender/makesdna/DNA_movieclip_types.h
M	source/blender/makesrna/intern/rna_image.c
M	source/blender/makesrna/intern/rna_image_api.c
M	source/blender/nodes/shader/nodes/node_shader_tex_environment.c
M	source/blender/nodes/shader/nodes/node_shader_tex_image.c

===================================================================

diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 6d6e5166e1c..f748ad64bc4 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -441,10 +441,9 @@ void BKE_image_copy_data(Main *UNUSED(bmain), Image *ima_dst, const Image *ima_s
   BLI_listbase_clear(&ima_dst->anims);
 
   BLI_duplicatelist(&ima_dst->tiles, &ima_src->tiles);
-  LISTBASE_FOREACH (ImageTile *, tile, &ima_dst->tiles) {
-    for (int i = 0; i < TEXTARGET_COUNT; i++) {
-      tile->gputexture[i] = NULL;
-    }
+
+  for (int i = 0; i < TEXTARGET_COUNT; i++) {
+    ima_dst->gputexture[i] = NULL;
   }
 
   if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
@@ -510,11 +509,9 @@ bool BKE_image_scale(Image *image, int width, int height)
 
 bool BKE_image_has_opengl_texture(Image *ima)
 {
-  LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
-    for (int i = 0; i < TEXTARGET_COUNT; i++) {
-      if (tile->gputexture[i] != NULL) {
-        return true;
-      }
+  for (int i = 0; i < TEXTARGET_COUNT; i++) {
+    if (ima->gputexture[i] != NULL) {
+      return true;
     }
   }
   return false;
@@ -3293,9 +3290,16 @@ void BKE_image_init_imageuser(Image *ima, ImageUser *iuser)
 static void image_free_tile(Image *ima, ImageTile *tile)
 {
   for (int i = 0; i < TEXTARGET_COUNT; i++) {
-    if (tile->gputexture[i] != NULL) {
-      GPU_texture_free(tile->gputexture[i]);
-      tile->gputexture[i] = NULL;
+    /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other
+     * two. */
+    if (tile != ima->tiles.first &&
+        !(ELEM(i, TEXTARGET_TEXTURE_2D_ARRAY, TEXTARGET_TEXTURE_TILE_MAPPING))) {
+      continue;
+    }
+
+    if (ima->gputexture[i] != NULL) {
+      GPU_texture_free(ima->gputexture[i]);
+      ima->gputexture[i] = NULL;
     }
   }
 
@@ -3560,6 +3564,16 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la
     BLI_strncpy(tile->label, label, sizeof(tile->label));
   }
 
+  /* Reallocate GPU tile array. */
+  if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY] != NULL) {
+    GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]);
+    ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY] = NULL;
+  }
+  if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING] != NULL) {
+    GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING]);
+    ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING] = NULL;
+  }
+
   return tile;
 }
 
diff --git a/source/blender/blenlib/BLI_boxpack_2d.h b/source/blender/blenlib/BLI_boxpack_2d.h
index 626a00b50fd..b519a920a77 100644
--- a/source/blender/blenlib/BLI_boxpack_2d.h
+++ b/source/blender/blenlib/BLI_boxpack_2d.h
@@ -24,6 +24,8 @@
  * \ingroup bli
  */
 
+struct ListBase;
+
 /* Box Packer */
 
 typedef struct BoxPack {
@@ -44,4 +46,15 @@ void BLI_box_pack_2d(BoxPack *boxarray,
                      float *tot_width,
                      float *tot_height);
 
+typedef struct FixedSizeBoxPack {
+  struct FixedSizeBoxPack *next, *prev;
+  int x, y;
+  int w, h;
+} FixedSizeBoxPack;
+
+void BLI_box_pack_2d_fixedarea(struct ListBase *boxes,
+                               int width,
+                               int height,
+                               struct ListBase *packed);
+
 #endif /* __BLI_BOXPACK_2D_H__ */
diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c
index ddc7f9ee4c7..8a2427a32a8 100644
--- a/source/blender/blenlib/intern/boxpack_2d.c
+++ b/source/blender/blenlib/intern/boxpack_2d.c
@@ -24,6 +24,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_listbase.h"
 #include "BLI_boxpack_2d.h" /* own include */
 
 #include "BLI_sort.h" /* qsort_r */
@@ -673,3 +674,110 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r
   MEM_freeN(vertex_pack_indices);
   MEM_freeN(vs_ctx.vertarray);
 }
+
+/* Packs boxes into a fixed area.
+ * boxes and packed are linked lists containing structs that can be cast to
+ * FixedSizeBoxPack (i.e. contains a FixedSizeBoxPack as its first element).
+ * Boxes that were packed successfully are placed into *packed and removed from *boxes.
+ *
+ * The algorithm is a simplified version of https://github.com/TeamHypersomnia/rectpack2D.
+ * Better ones could be used, but for the current use case (packing Image tiles into GPU
+ * textures) this is fine.
+ *
+ * Note that packing efficiency depends on the order of the input boxes. Generally speaking,
+ * larger boxes should come first, though how exactly size is best defined (e.g. area,
+ * perimeter) depends on the particular application. */
+void BLI_box_pack_2d_fixedarea(ListBase *boxes, int width, int height, ListBase *packed)
+{
+  ListBase spaces = {NULL};
+  FixedSizeBoxPack *full_rect = MEM_callocN(sizeof(FixedSizeBoxPack), __func__);
+  full_rect->w = width;
+  full_rect->h = height;
+
+  BLI_addhead(&spaces, full_rect);
+
+  /* The basic idea of the algorithm is to keep a list of free spaces in the packing area.
+   * Then, for each box to be packed, we try to find a space that can contain it.
+   * The found space is then split into the area that is occupied by the box and the
+   * remaining area, which is reinserted into the free space list.
+   * By inserting the smaller remaining spaces first, the algorithm tries to use these
+   * smaller spaces first instead of "wasting" a large space. */
+  LISTBASE_FOREACH_MUTABLE (FixedSizeBoxPack *, box, boxes) {
+    LISTBASE_FOREACH (FixedSizeBoxPack *, space, &spaces) {
+      /* Skip this space if it's too small. */
+      if (box->w > space->w || box->h > space->w) {
+        continue;
+      }
+
+      /* Pack this box into this space. */
+      box->x = space->x;
+      box->y = space->y;
+      BLI_remlink(boxes, box);
+      BLI_addtail(packed, box);
+
+      if (box->w == space->w && box->h == space->h) {
+        /* Box exactly fills space, so just remove the space. */
+        BLI_remlink(&spaces, space);
+        MEM_freeN(space);
+      }
+      else if (box->w == space->w) {
+        /* Box fills the entire width, so we can just contract the box
+         * to the upper part that remains. */
+        space->y += box->h;
+        space->h -= box->h;
+      }
+      else if (box->h == space->h) {
+        /* Box fills the entire height, so we can just contract the box
+         * to the right part that remains. */
+        space->x += box->w;
+        space->w -= box->w;
+      }
+      else {
+        /* Split the remaining L-shaped space into two spaces.
+         * There are two ways to do so, we pick the one that produces the biggest
+         * remaining space:
+         *
+         *  Horizontal Split            Vertical Split
+         * ###################        ###################
+         * #                 #        #       -         #
+         * #      Large      #        # Small -         #
+         * #                 #        #       -         #
+         * #********---------#        #********  Large  #
+         * #  Box  *  Small  #        #  Box  *         #
+         * #       *         #        #       *         #
+         * ###################        ###################
+         *
+         */
+        int area_hsplit_large = space->w * (space->h - box->h);
+        int area_vsplit_large = (space->w - box->w) * space->h;
+
+        /* Perform split. This space becomes the larger space,
+         * while the new smaller space is inserted _before_ it. */
+        FixedSizeBoxPack *new_space = MEM_callocN(sizeof(FixedSizeBoxPack), __func__);
+        if (area_hsplit_large > area_vsplit_large) {
+          new_space->x = space->x + box->w;
+          new_space->y = space->y;
+          new_space->w = space->w - box->w;
+          new_space->h = box->h;
+
+          space->y += box->h;
+          space->h -= box->h;
+        }
+        else {
+          new_space->x = space->x;
+          new_space->y = space->y + box->h;
+          new_space->w = box->w;
+          new_space->h = space->h - box->h;
+
+          space->x += box->w;
+          space->w -= box->w;
+        }
+        BLI_addhead(&spaces, new_space);
+      }
+
+      break;
+    }
+  }
+
+  BLI_freelistN(&spaces);
+}
\ No newline at end of file
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index 67bc5c2fa50..caa38c9cf08 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -169,6 +169,12 @@ MINLINE void copy_v4_v4_short(short r[4], const short a[4])
 }
 
 /* int */
+MINLINE void zero_v2_int(int r[2])
+{
+  r[0] = 0;
+  r[1] = 0;
+}
+
 MINLINE void zero_v3_int(int r[3])
 {
   r[0] = 0;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 086f1db117d..0ebff916cf9 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1910,11 +1910,9 @@ void blo_make_image_pointer_map(FileData *fd, Main *oldmain)
     if (ima->cache) {
       oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0);
     }
-    LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
-      for (a = 0; a < TEXTARGET_COUNT; a++) {
-        if (tile->gputexture[a] != NULL) {
-          oldnewmap_insert(fd->imamap, tile->gputexture[a], tile->gputexture[a], 0);
-        }
+    for (a = 0; a < TEXTARGET_COUNT; a++) {
+      if (ima->gputexture[a] != NULL) {
+        oldnewmap_insert(fd->imamap, ima->gputexture[a], ima->gputexture[a], 0);
       }
     }
     if (ima->rr) {
@@ -1958,10 +1956,8 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
     if (ima->cache == NULL) {
       ima->gpuflag = 0;
       ima->gpuframenr = INT_MAX;
-      LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
-        for (i = 0; i < TEXTARGET_COUNT; i++) {
-          tile->gputexture[i] = NULL;
-        }
+      for (i = 0; i < TEXTARGET_COUNT; i++) {
+        ima->gputexture[i] = NULL;
       }
       ima->rr = NULL;
     }
@@ -1969,10 +1965,8 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
       slot->render = newimaadr(fd, slot->render

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list