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

Brecht Van Lommel noreply at git.blender.org
Fri Jan 7 18:37:01 CET 2022


Commit: ae28d90578be516bf81f3532846c29f9985f1085
Author: Brecht Van Lommel
Date:   Thu Jan 6 16:41:44 2022 +0100
Branches: master
https://developer.blender.org/rBae28d90578be516bf81f3532846c29f9985f1085

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.

This is an updated fix with a workaround for freezing with the NVIDIA
driver on Linux.

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 a7deae2c05d..f669adb9f37 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -802,7 +802,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..7524a3adf37 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