Blender V5.0
session/tile.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "session/tile.h"
6
7#include <atomic>
8
9#include "graph/node.h"
10#include "scene/background.h"
11#include "scene/bake.h"
12#include "scene/film.h"
13#include "scene/integrator.h"
14#include "scene/scene.h"
15#include "session/session.h"
16
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
30static const char *ATTR_PASSES_COUNT = "cycles.passes.count";
31static const char *ATTR_PASS_SOCKET_PREFIX_FORMAT = "cycles.passes.%d.";
32static const char *ATTR_BUFFER_SOCKET_PREFIX = "cycles.buffer.";
33static const char *ATTR_DENOISE_SOCKET_PREFIX = "cycles.denoise.";
34
35/* Global counter of ToleManager object instances. */
36static 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` data-types so that it can be assigned directly to the OIIO's `ImageSpec`. */
42static 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
73inline string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix)
74{
75 return attr_name_prefix + string(socket.name);
76}
77
78template<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
97static 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
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
129 image_spec->attribute(attr_name, node->get_float(socket));
130 return true;
131
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
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
176 node->set(socket, image_spec.get_float_attribute(attr_name, 0));
177 return true;
178
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
189static 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
215static 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. */
278static 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 = std::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
322TileManager::~TileManager() = default;
323
324int TileManager::compute_render_tile_size(const int suggested_tile_size) const
325{
326 /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file
327 * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size
328 * because too big tile size leads to integer overflow inside OpenEXR. */
329 const int computed_tile_size = (suggested_tile_size <= IMAGE_TILE_SIZE) ?
330 suggested_tile_size :
331 align_up(suggested_tile_size, IMAGE_TILE_SIZE);
332 return min(computed_tile_size, MAX_TILE_SIZE);
333}
334
336{
337 LOG_DEBUG << "Using tile size of " << tile_size;
338
340
341 tile_size_ = tile_size;
342
343 tile_state_.num_tiles_x = tile_size_.x ? divide_up(params.width, tile_size_.x) : 0;
344 tile_state_.num_tiles_y = tile_size_.y ? divide_up(params.height, tile_size_.y) : 0;
345 tile_state_.num_tiles = tile_state_.num_tiles_x * tile_state_.num_tiles_y;
346
347 tile_state_.next_tile_index = 0;
348
349 tile_state_.current_tile = Tile();
350}
351
353{
354 DCHECK_NE(params.pass_stride, -1);
355
357
358 if (has_multiple_tiles()) {
359 /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to
360 * write to a partially configured file. */
362
363 const DenoiseParams denoise_params = scene->integrator->get_denoise_params();
364 const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling();
365
367 &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX);
368
369 /* Not adaptive sampling overscan yet for baking, would need overscan also
370 * for buffers read from the output driver. */
371 if (adaptive_sampling.use && !scene->bake_manager->get_baking()) {
372 overscan_ = 4;
373 }
374 else {
375 overscan_ = 0;
376 }
377 }
378 else {
379 write_state_.image_spec = ImageSpec();
380 overscan_ = 0;
381 }
382}
383
384void TileManager::set_temp_dir(const string &temp_dir)
385{
386 temp_dir_ = temp_dir;
387}
388
390{
391 return tile_state_.next_tile_index == tile_state_.num_tiles;
392}
393
395{
396 if (done()) {
397 return false;
398 }
399
400 tile_state_.current_tile = get_tile_for_index(tile_state_.next_tile_index);
401
402 ++tile_state_.next_tile_index;
403
404 return true;
405}
406
408{
409 /* TODO(sergey): Consider using hilbert spiral, or. maybe, even configurable. Not sure this
410 * brings a lot of value since this is only applicable to BIG tiles. */
411
412 const int tile_index_y = index / tile_state_.num_tiles_x;
413 const int tile_index_x = index - tile_index_y * tile_state_.num_tiles_x;
414
415 const int tile_window_x = tile_index_x * tile_size_.x;
416 const int tile_window_y = tile_index_y * tile_size_.y;
417
418 Tile tile;
419
420 tile.x = max(0, tile_window_x - overscan_);
421 tile.y = max(0, tile_window_y - overscan_);
422
423 tile.window_x = tile_window_x - tile.x;
424 tile.window_y = tile_window_y - tile.y;
425 tile.window_width = min(tile_size_.x, buffer_params_.width - tile_window_x);
426 tile.window_height = min(tile_size_.y, buffer_params_.height - tile_window_y);
427
428 tile.width = min(buffer_params_.width - tile.x, tile.window_x + tile.window_width + overscan_);
429 tile.height = min(buffer_params_.height - tile.y,
430 tile.window_y + tile.window_height + overscan_);
431
432 return tile;
433}
434
436{
437 return tile_state_.current_tile;
438}
439
441{
442 return make_int2(buffer_params_.width, buffer_params_.height);
443}
444
446{
448 "cycles-tile-buffer-" + tile_file_unique_part_ + "-" +
449 to_string(write_state_.tile_file_index) + ".exr");
450
451 write_state_.tile_out = ImageOutput::create(write_state_.filename);
452 if (!write_state_.tile_out) {
453 LOG_ERROR << "Error creating image output for " << write_state_.filename;
454 return false;
455 }
456
457 if (!write_state_.tile_out->supports("tiles")) {
458 LOG_ERROR << "Progress tile file format does not support tiling.";
459 return false;
460 }
461
462 if (!write_state_.tile_out->open(write_state_.filename, write_state_.image_spec)) {
463 LOG_ERROR << "Error opening tile file: " << write_state_.tile_out->geterror();
464 write_state_.tile_out = nullptr;
465 return false;
466 }
467
468 write_state_.num_tiles_written = 0;
469
470 LOG_DEBUG << "Opened tile file " << write_state_.filename;
471
472 return true;
473}
474
476{
477 if (!write_state_.tile_out) {
478 return true;
479 }
480
481 const bool success = write_state_.tile_out->close();
482 write_state_.tile_out = nullptr;
483
484 if (!success) {
485 LOG_ERROR << "Error closing tile file.";
486 return false;
487 }
488
489 LOG_DEBUG << "Tile output is closed.";
490
491 return true;
492}
493
494bool TileManager::write_tile(const RenderBuffers &tile_buffers)
495{
496 if (!write_state_.tile_out) {
497 if (!open_tile_output()) {
498 return false;
499 }
500 }
501
502 const double time_start = time_dt();
503
504 DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride);
505
506 const BufferParams &tile_params = tile_buffers.params;
507
508 const int tile_x = tile_params.full_x - buffer_params_.full_x + tile_params.window_x;
509 const int tile_y = tile_params.full_y - buffer_params_.full_y + tile_params.window_y;
510
511 const int64_t pass_stride = tile_params.pass_stride;
512 const int64_t tile_row_stride = tile_params.width * pass_stride;
513
514 vector<float> pixel_storage;
515 const float *pixels = tile_buffers.buffer.data() + tile_params.window_x * pass_stride +
516 tile_params.window_y * tile_row_stride;
517
518 /* If there is an overscan used for the tile copy pixels into single continuous block of memory
519 * without any "gaps".
520 * This is a workaround for bug in OIIO (https://github.com/OpenImageIO/oiio/pull/3176).
521 * Our task reference: #93008. */
522 if (tile_params.window_x || tile_params.window_y ||
523 tile_params.window_width != tile_params.width ||
524 tile_params.window_height != tile_params.height)
525 {
526 pixel_storage.resize(pass_stride * tile_params.window_width * tile_params.window_height);
527 float *pixels_continuous = pixel_storage.data();
528
529 const int64_t pixels_row_stride = pass_stride * tile_params.width;
530 const int64_t pixels_continuous_row_stride = pass_stride * tile_params.window_width;
531
532 for (int i = 0; i < tile_params.window_height; ++i) {
533 memcpy(pixels_continuous, pixels, sizeof(float) * pixels_continuous_row_stride);
534 pixels += pixels_row_stride;
535 pixels_continuous += pixels_continuous_row_stride;
536 }
537
538 pixels = pixel_storage.data();
539 }
540
541 LOG_DEBUG << "Write tile at " << tile_x << ", " << tile_y;
542
543 /* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The
544 * write_tiles() method expects a contiguous image region that will be split into tiles
545 * internally. OpenEXR expects the size of this region to be a multiple of the tile size,
546 * however OpenImageIO automatically adds the required padding.
547 *
548 * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the
549 * image tile size, which happens in compute_render_tile_size. */
550
551 const int64_t xstride = pass_stride * sizeof(float);
552 const int64_t ystride = xstride * tile_params.window_width;
553 const int64_t zstride = ystride * tile_params.window_height;
554
555 if (!write_state_.tile_out->write_tiles(tile_x,
556 tile_x + tile_params.window_width,
557 tile_y,
558 tile_y + tile_params.window_height,
559 0,
560 1,
561 TypeDesc::FLOAT,
562 pixels,
563 xstride,
564 ystride,
565 zstride))
566 {
567 LOG_ERROR << "Error writing tile " << write_state_.tile_out->geterror();
568 return false;
569 }
570
571 ++write_state_.num_tiles_written;
572
573 LOG_DEBUG << "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) {
588 vector<float> pixel_storage(tile_size_.x * tile_size_.y * buffer_params_.pass_stride);
589
590 for (int tile_index = write_state_.num_tiles_written; tile_index < tile_state_.num_tiles;
591 ++tile_index)
592 {
594
595 const int tile_x = tile.x + tile.window_x;
596 const int tile_y = tile.y + tile.window_y;
597
598 LOG_DEBUG << "Write dummy tile at " << tile_x << ", " << tile_y;
599
600 write_state_.tile_out->write_tiles(tile_x,
601 tile_x + tile.window_width,
602 tile_y,
603 tile_y + tile.window_height,
604 0,
605 1,
606 TypeDesc::FLOAT,
607 pixel_storage.data());
608 }
609 }
610
612
615 }
616
617 LOG_DEBUG << "Tile file size is "
619
620 /* Advance the counter upon explicit finish of the file.
621 * Makes it possible to re-use tile manager for another scene, and avoids unnecessary increments
622 * of the tile-file-within-session index. */
623 ++write_state_.tile_file_index;
624
625 write_state_.filename = "";
626}
627
630 DenoiseParams *denoise_params)
631{
632 unique_ptr<ImageInput> in(ImageInput::open(filename));
633 if (!in) {
634 LOG_ERROR << "Error opening tile file " << filename;
635 return false;
636 }
637
638 const ImageSpec &image_spec = in->spec();
639
640 BufferParams buffer_params;
642 return false;
643 }
644 buffers->reset(buffer_params);
645
647 return false;
648 }
649
650 const int num_channels = in->spec().nchannels;
651 if (!in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, buffers->buffer.data())) {
652 LOG_ERROR << "Error reading pixels from the tile file " << in->geterror();
653 return false;
654 }
655
656 if (!in->close()) {
657 LOG_ERROR << "Error closing tile file " << in->geterror();
658 return false;
659 }
660
661 return true;
662}
663
long long int int64_t
int pass_stride
Definition buffers.h:92
vector< BufferPass > passes
Definition buffers.h:95
int window_y
Definition buffers.h:78
int window_height
Definition buffers.h:80
int window_width
Definition buffers.h:79
NODE_DECLARE int width
Definition buffers.h:70
int window_x
Definition buffers.h:77
void update_passes()
Definition buffers.cpp:117
ustring name
Definition buffers.h:30
int offset
Definition buffers.h:34
PassInfo get_info() const
Definition buffers.cpp:69
DenoiseParams get_denoise_params() const
AdaptiveSampling get_adaptive_sampling() const
device_vector< float > buffer
Definition buffers.h:158
BufferParams params
Definition buffers.h:155
BufferParams buffer_params_
void set_temp_dir(const string &temp_dir)
string temp_dir_
struct TileManager::@125027310271160227025200215276036146163231274365 write_state_
struct TileManager::@317216067254110143221163173360002321176262313034 tile_state_
bool close_tile_output()
int compute_render_tile_size(const int suggested_tile_size) const
bool has_multiple_tiles() const
int2 get_size() const
void update(const BufferParams &params, const Scene *scene)
const Tile & get_current_tile() const
string filename
std::function< void(string_view)> full_buffer_written_cb
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
Tile get_tile_for_index(const int index) const
static const int MAX_TILE_SIZE
string tile_file_unique_part_
void finish_write_tiles()
void reset_scheduling(const BufferParams &params, const int2 tile_size)
static const int IMAGE_TILE_SIZE
nullptr float
#define PASS_UNUSED
#define CCL_NAMESPACE_END
ccl_device_forceinline int2 make_int2(const int x, const int y)
static const char * to_string(const Interpolation &interp)
Definition gl_shader.cc:103
#define in
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
const ccl_global KernelWorkTile * tile
const int tile_index
#define DCHECK_EQ(a, b)
Definition log.h:144
#define LOG_DFATAL
Definition log.h:100
#define LOG_DEBUG
Definition log.h:107
#define DCHECK_GT(a, b)
Definition log.h:145
#define LOG_ERROR
Definition log.h:101
#define DCHECK_NE(a, b)
Definition log.h:143
size_t path_file_size(const string &path)
Definition path.cpp:554
string path_join(const string &dir, const string &file)
Definition path.cpp:415
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.cc:36
string string_human_readable_number(size_t num)
Definition string.cpp:276
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
bool exists(ustring x) const
Definition node_enum.h:29
vector< SocketType, std::allocator< SocketType > > inputs
Definition node_type.h:128
float get_float(const SocketType &input) const
void set(const SocketType &input, bool value)
const NodeType * type
Definition graph/node.h:178
bool get_bool(const SocketType &input) const
ustring get_string(const SocketType &input) const
int get_int(const SocketType &input) const
int num_components
Definition pass.h:28
unique_ptr< BakeManager > bake_manager
Definition scene.h:152
Integrator * integrator
Definition scene.h:130
ustring name
Definition node_type.h:81
Type type
Definition node_type.h:82
const NodeEnum * enum_values
Definition node_type.h:85
uint64_t system_self_process_id()
Definition system.cpp:248
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:47
ccl_device_inline size_t divide_up(const size_t x, const size_t y)
Definition types_base.h:52
ccl_device_inline size_t align_up(const size_t offset, const size_t alignment)
Definition types_base.h:47
char * buffers[2]