Blender  V3.3
session/tile.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2011-2022 Blender Foundation */
3 
4 #include "session/tile.h"
5 
6 #include <atomic>
7 
8 #include "graph/node.h"
9 #include "scene/background.h"
10 #include "scene/bake.h"
11 #include "scene/film.h"
12 #include "scene/integrator.h"
13 #include "scene/scene.h"
14 #include "session/session.h"
15 #include "util/algorithm.h"
16 #include "util/foreach.h"
17 #include "util/log.h"
18 #include "util/path.h"
19 #include "util/string.h"
20 #include "util/system.h"
21 #include "util/time.h"
22 #include "util/types.h"
23 
25 
26 /* --------------------------------------------------------------------
27  * Internal functions.
28  */
29 
30 static const char *ATTR_PASSES_COUNT = "cycles.passes.count";
31 static const char *ATTR_PASS_SOCKET_PREFIX_FORMAT = "cycles.passes.%d.";
32 static const char *ATTR_BUFFER_SOCKET_PREFIX = "cycles.buffer.";
33 static const char *ATTR_DENOISE_SOCKET_PREFIX = "cycles.denoise.";
34 
35 /* Global counter of ToleManager object instances. */
36 static std::atomic<uint64_t> g_instance_index = 0;
37 
38 /* Construct names of EXR channels which will ensure order of all channels to match exact offsets
39  * in render buffers corresponding to the given passes.
40  *
41  * Returns `std` datatypes so that it can be assigned directly to the OIIO's `ImageSpec`. */
42 static std::vector<std::string> exr_channel_names_for_passes(const BufferParams &buffer_params)
43 {
44  static const char *component_suffixes[] = {"R", "G", "B", "A"};
45 
46  int pass_index = 0;
47  std::vector<std::string> channel_names;
48  for (const BufferPass &pass : buffer_params.passes) {
49  if (pass.offset == PASS_UNUSED) {
50  continue;
51  }
52 
53  const PassInfo pass_info = pass.get_info();
54 
55  /* EXR canonically expects first part of channel names to be sorted alphabetically, which is
56  * not guaranteed to be the case with passes names. Assign a prefix based on the pass index
57  * with a fixed width to ensure ordering. This makes it possible to dump existing render
58  * buffers memory to disk and read it back without doing extra mapping. */
59  const string prefix = string_printf("%08d", pass_index);
60 
61  const string channel_name_prefix = prefix + string(pass.name) + ".";
62 
63  for (int i = 0; i < pass_info.num_components; ++i) {
64  channel_names.push_back(channel_name_prefix + component_suffixes[i]);
65  }
66 
67  ++pass_index;
68  }
69 
70  return channel_names;
71 }
72 
73 inline string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix)
74 {
75  return attr_name_prefix + string(socket.name);
76 }
77 
78 template<typename ValidateValueFunc, typename GetValueFunc>
80  ImageSpec *image_spec,
81  const Node *node,
82  const SocketType &socket,
83  const string &attr_name_prefix,
84  const ValidateValueFunc &validate_value_func,
85  const GetValueFunc &get_value_func)
86 {
87  if (!validate_value_func(node, socket)) {
88  return false;
89  }
90 
91  image_spec->attribute(node_socket_attribute_name(socket, attr_name_prefix),
92  get_value_func(node, socket));
93 
94  return true;
95 }
96 
97 static bool node_socket_to_image_spec_atttributes(ImageSpec *image_spec,
98  const Node *node,
99  const SocketType &socket,
100  const string &attr_name_prefix)
101 {
102  const string attr_name = node_socket_attribute_name(socket, attr_name_prefix);
103 
104  switch (socket.type) {
105  case SocketType::ENUM: {
106  const ustring value = node->get_string(socket);
107 
108  /* Validate that the node is consistent with the node type definition. */
109  const NodeEnum &enum_values = *socket.enum_values;
110  if (!enum_values.exists(value)) {
111  LOG(DFATAL) << "Node enum contains invalid value " << value;
112  return false;
113  }
114 
115  image_spec->attribute(attr_name, value);
116 
117  return true;
118  }
119 
120  case SocketType::STRING:
121  image_spec->attribute(attr_name, node->get_string(socket));
122  return true;
123 
124  case SocketType::INT:
125  image_spec->attribute(attr_name, node->get_int(socket));
126  return true;
127 
128  case SocketType::FLOAT:
129  image_spec->attribute(attr_name, node->get_float(socket));
130  return true;
131 
132  case SocketType::BOOLEAN:
133  image_spec->attribute(attr_name, node->get_bool(socket));
134  return true;
135 
136  default:
137  LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen.";
138  return false;
139  }
140 }
141 
143  const SocketType &socket,
144  const ImageSpec &image_spec,
145  const string &attr_name_prefix)
146 {
147  const string attr_name = node_socket_attribute_name(socket, attr_name_prefix);
148 
149  switch (socket.type) {
150  case SocketType::ENUM: {
151  /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */
152  const ustring value(image_spec.get_string_attribute(attr_name, ""));
153 
154  /* Validate that the node is consistent with the node type definition. */
155  const NodeEnum &enum_values = *socket.enum_values;
156  if (!enum_values.exists(value)) {
157  LOG(ERROR) << "Invalid enumerator value " << value;
158  return false;
159  }
160 
161  node->set(socket, enum_values[value]);
162 
163  return true;
164  }
165 
166  case SocketType::STRING:
167  /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */
168  node->set(socket, ustring(image_spec.get_string_attribute(attr_name, "")));
169  return true;
170 
171  case SocketType::INT:
172  node->set(socket, image_spec.get_int_attribute(attr_name, 0));
173  return true;
174 
175  case SocketType::FLOAT:
176  node->set(socket, image_spec.get_float_attribute(attr_name, 0));
177  return true;
178 
179  case SocketType::BOOLEAN:
180  node->set(socket, static_cast<bool>(image_spec.get_int_attribute(attr_name, 0)));
181  return true;
182 
183  default:
184  LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen.";
185  return false;
186  }
187 }
188 
189 static bool node_to_image_spec_atttributes(ImageSpec *image_spec,
190  const Node *node,
191  const string &attr_name_prefix)
192 {
193  for (const SocketType &socket : node->type->inputs) {
194  if (!node_socket_to_image_spec_atttributes(image_spec, node, socket, attr_name_prefix)) {
195  return false;
196  }
197  }
198 
199  return true;
200 }
201 
203  const ImageSpec &image_spec,
204  const string &attr_name_prefix)
205 {
206  for (const SocketType &socket : node->type->inputs) {
207  if (!node_socket_from_image_spec_atttributes(node, socket, image_spec, attr_name_prefix)) {
208  return false;
209  }
210  }
211 
212  return true;
213 }
214 
215 static bool buffer_params_to_image_spec_atttributes(ImageSpec *image_spec,
216  const BufferParams &buffer_params)
217 {
218  if (!node_to_image_spec_atttributes(image_spec, &buffer_params, ATTR_BUFFER_SOCKET_PREFIX)) {
219  return false;
220  }
221 
222  /* Passes storage is not covered by the node socket. so "expand" the loop manually. */
223 
224  const int num_passes = buffer_params.passes.size();
225  image_spec->attribute(ATTR_PASSES_COUNT, num_passes);
226 
227  for (int pass_index = 0; pass_index < num_passes; ++pass_index) {
228  const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index);
229 
230  const BufferPass *pass = &buffer_params.passes[pass_index];
231  if (!node_to_image_spec_atttributes(image_spec, pass, attr_name_prefix)) {
232  return false;
233  }
234  }
235 
236  return true;
237 }
238 
240  const ImageSpec &image_spec)
241 {
242  if (!node_from_image_spec_atttributes(buffer_params, image_spec, ATTR_BUFFER_SOCKET_PREFIX)) {
243  return false;
244  }
245 
246  /* Passes storage is not covered by the node socket. so "expand" the loop manually. */
247 
248  const int num_passes = image_spec.get_int_attribute(ATTR_PASSES_COUNT, 0);
249  if (num_passes == 0) {
250  LOG(ERROR) << "Missing passes count attribute.";
251  return false;
252  }
253 
254  for (int pass_index = 0; pass_index < num_passes; ++pass_index) {
255  const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index);
256 
257  BufferPass pass;
258 
259  if (!node_from_image_spec_atttributes(&pass, image_spec, attr_name_prefix)) {
260  return false;
261  }
262 
263  buffer_params->passes.emplace_back(std::move(pass));
264  }
265 
266  buffer_params->update_passes();
267 
268  return true;
269 }
270 
271 /* Configure image specification for the given buffer parameters and passes.
272  *
273  * Image channels will be strictly ordered to match content of corresponding buffer, and the
274  * metadata will be set so that the render buffers and passes can be reconstructed from it.
275  *
276  * If the tile size different from (0, 0) the image specification will be configured to use the
277  * given tile size for tiled IO. */
278 static bool configure_image_spec_from_buffer(ImageSpec *image_spec,
279  const BufferParams &buffer_params,
280  const int2 tile_size = make_int2(0, 0))
281 {
282  const std::vector<std::string> channel_names = exr_channel_names_for_passes(buffer_params);
283  const int num_channels = channel_names.size();
284 
285  *image_spec = ImageSpec(
286  buffer_params.width, buffer_params.height, num_channels, TypeDesc::FLOAT);
287 
288  image_spec->channelnames = move(channel_names);
289 
290  if (!buffer_params_to_image_spec_atttributes(image_spec, buffer_params)) {
291  return false;
292  }
293 
294  if (tile_size.x != 0 || tile_size.y != 0) {
295  DCHECK_GT(tile_size.x, 0);
296  DCHECK_GT(tile_size.y, 0);
297 
298  image_spec->tile_width = min(TileManager::IMAGE_TILE_SIZE, tile_size.x);
299  image_spec->tile_height = min(TileManager::IMAGE_TILE_SIZE, tile_size.y);
300  }
301 
302  return true;
303 }
304 
305 /* --------------------------------------------------------------------
306  * Tile Manager.
307  */
308 
310 {
311  /* Use process ID to separate different processes.
312  * To ensure uniqueness from within a process use combination of object address and instance
313  * index. This solves problem of possible object re-allocation at the same time, and solves
314  * possible conflict when the counter overflows while there are still active instances of the
315  * class. */
316  const int tile_manager_id = g_instance_index.fetch_add(1, std::memory_order_relaxed);
318  to_string(reinterpret_cast<uintptr_t>(this)) + "-" +
319  to_string(tile_manager_id);
320 }
321 
323 {
324 }
325 
326 int TileManager::compute_render_tile_size(const int suggested_tile_size) const
327 {
328  /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file
329  * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size
330  * because too big tile size leads to integer overflow inside OpenEXR. */
331  const int computed_tile_size = (suggested_tile_size <= IMAGE_TILE_SIZE) ?
332  suggested_tile_size :
333  align_up(suggested_tile_size, IMAGE_TILE_SIZE);
334  return min(computed_tile_size, MAX_TILE_SIZE);
335 }
336 
338 {
339  VLOG_WORK << "Using tile size of " << tile_size;
340 
342 
343  tile_size_ = tile_size;
344 
345  tile_state_.num_tiles_x = divide_up(params.width, tile_size_.x);
346  tile_state_.num_tiles_y = divide_up(params.height, tile_size_.y);
347  tile_state_.num_tiles = tile_state_.num_tiles_x * tile_state_.num_tiles_y;
348 
349  tile_state_.next_tile_index = 0;
350 
351  tile_state_.current_tile = Tile();
352 }
353 
355 {
356  DCHECK_NE(params.pass_stride, -1);
357 
359 
360  if (has_multiple_tiles()) {
361  /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to
362  * write to a partially configured file. */
364 
365  const DenoiseParams denoise_params = scene->integrator->get_denoise_params();
366  const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling();
367 
369  &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX);
370 
371  /* Not adaptive sampling overscan yet for baking, would need overscan also
372  * for buffers read from the output driver. */
373  if (adaptive_sampling.use && !scene->bake_manager->get_baking()) {
374  overscan_ = 4;
375  }
376  else {
377  overscan_ = 0;
378  }
379  }
380  else {
381  write_state_.image_spec = ImageSpec();
382  overscan_ = 0;
383  }
384 }
385 
386 void TileManager::set_temp_dir(const string &temp_dir)
387 {
388  temp_dir_ = temp_dir;
389 }
390 
392 {
393  return tile_state_.next_tile_index == tile_state_.num_tiles;
394 }
395 
397 {
398  if (done()) {
399  return false;
400  }
401 
402  tile_state_.current_tile = get_tile_for_index(tile_state_.next_tile_index);
403 
404  ++tile_state_.next_tile_index;
405 
406  return true;
407 }
408 
410 {
411  /* TODO(sergey): Consider using hilbert spiral, or. maybe, even configurable. Not sure this
412  * brings a lot of value since this is only applicable to BIG tiles. */
413 
414  const int tile_index_y = index / tile_state_.num_tiles_x;
415  const int tile_index_x = index - tile_index_y * tile_state_.num_tiles_x;
416 
417  const int tile_window_x = tile_index_x * tile_size_.x;
418  const int tile_window_y = tile_index_y * tile_size_.y;
419 
420  Tile tile;
421 
422  tile.x = max(0, tile_window_x - overscan_);
423  tile.y = max(0, tile_window_y - overscan_);
424 
425  tile.window_x = tile_window_x - tile.x;
426  tile.window_y = tile_window_y - tile.y;
427  tile.window_width = min(tile_size_.x, buffer_params_.width - tile_window_x);
428  tile.window_height = min(tile_size_.y, buffer_params_.height - tile_window_y);
429 
430  tile.width = min(buffer_params_.width - tile.x, tile.window_x + tile.window_width + overscan_);
431  tile.height = min(buffer_params_.height - tile.y,
432  tile.window_y + tile.window_height + overscan_);
433 
434  return tile;
435 }
436 
438 {
439  return tile_state_.current_tile;
440 }
441 
443 {
445 }
446 
448 {
449  write_state_.filename = path_join(temp_dir_,
450  "cycles-tile-buffer-" + tile_file_unique_part_ + "-" +
451  to_string(write_state_.tile_file_index) + ".exr");
452 
453  write_state_.tile_out = ImageOutput::create(write_state_.filename);
454  if (!write_state_.tile_out) {
455  LOG(ERROR) << "Error creating image output for " << write_state_.filename;
456  return false;
457  }
458 
459  if (!write_state_.tile_out->supports("tiles")) {
460  LOG(ERROR) << "Progress tile file format does not support tiling.";
461  return false;
462  }
463 
464  if (!write_state_.tile_out->open(write_state_.filename, write_state_.image_spec)) {
465  LOG(ERROR) << "Error opening tile file: " << write_state_.tile_out->geterror();
466  write_state_.tile_out = nullptr;
467  return false;
468  }
469 
470  write_state_.num_tiles_written = 0;
471 
472  VLOG_WORK << "Opened tile file " << write_state_.filename;
473 
474  return true;
475 }
476 
478 {
479  if (!write_state_.tile_out) {
480  return true;
481  }
482 
483  const bool success = write_state_.tile_out->close();
484  write_state_.tile_out = nullptr;
485 
486  if (!success) {
487  LOG(ERROR) << "Error closing tile file.";
488  return false;
489  }
490 
491  VLOG_WORK << "Tile output is closed.";
492 
493  return true;
494 }
495 
496 bool TileManager::write_tile(const RenderBuffers &tile_buffers)
497 {
498  if (!write_state_.tile_out) {
499  if (!open_tile_output()) {
500  return false;
501  }
502  }
503 
504  const double time_start = time_dt();
505 
507 
508  const BufferParams &tile_params = tile_buffers.params;
509 
510  const int tile_x = tile_params.full_x - buffer_params_.full_x + tile_params.window_x;
511  const int tile_y = tile_params.full_y - buffer_params_.full_y + tile_params.window_y;
512 
513  const int64_t pass_stride = tile_params.pass_stride;
514  const int64_t tile_row_stride = tile_params.width * pass_stride;
515 
516  vector<float> pixel_storage;
517  const float *pixels = tile_buffers.buffer.data() + tile_params.window_x * pass_stride +
518  tile_params.window_y * tile_row_stride;
519 
520  /* If there is an overscan used for the tile copy pixels into single continuous block of memory
521  * without any "gaps".
522  * This is a workaround for bug in OIIO (https://github.com/OpenImageIO/oiio/pull/3176).
523  * Our task reference: T93008. */
524  if (tile_params.window_x || tile_params.window_y ||
525  tile_params.window_width != tile_params.width ||
526  tile_params.window_height != tile_params.height) {
527  pixel_storage.resize(pass_stride * tile_params.window_width * tile_params.window_height);
528  float *pixels_continuous = pixel_storage.data();
529 
530  const int64_t pixels_row_stride = pass_stride * tile_params.width;
531  const int64_t pixels_continuous_row_stride = pass_stride * tile_params.window_width;
532 
533  for (int i = 0; i < tile_params.window_height; ++i) {
534  memcpy(pixels_continuous, pixels, sizeof(float) * pixels_continuous_row_stride);
535  pixels += pixels_row_stride;
536  pixels_continuous += pixels_continuous_row_stride;
537  }
538 
539  pixels = pixel_storage.data();
540  }
541 
542  VLOG_WORK << "Write tile at " << tile_x << ", " << tile_y;
543 
544  /* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The
545  * write_tiles() method expects a contiguous image region that will be split into tiles
546  * internally. OpenEXR expects the size of this region to be a multiple of the tile size,
547  * however OpenImageIO automatically adds the required padding.
548  *
549  * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the
550  * image tile size, which happens in compute_render_tile_size. */
551 
552  const int64_t xstride = pass_stride * sizeof(float);
553  const int64_t ystride = xstride * tile_params.window_width;
554  const int64_t zstride = ystride * tile_params.window_height;
555 
556  if (!write_state_.tile_out->write_tiles(tile_x,
557  tile_x + tile_params.window_width,
558  tile_y,
559  tile_y + tile_params.window_height,
560  0,
561  1,
563  pixels,
564  xstride,
565  ystride,
566  zstride)) {
567  LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror();
568  return false;
569  }
570 
571  ++write_state_.num_tiles_written;
572 
573  VLOG_WORK << "Tile written in " << time_dt() - time_start << " seconds.";
574 
575  return true;
576 }
577 
579 {
580  if (!write_state_.tile_out) {
581  /* None of the tiles were written hence the file was not created.
582  * Avoid creation of fully empty file since it is redundant. */
583  return;
584  }
585 
586  /* EXR expects all tiles to present in file. So explicitly write missing tiles as all-zero. */
587  if (write_state_.num_tiles_written < tile_state_.num_tiles) {
589 
590  for (int tile_index = write_state_.num_tiles_written; tile_index < tile_state_.num_tiles;
591  ++tile_index) {
593 
594  const int tile_x = tile.x + tile.window_x;
595  const int tile_y = tile.y + tile.window_y;
596 
597  VLOG_WORK << "Write dummy tile at " << tile_x << ", " << tile_y;
598 
599  write_state_.tile_out->write_tiles(tile_x,
600  tile_x + tile.window_width,
601  tile_y,
602  tile_y + tile.window_height,
603  0,
604  1,
606  pixel_storage.data());
607  }
608  }
609 
611 
614  }
615 
616  VLOG_WORK << "Tile file size is "
617  << string_human_readable_number(path_file_size(write_state_.filename)) << " bytes.";
618 
619  /* Advance the counter upon explicit finish of the file.
620  * Makes it possible to re-use tile manager for another scene, and avoids unnecessary increments
621  * of the tile-file-within-session index. */
622  ++write_state_.tile_file_index;
623 
624  write_state_.filename = "";
625 }
626 
627 bool TileManager::read_full_buffer_from_disk(const string_view filename,
628  RenderBuffers *buffers,
629  DenoiseParams *denoise_params)
630 {
631  unique_ptr<ImageInput> in(ImageInput::open(filename));
632  if (!in) {
633  LOG(ERROR) << "Error opening tile file " << filename;
634  return false;
635  }
636 
637  const ImageSpec &image_spec = in->spec();
638 
639  BufferParams buffer_params;
641  return false;
642  }
643  buffers->reset(buffer_params);
644 
646  return false;
647  }
648 
649  if (!in->read_image(TypeDesc::FLOAT, buffers->buffer.data())) {
650  LOG(ERROR) << "Error reading pixels from the tile file " << in->geterror();
651  return false;
652  }
653 
654  if (!in->close()) {
655  LOG(ERROR) << "Error closing tile file " << in->geterror();
656  return false;
657  }
658 
659  return true;
660 }
661 
typedef float(TangentPoint)[2]
bool get_baking() const
Definition: bake.cpp:25
int pass_stride
Definition: buffers.h:93
int full_x
Definition: buffers.h:84
vector< BufferPass > passes
Definition: buffers.h:96
int height
Definition: buffers.h:72
int window_y
Definition: buffers.h:79
int window_height
Definition: buffers.h:81
int window_width
Definition: buffers.h:80
NODE_DECLARE int width
Definition: buffers.h:71
int window_x
Definition: buffers.h:78
int full_y
Definition: buffers.h:85
void update_passes()
Definition: buffers.cpp:121
ustring name
Definition: buffers.h:31
int offset
Definition: buffers.h:35
PassInfo get_info() const
Definition: buffers.cpp:73
DenoiseParams get_denoise_params() const
Definition: integrator.cpp:372
AdaptiveSampling get_adaptive_sampling() const
Definition: integrator.cpp:329
device_vector< float > buffer
Definition: buffers.h:159
BufferParams params
Definition: buffers.h:156
void reset(const BufferParams &params)
Definition: buffers.cpp:274
BufferParams buffer_params_
Definition: session/tile.h:139
void set_temp_dir(const string &temp_dir)
function< void(string_view)> full_buffer_written_cb
Definition: session/tile.h:40
string temp_dir_
Definition: session/tile.h:128
bool close_tile_output()
void reset_scheduling(const BufferParams &params, int2 tile_size)
int compute_render_tile_size(const int suggested_tile_size) const
bool has_multiple_tiles() const
Definition: session/tile.h:68
struct TileManager::@1269 write_state_
Tile get_tile_for_index(int index) const
void update(const BufferParams &params, const Scene *scene)
const int2 get_size() const
const Tile & get_current_tile() const
string filename
Definition: session/tile.h:160
struct TileManager::@1268 tile_state_
bool open_tile_output()
bool write_tile(const RenderBuffers &tile_buffers)
bool read_full_buffer_from_disk(string_view filename, RenderBuffers *buffers, DenoiseParams *denoise_params)
ImageSpec image_spec
Definition: session/tile.h:167
static const int MAX_TILE_SIZE
Definition: session/tile.h:118
string tile_file_unique_part_
Definition: session/tile.h:132
void finish_write_tiles()
static const int IMAGE_TILE_SIZE
Definition: session/tile.h:112
#define CCL_NAMESPACE_END
Definition: cuda/compat.h:9
OperationNode * node
Scene scene
static const char * to_string(const Interpolation &interp)
Definition: gl_shader.cc:63
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
const int tile_index
ccl_global const KernelWorkTile * tile
ccl_gpu_kernel_postfix ccl_global float int int int int int int int pass_stride
#define PASS_UNUSED
Definition: kernel/types.h:44
#define DCHECK_EQ(a, b)
Definition: log.h:64
#define VLOG_WORK
Definition: log.h:80
#define DCHECK_GT(a, b)
Definition: log.h:65
#define LOG(severity)
Definition: log.h:36
#define DCHECK_NE(a, b)
Definition: log.h:63
#define make_int2(x, y)
Definition: metal/compat.h:206
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
size_t path_file_size(const string &path)
Definition: path.cpp:550
string path_join(const string &dir, const string &file)
Definition: path.cpp:413
@ FLOAT
static const char * ATTR_PASS_SOCKET_PREFIX_FORMAT
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)
static const char * ATTR_BUFFER_SOCKET_PREFIX
static std::vector< std::string > exr_channel_names_for_passes(const BufferParams &buffer_params)
static bool buffer_params_to_image_spec_atttributes(ImageSpec *image_spec, const BufferParams &buffer_params)
static const char * ATTR_DENOISE_SOCKET_PREFIX
static CCL_NAMESPACE_BEGIN const char * ATTR_PASSES_COUNT
static std::atomic< uint64_t > g_instance_index
static bool node_socket_from_image_spec_atttributes(Node *node, const SocketType &socket, const ImageSpec &image_spec, const string &attr_name_prefix)
static bool node_from_image_spec_atttributes(Node *node, const ImageSpec &image_spec, const string &attr_name_prefix)
static bool node_to_image_spec_atttributes(ImageSpec *image_spec, const Node *node, const string &attr_name_prefix)
string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix)
static bool buffer_params_from_image_spec_atttributes(BufferParams *buffer_params, const ImageSpec &image_spec)
static bool configure_image_spec_from_buffer(ImageSpec *image_spec, const BufferParams &buffer_params, const int2 tile_size=make_int2(0, 0))
static bool node_socket_to_image_spec_atttributes(ImageSpec *image_spec, const Node *node, const SocketType &socket, const string &attr_name_prefix)
#define min(a, b)
Definition: sort.c:35
_W64 unsigned int uintptr_t
Definition: stdint.h:119
__int64 int64_t
Definition: stdint.h:89
string string_human_readable_number(size_t num)
Definition: string.cpp:248
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition: string.cpp:22
bool exists(ustring x) const
Definition: node_enum.h:28
Definition: pass.h:26
int num_components
Definition: pass.h:27
BakeManager * bake_manager
Definition: scene.h:228
Integrator * integrator
Definition: scene.h:210
ustring name
Definition: node_type.h:72
Type type
Definition: node_type.h:73
const NodeEnum * enum_values
Definition: node_type.h:76
int x
Definition: types_int2.h:15
int y
Definition: types_int2.h:15
uint64_t system_self_process_id()
Definition: system.cpp:279
CCL_NAMESPACE_BEGIN double time_dt()
Definition: time.cpp:35
float max
ccl_device_inline size_t align_up(size_t offset, size_t alignment)
Definition: util/types.h:46
ccl_device_inline size_t divide_up(size_t x, size_t y)
Definition: util/types.h:51