[Bf-blender-cvs] [5e37f70307b] master: Fix T93350: Cycles renders shows black during rendering huge resolutions

Sergey Sharybin noreply at git.blender.org
Tue Dec 7 19:02:23 CET 2021


Commit: 5e37f70307bdacedd0f7da65f8b385bc1426f21d
Author: Sergey Sharybin
Date:   Tue Dec 7 18:50:48 2021 +0100
Branches: master
https://developer.blender.org/rB5e37f70307bdacedd0f7da65f8b385bc1426f21d

Fix T93350: Cycles renders shows black during rendering huge resolutions

The root of the issue is caused by Cycles ignoring OpenGL limitation on
the maximum resolution of textures: Cycles was allocating texture of the
final render resolution. It was exceeding limitation on certain GPUs and
driver.

The idea is simple: use multiple textures for the display, each of which
will fit into OpenGL limitations.

There is some code which allows the display driver to know when to start
the new tile. Also added some code to allow force graphics interop to be
re-created. The latter one ended up not used in the final version of the
patch, but it might be helpful for other drivers implementation.

The tile size is limited to 8K now as it is the safest size for textures
on many GPUs and OpenGL drivers.

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

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

M	intern/cycles/blender/addon/properties.py
M	intern/cycles/blender/display_driver.cpp
M	intern/cycles/blender/display_driver.h
M	intern/cycles/device/cuda/graphics_interop.cpp
M	intern/cycles/integrator/path_trace.cpp
M	intern/cycles/integrator/path_trace.h
M	intern/cycles/integrator/path_trace_display.cpp
M	intern/cycles/integrator/path_trace_display.h
M	intern/cycles/integrator/path_trace_work.cpp
M	intern/cycles/integrator/path_trace_work_gpu.cpp
M	intern/cycles/session/display_driver.h
M	intern/cycles/session/session.cpp
M	intern/cycles/session/tile.cpp
M	intern/cycles/session/tile.h

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

diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 8569cb7d946..3dd6a02946b 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -799,7 +799,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
         name="Tile Size",
         default=2048,
         description="",
-        min=8, max=16384,
+        min=8, max=8192,
     )
 
     # Various fine-tuning debug flags
diff --git a/intern/cycles/blender/display_driver.cpp b/intern/cycles/blender/display_driver.cpp
index abf421983b3..d902edc4695 100644
--- a/intern/cycles/blender/display_driver.cpp
+++ b/intern/cycles/blender/display_driver.cpp
@@ -272,12 +272,300 @@ uint BlenderDisplaySpaceShader::get_shader_program()
   return shader_program_;
 }
 
+/* --------------------------------------------------------------------
+ * DrawTile.
+ */
+
+/* Higher level representation of a texture from the graphics library. */
+class GLTexture {
+ public:
+  /* Global counter for all allocated OpenGL textures used by instances of this class. */
+  static inline std::atomic<int> num_used = 0;
+
+  GLTexture() = default;
+
+  ~GLTexture()
+  {
+    assert(gl_id == 0);
+  }
+
+  GLTexture(const GLTexture &other) = delete;
+  GLTexture &operator=(GLTexture &other) = delete;
+
+  GLTexture(GLTexture &&other) noexcept
+      : gl_id(other.gl_id), width(other.width), height(other.height)
+  {
+    other.reset();
+  }
+
+  GLTexture &operator=(GLTexture &&other)
+  {
+    if (this == &other) {
+      return *this;
+    }
+
+    gl_id = other.gl_id;
+    width = other.width;
+    height = other.height;
+
+    other.reset();
+
+    return *this;
+  }
+
+  bool gl_resources_ensure()
+  {
+    if (gl_id) {
+      return true;
+    }
+
+    /* Create texture. */
+    glGenTextures(1, &gl_id);
+    if (!gl_id) {
+      LOG(ERROR) << "Error creating texture.";
+      return false;
+    }
+
+    /* Configure the texture. */
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, gl_id);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    /* Clamp to edge so that precision issues when zoomed out (which forces linear interpolation)
+     * does not cause unwanted repetition. */
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    ++num_used;
+
+    return true;
+  }
+
+  void gl_resources_destroy()
+  {
+    if (!gl_id) {
+      return;
+    }
+
+    glDeleteTextures(1, &gl_id);
+
+    reset();
+
+    --num_used;
+  }
+
+  /* OpenGL resource IDs of the texture.
+   *
+   * NOTE: Allocated on the render engine's context. */
+  uint gl_id = 0;
+
+  /* Dimensions of the texture in pixels. */
+  int width = 0;
+  int height = 0;
+
+ protected:
+  void reset()
+  {
+    gl_id = 0;
+    width = 0;
+    height = 0;
+  }
+};
+
+/* Higher level representation of a Pixel Buffer Object (PBO) from the graphics library. */
+class GLPixelBufferObject {
+ public:
+  /* Global counter for all allocated OpenGL PBOs used by instances of this class. */
+  static inline std::atomic<int> num_used = 0;
+
+  GLPixelBufferObject() = default;
+
+  ~GLPixelBufferObject()
+  {
+    assert(gl_id == 0);
+  }
+
+  GLPixelBufferObject(const GLPixelBufferObject &other) = delete;
+  GLPixelBufferObject &operator=(GLPixelBufferObject &other) = delete;
+
+  GLPixelBufferObject(GLPixelBufferObject &&other) noexcept
+      : gl_id(other.gl_id), width(other.width), height(other.height)
+  {
+    other.reset();
+  }
+
+  GLPixelBufferObject &operator=(GLPixelBufferObject &&other)
+  {
+    if (this == &other) {
+      return *this;
+    }
+
+    gl_id = other.gl_id;
+    width = other.width;
+    height = other.height;
+
+    other.reset();
+
+    return *this;
+  }
+
+  bool gl_resources_ensure()
+  {
+    if (gl_id) {
+      return true;
+    }
+
+    glGenBuffers(1, &gl_id);
+    if (!gl_id) {
+      LOG(ERROR) << "Error creating texture pixel buffer object.";
+      return false;
+    }
+
+    ++num_used;
+
+    return true;
+  }
+
+  void gl_resources_destroy()
+  {
+    if (!gl_id) {
+      return;
+    }
+
+    glDeleteBuffers(1, &gl_id);
+
+    reset();
+
+    --num_used;
+  }
+
+  /* OpenGL resource IDs of the PBO.
+   *
+   * NOTE: Allocated on the render engine's context. */
+  uint gl_id = 0;
+
+  /* Dimensions of the PBO. */
+  int width = 0;
+  int height = 0;
+
+ protected:
+  void reset()
+  {
+    gl_id = 0;
+    width = 0;
+    height = 0;
+  }
+};
+
+class DrawTile {
+ public:
+  DrawTile() = default;
+  ~DrawTile() = default;
+
+  DrawTile(const DrawTile &other) = delete;
+  DrawTile &operator=(const DrawTile &other) = delete;
+
+  DrawTile(DrawTile &&other) noexcept = default;
+
+  DrawTile &operator=(DrawTile &&other) = default;
+
+  bool gl_resources_ensure()
+  {
+    if (!texture.gl_resources_ensure()) {
+      gl_resources_destroy();
+      return false;
+    }
+
+    if (!gl_vertex_buffer) {
+      glGenBuffers(1, &gl_vertex_buffer);
+      if (!gl_vertex_buffer) {
+        LOG(ERROR) << "Error allocating tile VBO.";
+        gl_resources_destroy();
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  void gl_resources_destroy()
+  {
+    texture.gl_resources_destroy();
+
+    if (gl_vertex_buffer) {
+      glDeleteBuffers(1, &gl_vertex_buffer);
+      gl_vertex_buffer = 0;
+    }
+  }
+
+  inline bool ready_to_draw() const
+  {
+    return texture.gl_id != 0;
+  }
+
+  /* Texture which contains pixels of the tile. */
+  GLTexture texture;
+
+  /* Display parameters the texture of this tile has been updated for. */
+  BlenderDisplayDriver::Params params;
+
+  /* OpenGL resources needed for drawing. */
+  uint gl_vertex_buffer = 0;
+};
+
+class DrawTileAndPBO {
+ public:
+  bool gl_resources_ensure()
+  {
+    if (!tile.gl_resources_ensure() || !buffer_object.gl_resources_ensure()) {
+      gl_resources_destroy();
+      return false;
+    }
+
+    return true;
+  }
+
+  void gl_resources_destroy()
+  {
+    tile.gl_resources_destroy();
+    buffer_object.gl_resources_destroy();
+  }
+
+  DrawTile tile;
+  GLPixelBufferObject buffer_object;
+};
+
 /* --------------------------------------------------------------------
  * BlenderDisplayDriver.
  */
 
+struct BlenderDisplayDriver::Tiles {
+  /* Resources of a tile which is being currently rendered. */
+  DrawTileAndPBO current_tile;
+
+  /* All tiles which rendering is finished and which content will not be changed. */
+  struct {
+    vector<DrawTile> tiles;
+
+    void gl_resources_destroy_and_clear()
+    {
+      for (DrawTile &tile : tiles) {
+        tile.gl_resources_destroy();
+      }
+
+      tiles.clear();
+    }
+  } finished_tiles;
+};
+
 BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene)
-    : b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene))
+    : b_engine_(b_engine),
+      display_shader_(BlenderDisplayShader::create(b_engine, b_scene)),
+      tiles_(make_unique<Tiles>())
 {
   /* Create context while on the main thread. */
   gl_context_create();
@@ -292,6 +580,21 @@ BlenderDisplayDriver::~BlenderDisplayDriver()
  * Update procedure.
  */
 
+void BlenderDisplayDriver::next_tile_begin()
+{
+  if (!tiles_->current_tile.tile.ready_to_draw()) {
+    LOG(ERROR)
+        << "Unexpectedly moving to the next tile without any data provided for current tile.";
+    return;
+  }
+
+  /* Moving to the next tile without giving render data for the current tile is not an expected
+   * situation. */
+  DCHECK(!need_clear_);
+
+  tiles_->finished_tiles.tiles.emplace_back(std::move(tiles_->current_tile.tile));
+}
+
 bool BlenderDisplayDriver::update_begin(const Params &params,
                                         int texture_width,
                                         int texture_height)
@@ -312,24 +615,33 @@ bool BlenderDisplayDriver::update_begin(const Params &params,
     glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
   }
 
-  if (!gl_texture_resources_ensure()) {
+  DrawTile &current_tile = tiles_->current_tile.tile;
+  GLPixelBufferObject &current_tile_buffer_object = tiles_->current_tile.buffer_object;
+
+  /* Clear storage of all finished tiles when display clear is requested.
+   * Do it when new tile data is provided to handle the display clear flag in a single place.
+   * It also makes the logic reliable from the whether drawing did happen or not point of view. */
+  if (need_clear_) {
+    tiles_->finished_tiles.gl_resources_destroy_and_clear();
+    need_clear_ = false;
+  }
+
+  if (!tiles_->current_tile.gl_resources_ensure()) {
+    tiles_->current_tile.gl_resources_destroy();
     gl_context_disable();
     return false;
   }
 
   /* Update texture dimensions if needed. */
-  if (texture_.width != texture_width || texture_.height != texture_height) {
+  if (current_tile.texture.width != texture_width ||
+      current_tile.texture.height != texture_height) {
     glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
+    glBindTexture(GL_TEXTURE_2D, current_tile.texture.gl_id);
     glTexImage2D(
         GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0);
-    texture_.width = texture_width;
-    texture_.height = texture_height;
+    current_tile.texture.width = texture_width;
+    current_tile.texture.height = texture_height;
     glBindTexture(GL_TEXTURE_2D, 0);
-
-    /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to
-     * avoid undefined content. */
-    texture_.need_clear = true;
   }
 
   /* Update PBO dimensions if needed.
@@ -341,29 +653,58 @@ bool BlenderDisplayDriver::update_begin(const Params &params,
    * sending too much data to GPU when resolution divider is not 1. */
   /* TODO(sergey): Investigate whether keeping the PBO exact size of the texture makes non-interop
    * mode faster. */
-  const int buffer_width = params.full_size.x;
-  const int buffer_height = params

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list