[Bf-blender-cvs] [854d3d4645e] temp-texture-painting-gpu: Painting first pixels to intermediate buffer.

Jeroen Bakker noreply at git.blender.org
Fri Sep 30 15:50:29 CEST 2022


Commit: 854d3d4645ed2ab6475ce399c39189df29ba0a0d
Author: Jeroen Bakker
Date:   Wed Sep 28 13:04:18 2022 +0200
Branches: temp-texture-painting-gpu
https://developer.blender.org/rB854d3d4645ed2ab6475ce399c39189df29ba0a0d

Painting first pixels to intermediate buffer.

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

M	source/blender/blenkernel/BKE_pbvh_pixels.hh
M	source/blender/blenkernel/intern/pbvh_pixels.cc
M	source/blender/editors/sculpt_paint/CMakeLists.txt
M	source/blender/editors/sculpt_paint/sculpt_intern.h
M	source/blender/editors/sculpt_paint/sculpt_paint_image.cc
A	source/blender/editors/sculpt_paint/sculpt_shaders.cc
M	source/blender/gpu/shaders/sculpt_paint/infos/sculpt_paint_image_info.hh
M	source/blender/gpu/shaders/sculpt_paint/sculpt_paint_image_comp.glsl

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

diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh
index da6dc368536..ca82418fb79 100644
--- a/source/blender/blenkernel/BKE_pbvh_pixels.hh
+++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh
@@ -17,6 +17,7 @@
 #include "IMB_imbuf_types.h"
 
 #include "GPU_sculpt_shader_shared.h"
+#include "GPU_storage_buffer.h"
 
 namespace blender::bke::pbvh::pixels {
 
@@ -29,6 +30,7 @@ namespace blender::bke::pbvh::pixels {
 struct Triangles {
   /** Data accessed by the inner loop of the painting brush. */
   Vector<TrianglePaintInput> paint_input;
+  GPUStorageBuf *gpu_buffer = nullptr;
 
  public:
   void append(const int3 vert_indices)
@@ -49,11 +51,15 @@ struct Triangles {
     return paint_input[index];
   }
 
-  void clear()
+  ~Triangles()
   {
-    paint_input.clear();
+    clear();
   }
 
+  /** Clear data associated with self. */
+  void clear();
+  void ensure_gpu_buffer();
+
   uint64_t size() const
   {
     return paint_input.size();
@@ -94,6 +100,7 @@ struct UDIMTilePixels {
   rcti dirty_region;
 
   Vector<PackedPixelRow> pixel_rows;
+  int64_t gpu_buffer_offset;
 
   UDIMTilePixels()
   {
@@ -134,11 +141,22 @@ struct NodeData {
   Vector<UDIMTileUndo> undo_regions;
   Triangles triangles;
 
+  struct {
+    /** Contains GPU buffer for all associated pixels. Tiles have a range inside this buffer
+     * (#UDIMTilePixels.start_index, #UDIMTilePixels.end_index). */
+    GPUStorageBuf *pixels = nullptr;
+  } gpu_buffers;
+
   NodeData()
   {
     flags.dirty = false;
   }
 
+  ~NodeData()
+  {
+    clear_data();
+  }
+
   UDIMTilePixels *find_tile_data(const image::ImageTileWrapper &image_tile)
   {
     for (UDIMTilePixels &tile : tiles) {
@@ -186,6 +204,18 @@ struct NodeData {
   {
     tiles.clear();
     triangles.clear();
+    if (gpu_buffers.pixels) {
+      GPU_storagebuf_free(gpu_buffers.pixels);
+      gpu_buffers.pixels = nullptr;
+    }
+  }
+
+  void ensure_gpu_buffers()
+  {
+    triangles.ensure_gpu_buffer();
+    if (gpu_buffers.pixels == nullptr) {
+      build_pixels_gpu_buffer();
+    }
   }
 
   static void free_func(void *instance)
@@ -193,6 +223,9 @@ struct NodeData {
     NodeData *node_data = static_cast<NodeData *>(instance);
     MEM_delete(node_data);
   }
+
+ private:
+  void build_pixels_gpu_buffer();
 };
 
 NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node);
diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc
index f733f3145ec..fdae9d51986 100644
--- a/source/blender/blenkernel/intern/pbvh_pixels.cc
+++ b/source/blender/blenkernel/intern/pbvh_pixels.cc
@@ -23,6 +23,60 @@
 
 namespace blender::bke::pbvh::pixels {
 
+void Triangles::clear()
+{
+  paint_input.clear();
+  if (gpu_buffer) {
+    GPU_storagebuf_free(gpu_buffer);
+    gpu_buffer = nullptr;
+  }
+}
+
+void Triangles::ensure_gpu_buffer()
+{
+  if (gpu_buffer) {
+    return;
+  }
+  gpu_buffer = GPU_storagebuf_create_ex(
+      mem_size(), paint_input.data(), GPU_USAGE_STATIC, __func__);
+}
+
+/**
+ * Update the gpu buffer offsets of the given tiles.
+ * \return the total needed buffer length.
+ */
+static int64_t update_gpu_buffer_offsets(MutableSpan<UDIMTilePixels> tiles)
+{
+  int64_t elem_len = 0;
+  for (UDIMTilePixels &tile : tiles) {
+    tile.gpu_buffer_offset = elem_len;
+    elem_len += tile.pixel_rows.size();
+  }
+  return elem_len;
+}
+
+static void flatten_pixel_rows(Vector<PackedPixelRow> &elements, Span<UDIMTilePixels> tiles)
+{
+  for (const UDIMTilePixels &tile : tiles) {
+    BLI_assert(elements.size() == tile.gpu_buffer_offset);
+    elements.extend(tile.pixel_rows);
+  }
+}
+
+void NodeData::build_pixels_gpu_buffer()
+{
+  BLI_assert(gpu_buffers.pixels == nullptr);
+
+  int64_t elem_len = update_gpu_buffer_offsets(tiles);
+  /* TODO(jbakker): we should store the packed pixels in a single vector per node to reduce
+   * copying. */
+  Vector<PackedPixelRow> elements;
+  elements.reserve(elem_len);
+  flatten_pixel_rows(elements, tiles);
+  gpu_buffers.pixels = GPU_storagebuf_create_ex(
+      elem_len * sizeof(PackedPixelRow), elements.data(), GPU_USAGE_STATIC, __func__);
+}
+
 /**
  * During debugging this check could be enabled.
  * It will write to each image pixel that is covered by the PBVH.
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 2709ac3fd91..139be705178 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -80,6 +80,7 @@ set(SRC
   sculpt_paint_color.c
   sculpt_paint_image.cc
   sculpt_pose.c
+  sculpt_shaders.cc
   sculpt_smooth.c
   sculpt_transform.c
   sculpt_undo.c
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index cdfa9c2586f..ae0bc8cfb92 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -34,6 +34,7 @@ struct Object;
 struct SculptUndoNode;
 struct bContext;
 struct PaintModeSettings;
+struct GPUShader;
 
 /* Updates */
 
@@ -1821,6 +1822,13 @@ void SCULPT_bmesh_topology_rake(
 
 /* end sculpt_brush_types.c */
 
+/* sculpt_shaders.cc */
+
+struct GPUShader *SCULPT_shader_paint_image_get(void);
+void SCULPT_shader_free(void);
+
+/* end sculpt_shadders.cc */
+
 /* sculpt_ops.c */
 
 void SCULPT_OT_brush_stroke(struct wmOperatorType *ot);
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
index 906ab50d51d..0fdfc3ebdc4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
@@ -14,6 +14,11 @@
 #include "BLI_math_color_blend.h"
 #include "BLI_task.h"
 
+#include "GPU_capabilities.h"
+#include "GPU_compute.h"
+#include "GPU_debug.h"
+#include "GPU_shader.h"
+
 #include "IMB_colormanagement.h"
 #include "IMB_imbuf.h"
 
@@ -49,11 +54,16 @@ struct ImageData {
   }
 };
 
+/* -------------------------------------------------------------------- */
+/** \name CPU
+ * \{ */
+
 struct TexturePaintingUserData {
   Object *ob;
   Brush *brush;
   PBVHNode **nodes;
   ImageData image_data;
+  int32_t nodes_len;
 };
 
 /** Reading and writing to image buffer with 4 float channels. */
@@ -368,6 +378,12 @@ static void do_paint_pixels(void *__restrict userdata,
   node_data.flags.dirty |= pixels_updated;
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Undo
+ * \{ */
+
 static void undo_region_tiles(
     ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th)
 {
@@ -457,6 +473,102 @@ static void do_mark_dirty_regions(void *__restrict userdata,
   BKE_pbvh_pixels_mark_image_dirty(*node, *data->image_data.image, *data->image_data.image_user);
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPU
+ * \{ */
+
+static void ensure_gpu_buffers(TexturePaintingUserData &data)
+{
+  for (PBVHNode *node : MutableSpan<PBVHNode *>(data.nodes, data.nodes_len)) {
+    NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node);
+    node_data.ensure_gpu_buffers();
+  }
+}
+
+static void dispatch_gpu_painting(TexturePaintingUserData &data)
+{
+  GPUShader *shader = SCULPT_shader_paint_image_get();
+  GPU_shader_bind(shader);
+
+  ImageUser local_image_user = *data.image_data.image_user;
+  GPUTexture *tex = nullptr;
+
+  LISTBASE_FOREACH (ImageTile *, tile, &data.image_data.image->tiles) {
+    ImageTileWrapper image_tile(tile);
+    local_image_user.tile = image_tile.get_tile_number();
+
+    ImBuf *image_buffer = BKE_image_acquire_ibuf(
+        data.image_data.image, &local_image_user, nullptr);
+    if (image_buffer == nullptr) {
+      continue;
+    }
+
+    bool texture_needs_clearing = true;
+
+    /* Ensure that texture size is same as tile size. */
+    if (tex == nullptr || GPU_texture_width(tex) != image_buffer->x ||
+        GPU_texture_height(tex) != image_buffer->y) {
+      if (tex) {
+        GPU_texture_free(tex);
+        tex = nullptr;
+      }
+      tex = GPU_texture_create_2d(
+          __func__, image_buffer->x, image_buffer->y, 1, GPU_RGBA32F, nullptr);
+    }
+
+    /* Dispatch all nodes that paint on the active tile. */
+    for (PBVHNode *node : MutableSpan<PBVHNode *>(data.nodes, data.nodes_len)) {
+      NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node);
+
+      for (UDIMTilePixels &tile_pixels : node_data.tiles) {
+        if (tile_pixels.tile_number != image_tile.get_tile_number()) {
+          continue;
+        }
+
+        /* Only clear the texture when it is used for the first time. */
+        if (texture_needs_clearing) {
+          GPU_texture_clear(tex, GPU_DATA_FLOAT, float4(0.0f, 0.0f, 0.0f, 0.0f));
+          texture_needs_clearing = false;
+        }
+
+        GPU_shader_bind(shader);
+        GPU_texture_image_bind(tex, GPU_shader_get_texture_binding(shader, "out_img"));
+        GPU_storagebuf_bind(node_data.triangles.gpu_buffer,
+                            GPU_shader_get_ssbo(shader, "paint_input"));
+        GPU_storagebuf_bind(node_data.gpu_buffers.pixels,
+                            GPU_shader_get_ssbo(shader, "pixel_row_buf"));
+        GPU_shader_uniform_1i(shader, "pixel_row_offset", tile_pixels.gpu_buffer_offset);
+
+        GPU_compute_dispatch(shader, tile_pixels.pixel_rows.size(), 1, 1);
+      }
+      node_data.ensure_gpu_buffers();
+    }
+
+#if 0
+    GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+    float *tex_data = static_cast<float *>(GPU_texture_read(tex, GPU_DATA_FLOAT, 0));
+    for (int i = 0; i < 10; i++) {
+      printf("%f,", tex_data[i]);
+    }
+    printf("\n");
+    MEM_freeN(tex_data);
+#endif
+
+    /* Integrate active tile to draw engine texture. */
+
+    BKE_image_release_ibuf(data.image_data.image, image_buffer, nullptr);
+  }
+
+  if (tex) {
+    GPU_texture_free(tex);
+    tex = nullptr;
+  }
+}
+
+/** \} */
+
 }  // namespace blender::ed::sculpt_paint::paint::image
 
 extern "C" {
@@ -494,6 +606,13 @@ bool SCULPT_use_image_paint_brush(PaintModeSettings *settings, 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list