[Bf-blender-cvs] [a6eb5370093] cycles-x: Cycles X: Store passes and buffer params in tile file metadata

Sergey Sharybin noreply at git.blender.org
Mon Sep 13 12:15:12 CEST 2021


Commit: a6eb5370093c4960d8e3d7be9df922acd3194955
Author: Sergey Sharybin
Date:   Wed Sep 8 12:50:45 2021 +0200
Branches: cycles-x
https://developer.blender.org/rBa6eb5370093c4960d8e3d7be9df922acd3194955

Cycles X: Store passes and buffer params in tile file metadata

Allows to fully forget about passes and parameters once the file
was fully written.

Currently should be no functional changes. Will be used when a proper
memory saving is implemented: when EXR files are read from disk and
written to Blender after all view layers were rendered.

Is debatable why we need to store actual passes: for now it was the
most local change to move towards the goal (which is to decouple
full buffers reading from the Session/PathTrace/TileManager state).
Might end up storing simplified version of passes in the BufferParams
so that we can use `get_render_tile_pixels()` without having Scene
and only relying on the RenderBuffers. In any case, we would need to
store some sort of metadata, and whether it comes to/from passes is
something we can tweak in the future.

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

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

M	intern/cycles/render/tile.cpp
M	intern/cycles/render/tile.h

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

diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp
index fa32c2c8532..c91ba36fd09 100644
--- a/intern/cycles/render/tile.cpp
+++ b/intern/cycles/render/tile.cpp
@@ -16,6 +16,7 @@
 
 #include "render/tile.h"
 
+#include "graph/node.h"
 #include "render/pass.h"
 #include "util/util_algorithm.h"
 #include "util/util_foreach.h"
@@ -27,6 +28,316 @@
 
 CCL_NAMESPACE_BEGIN
 
+static const char *ATTR_PASSES_COUNT = "cycles.passes.count";
+
+static const char *ATTR_PASS_SOCKET_PREFIX_FORMAT = "cycles.passes.%d.";
+
+static const char *ATTR_BUFFER_FULL_X = "cycles.buffer.full_x";
+static const char *ATTR_BUFFER_FULL_Y = "cycles.buffer.full_y";
+static const char *ATTR_BUFFER_FULL_WIDTH = "cycles.buffer.full_width";
+static const char *ATTR_BUFFER_FULL_HEIGHT = "cycles.buffer.full_height";
+
+/* Construct names of EXR channels which will ensure order of all channels to match exact offsets
+ * in render buffers corresponding to the given passes.
+ *
+ * Returns `std` datatypes so that it can be assigned directly to the OIIO's `ImageSpec`. */
+static std::vector<std::string> exr_channel_names_for_passes(const vector<Pass *> &passes)
+{
+  static const char *component_suffixes[] = {"R", "G", "B", "A"};
+
+  int pass_index = 0;
+  int num_channels = 0;
+  std::vector<std::string> channel_names;
+  for (const Pass *pass : passes) {
+    if (!pass->is_written()) {
+      continue;
+    }
+
+    const PassInfo &pass_info = pass->get_info();
+    num_channels += pass_info.num_components;
+
+    /* EXR canonically expects first part of channel names to be sorted alphabetically, which is
+     * not guaranteed to be the case with passes names. Assign a prefix based on the pass index
+     * with a fixed width to ensure ordering. This makes it possible to dump existing render
+     * buffers memory to disk and read it back without doing extra mapping. */
+    const string prefix = string_printf("%08d", pass_index);
+
+    const string channel_name_prefix = prefix + string(pass->name) + ".";
+
+    for (int i = 0; i < pass_info.num_components; ++i) {
+      channel_names.push_back(channel_name_prefix + component_suffixes[i]);
+    }
+
+    ++pass_index;
+  }
+
+  return channel_names;
+}
+
+static bool buffer_params_to_image_spec_atttributes(ImageSpec *image_spec,
+                                                    const BufferParams &buffer_params)
+{
+  image_spec->attribute(ATTR_BUFFER_FULL_X, buffer_params.full_x);
+  image_spec->attribute(ATTR_BUFFER_FULL_Y, buffer_params.full_y);
+  image_spec->attribute(ATTR_BUFFER_FULL_WIDTH, buffer_params.full_width);
+  image_spec->attribute(ATTR_BUFFER_FULL_HEIGHT, buffer_params.full_height);
+
+  return true;
+}
+
+/* NOTE: The parameters needs to be updated with passes after this call still
+ * (`buffer_params->update_passes()`). */
+static bool buffer_params_from_image_spec_atttributes(BufferParams *buffer_params,
+                                                      const ImageSpec &image_spec)
+{
+  buffer_params->width = image_spec.width;
+  buffer_params->height = image_spec.height;
+  buffer_params->full_x = image_spec.get_int_attribute(ATTR_BUFFER_FULL_X, 0);
+  buffer_params->full_y = image_spec.get_int_attribute(ATTR_BUFFER_FULL_Y, 0);
+  buffer_params->full_width = image_spec.get_int_attribute(ATTR_BUFFER_FULL_WIDTH, 0);
+  buffer_params->full_height = image_spec.get_int_attribute(ATTR_BUFFER_FULL_HEIGHT, 0);
+
+  return true;
+}
+
+inline string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix)
+{
+  return attr_name_prefix + string(socket.name);
+}
+
+template<typename ValidateValueFunc, typename GetValueFunc>
+static bool node_socket_generic_to_image_spec_atttributes(
+    ImageSpec *image_spec,
+    const Node *node,
+    const SocketType &socket,
+    const string &attr_name_prefix,
+    const ValidateValueFunc &validate_value_func,
+    const GetValueFunc &get_value_func)
+{
+  if (!validate_value_func(node, socket)) {
+    return false;
+  }
+
+  image_spec->attribute(node_socket_attribute_name(socket, attr_name_prefix),
+                        get_value_func(node, socket));
+
+  return true;
+}
+
+static bool node_socket_to_image_spec_atttributes(ImageSpec *image_spec,
+                                                  const Node *node,
+                                                  const SocketType &socket,
+                                                  const string &attr_name_prefix)
+{
+  const string attr_name = node_socket_attribute_name(socket, attr_name_prefix);
+
+  switch (socket.type) {
+    case SocketType::ENUM: {
+      const ustring value = node->get_string(socket);
+
+      /* Validate that the node is consistent with the node type definition. */
+      const NodeEnum &enum_values = *socket.enum_values;
+      if (!enum_values.exists(value)) {
+        LOG(DFATAL) << "Node enum contains invalid value " << value;
+        return false;
+      }
+
+      image_spec->attribute(attr_name, value);
+
+      return true;
+    }
+
+    case SocketType::STRING:
+      image_spec->attribute(attr_name, node->get_string(socket));
+      return true;
+
+    case SocketType::BOOLEAN:
+      image_spec->attribute(attr_name, node->get_bool(socket));
+      return true;
+
+    default:
+      LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen.";
+      return false;
+  }
+}
+
+static bool node_socket_from_image_spec_atttributes(Node *node,
+                                                    const SocketType &socket,
+                                                    const ImageSpec &image_spec,
+                                                    const string &attr_name_prefix)
+{
+  const string attr_name = node_socket_attribute_name(socket, attr_name_prefix);
+
+  switch (socket.type) {
+    case SocketType::ENUM: {
+      /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */
+      const ustring value(image_spec.get_string_attribute(attr_name, ""));
+
+      /* Validate that the node is consistent with the node type definition. */
+      const NodeEnum &enum_values = *socket.enum_values;
+      if (!enum_values.exists(value)) {
+        LOG(ERROR) << "Invalid enumerator value " << value;
+        return false;
+      }
+
+      node->set(socket, enum_values[value]);
+
+      return true;
+    }
+
+    case SocketType::STRING:
+      /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */
+      node->set(socket, ustring(image_spec.get_string_attribute(attr_name, "")));
+      return true;
+
+    case SocketType::BOOLEAN:
+      node->set(socket, static_cast<bool>(image_spec.get_int_attribute(attr_name, 0)));
+      return true;
+
+    default:
+      LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen.";
+      return false;
+  }
+}
+
+static bool node_to_image_spec_atttributes(ImageSpec *image_spec,
+                                           const Node *node,
+                                           const string &attr_name_prefix)
+{
+  for (const SocketType &socket : node->type->inputs) {
+    if (!node_socket_to_image_spec_atttributes(image_spec, node, socket, attr_name_prefix)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static bool node_from_image_spec_atttributes(Node *node,
+                                             const ImageSpec &image_spec,
+                                             const string &attr_name_prefix)
+{
+  for (const SocketType &socket : node->type->inputs) {
+    if (!node_socket_from_image_spec_atttributes(node, socket, image_spec, attr_name_prefix)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static bool passes_to_image_spec_atttributes(ImageSpec *image_spec, const vector<Pass *> &passes)
+{
+  const int num_passes = passes.size();
+
+  image_spec->attribute(ATTR_PASSES_COUNT, num_passes);
+
+  for (int pass_index = 0; pass_index < num_passes; ++pass_index) {
+    const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index);
+
+    const Pass *pass = passes[pass_index];
+
+    if (!node_to_image_spec_atttributes(image_spec, pass, attr_name_prefix)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static bool passes_from_image_spec_atttributes(NodeOwner *passes_owner,
+                                               vector<Pass *> *passes,
+                                               const ImageSpec image_spec)
+{
+  const int num_passes = image_spec.get_int_attribute(ATTR_PASSES_COUNT, 0);
+  if (num_passes == 0) {
+    LOG(ERROR) << "Missing passes count attribute.";
+    return false;
+  }
+
+  for (int pass_index = 0; pass_index < num_passes; ++pass_index) {
+    const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index);
+
+    Pass *pass = new Pass();
+    pass->set_owner(passes_owner);
+    passes->push_back(pass);
+
+    if (!node_from_image_spec_atttributes(pass, image_spec, attr_name_prefix)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static bool configure_image_spec_attributes(ImageSpec *image_spec,
+                                            const BufferParams &buffer_params,
+                                            const vector<Pass *> &passes)
+{
+  if (!buffer_params_to_image_spec_atttributes(image_spec, buffer_params)) {
+    return false;
+  }
+
+  if (!passes_to_image_spec_atttributes(image_spec, passes)) {
+    return false;
+  }
+
+  return true;
+}
+
+/* Configure image specification for the given buffer parameters and passes.
+ *
+ * Image channels will ber strictly ordered to match content of corresponding buffer, and the
+ * metadata will be set so that the render buffers and passes can be reconstructed from it.
+ *
+ * If the tile size different from (0, 0) the image specification will be configured to use the
+ * given tile size for tiled IO. */
+static bool configure_image_spec_from_buffer(ImageSpec *image_spec,
+                                             const BufferParams &buffer_params,
+                                             const vector<Pass *> &passes,
+                                             const

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list