Blender V5.0
cycles_standalone.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 <cstdio>
6
7#include "device/device.h"
8#include "scene/camera.h"
9#include "scene/integrator.h"
10#include "scene/scene.h"
11#include "session/buffers.h"
12#include "session/session.h"
13
14#include "util/args.h"
15#include "util/log.h"
16#include "util/path.h"
17#include "util/progress.h"
18#include "util/string.h"
19#ifdef WITH_CYCLES_STANDALONE_GUI
20# include "util/time.h"
21# include "util/transform.h"
22#endif
23#include "util/unique_ptr.h"
24#include "util/version.h"
25
26#ifdef WITH_USD
27# include "hydra/file_reader.h"
28#endif
29
30#include "app/cycles_xml.h"
32
33#ifdef WITH_CYCLES_STANDALONE_GUI
35# include "opengl/window.h"
36#endif
37
39
52
53static void session_print(const string &str)
54{
55 /* print with carriage return to overwrite previous */
56 printf("\r%s", str.c_str());
57
58 /* add spaces to overwrite longer previous print */
59 static int maxlen = 0;
60 const int len = str.size();
61 maxlen = max(len, maxlen);
62
63 for (int i = len; i < maxlen; i++) {
64 printf(" ");
65 }
66
67 /* flush because we don't write an end of line */
68 fflush(stdout);
69}
70
72{
73 string status;
74 string substatus;
75
76 /* get status */
77 const double progress = options.session->progress.get_progress();
78 options.session->progress.get_status(status, substatus);
79
80 if (!substatus.empty()) {
81 status += ": " + substatus;
82 }
83
84 /* print status */
85 status = string_printf("Progress %05.2f %s", progress * 100, status.c_str());
87}
88
90{
91 static BufferParams buffer_params;
92 buffer_params.width = options.width;
93 buffer_params.height = options.height;
94 buffer_params.full_width = options.width;
95 buffer_params.full_height = options.height;
96
97 return buffer_params;
98}
99
100static void scene_init()
101{
102 options.scene = options.session->scene.get();
103
104 /* Read XML or USD */
105#ifdef WITH_USD
106 if (!string_endswith(string_to_lower(options.filepath), ".xml")) {
107 HD_CYCLES_NS::HdCyclesFileReader::read(options.session.get(), options.filepath.c_str());
108 }
109 else
110#endif
111 {
112 xml_read_file(options.scene, options.filepath.c_str());
113 }
114
115 /* Camera width/height override? */
116 if (!(options.width == 0 || options.height == 0)) {
117 options.scene->camera->set_full_width(options.width);
118 options.scene->camera->set_full_height(options.height);
119 }
120 else {
121 options.width = options.scene->camera->get_full_width();
122 options.height = options.scene->camera->get_full_height();
123 }
124
125 /* Calculate Viewplane */
126 options.scene->camera->compute_auto_viewplane();
127}
128
129static void session_init()
130{
131 options.output_pass = "combined";
132 options.session = make_unique<Session>(options.session_params, options.scene_params);
133
134#ifdef WITH_CYCLES_STANDALONE_GUI
135 if (!options.session_params.background) {
136 options.session->set_display_driver(make_unique<OpenGLDisplayDriver>(
138 }
139#endif
140
141 if (!options.output_filepath.empty()) {
142 options.session->set_output_driver(make_unique<OIIOOutputDriver>(
143 options.output_filepath, options.output_pass, session_print));
144 }
145
146 if (options.session_params.background && !options.quiet) {
147 options.session->progress.set_update_callback([] { session_print_status(); });
148 }
149#ifdef WITH_CYCLES_STANDALONE_GUI
150 else {
151 options.session->progress.set_update_callback([] { window_redraw(); });
152 }
153#endif
154
155 /* load scene */
156 scene_init();
157
158 /* add pass for output. */
159 Pass *pass = options.scene->create_node<Pass>();
160 pass->set_name(ustring(options.output_pass.c_str()));
161 pass->set_type(PASS_COMBINED);
162
163 options.session->reset(options.session_params, session_buffer_params());
164 options.session->start();
165}
166
167static void session_exit()
168{
169 if (options.session) {
170 options.session.reset();
171 }
172
173 if (options.session_params.background && !options.quiet) {
174 session_print("Finished Rendering.");
175 printf("\n");
176 }
177}
178
179#ifdef WITH_CYCLES_STANDALONE_GUI
180static void display_info(Progress &progress)
181{
182 static double latency = 0.0;
183 static double last = 0;
184 const double elapsed = time_dt();
185 string str;
186 string interactive;
187
188 latency = (elapsed - last);
189 last = elapsed;
190
191 double total_time;
192 double sample_time;
193 string status;
194 string substatus;
195
196 progress.get_time(total_time, sample_time);
197 progress.get_status(status, substatus);
198 const double progress_val = progress.get_progress();
199
200 if (!substatus.empty()) {
201 status += ": " + substatus;
202 }
203
204 interactive = options.interactive ? "On" : "Off";
205
207 "%s"
208 " Time: %.2f"
209 " Latency: %.4f"
210 " Progress: %05.2f"
211 " Average: %.4f"
212 " Interactive: %s",
213 status.c_str(),
215 latency,
216 progress_val * 100,
217 sample_time,
218 interactive.c_str());
219
220 window_display_info(str.c_str());
221
222 if (options.show_help) {
224 }
225}
226
227static void display()
228{
229 options.session->draw();
230
231 display_info(options.session->progress);
232}
233
234static void motion(const int x, const int y, int button)
235{
236 if (options.interactive) {
237 Transform matrix = options.session->scene->camera->get_matrix();
238
239 /* Translate */
240 if (button == 0) {
241 const float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
242 matrix = matrix * transform_translate(translate);
243 }
244
245 /* Rotate */
246 else if (button == 2) {
247 const float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f);
248 matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w));
249
250 const float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f);
251 matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w));
252 }
253
254 /* Update and Reset */
255 options.session->scene->camera->set_matrix(matrix);
256 options.session->scene->camera->need_flags_update = true;
257 options.session->scene->camera->need_device_update = true;
258
259 options.session->reset(options.session_params, session_buffer_params());
260 }
261}
262
263static void resize(const int width, const int height)
264{
265 options.width = width;
266 options.height = height;
267
268 if (options.session) {
269 /* Update camera */
270 options.session->scene->camera->set_full_width(options.width);
271 options.session->scene->camera->set_full_height(options.height);
272 options.session->scene->camera->compute_auto_viewplane();
273 options.session->scene->camera->need_flags_update = true;
274 options.session->scene->camera->need_device_update = true;
275
276 options.session->reset(options.session_params, session_buffer_params());
277 }
278}
279
280static void keyboard(unsigned char key)
281{
282 /* Toggle help */
283 if (key == 'h') {
284 options.show_help = !(options.show_help);
285
286 /* Reset */
287 }
288 else if (key == 'r') {
289 options.session->reset(options.session_params, session_buffer_params());
290
291 /* Cancel */
292 }
293 else if (key == 27) { // escape
294 options.session->progress.set_cancel("Canceled");
295
296 /* Pause */
297 }
298 else if (key == 'p') {
299 options.pause = !options.pause;
300 options.session->set_pause(options.pause);
301 }
302
303 /* Interactive Mode */
304 else if (key == 'i') {
305 options.interactive = !(options.interactive);
306
307 /* Navigation */
308 }
309 else if (options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
310 Transform matrix = options.session->scene->camera->get_matrix();
312
313 if (key == 'w') {
314 translate = make_float3(0.0f, 0.0f, 0.1f);
315 }
316 else if (key == 's') {
317 translate = make_float3(0.0f, 0.0f, -0.1f);
318 }
319 else if (key == 'a') {
320 translate = make_float3(-0.1f, 0.0f, 0.0f);
321 }
322 else if (key == 'd') {
323 translate = make_float3(0.1f, 0.0f, 0.0f);
324 }
325
326 matrix = matrix * transform_translate(translate);
327
328 /* Update and Reset */
329 options.session->scene->camera->set_matrix(matrix);
330 options.session->scene->camera->need_flags_update = true;
331 options.session->scene->camera->need_device_update = true;
332
333 options.session->reset(options.session_params, session_buffer_params());
334 }
335
336 /* Set Max Bounces */
337 else if (options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) {
338 int bounce;
339 switch (key) {
340 case '0':
341 bounce = 0;
342 break;
343 case '1':
344 bounce = 1;
345 break;
346 case '2':
347 bounce = 2;
348 break;
349 case '3':
350 bounce = 3;
351 break;
352 default:
353 bounce = 0;
354 break;
355 }
356
357 options.session->scene->integrator->set_max_bounce(bounce);
358
359 options.session->reset(options.session_params, session_buffer_params());
360 }
361}
362#endif
363
364static void parse_int(OIIO::cspan<const char *> argv, int *i)
365{
366 assert(argv.size() == 2);
367 *i = atoi(argv[1]);
368}
369
370static void parse_string(OIIO::cspan<const char *> argv, std::string *s)
371{
372 assert(argv.size() == 2);
373 *s = argv[1];
374}
375
376static void options_parse(const int argc, const char **argv)
377{
378 options.width = 1024;
379 options.height = 512;
380 options.filepath = "";
381 options.session = nullptr;
382 options.quiet = false;
383 options.session_params.use_auto_tile = false;
384 options.session_params.tile_size = 0;
385
386 /* device names */
387 string device_names;
388 string devicename = "CPU";
389 bool list = false;
390
391 /* List devices for which support is compiled in. */
393 for (const DeviceType type : types) {
394 if (!device_names.empty()) {
395 device_names += ", ";
396 }
397
398 device_names += Device::string_from_type(type);
399 }
400
401 /* shading system */
402 string ssname = "svm";
403
404 /* parse options */
405 ArgParse ap;
406 bool help = false;
407 bool profile = false;
408 bool version = false;
409 string log_level;
410
411 ap.usage("cycles [options] file.xml");
412 ap.arg("filename").hidden().action([&](auto argv) { options.filepath = argv[0]; });
413 ap.arg("--device %s:DEVICE").help("Devices to use: " + device_names).action([&](auto argv) {
414 parse_string(argv, &devicename);
415 });
416#ifdef WITH_OSL
417 ap.arg("--shadingsys %s:SHADINGSYSTEM")
418 .help("Shading system to use: svm, osl")
419 .action([&](auto argv) { parse_string(argv, &ssname); });
420#endif
421 ap.arg("--background", &options.session_params.background)
422 .help("Render in background, without user interface");
423 ap.arg("--quiet", &options.quiet).help("In background mode, don't print progress messages");
424 ap.arg("--samples %d:SAMPLES").help("Number of samples to render").action([&](auto argv) {
425 parse_int(argv, &options.session_params.samples);
426 });
427 ap.arg("--output %s:OUTPUT").help("File path to write output image").action([&](auto argv) {
428 parse_string(argv, &options.output_filepath);
429 });
430 ap.arg("--threads %d:THREADS").help("CPU Rendering Threads").action([&](auto argv) {
431 parse_int(argv, &options.session_params.threads);
432 });
433 ap.arg("--width %d:WIDTH").help("Image width in pixelx").action([&](auto argv) {
434 parse_int(argv, &options.width);
435 });
436 ap.arg("--height %d:HEIGHT").help("Image height in pixel").action([&](auto argv) {
437 parse_int(argv, &options.height);
438 });
439 ap.arg("--tile-size %d:TILE_SIZE").help("Tile size in pixels").action([&](auto argv) {
440 parse_int(argv, &options.session_params.tile_size);
441 });
442 ap.arg("--list-devices", &list).help("List information about all available devices");
443 ap.arg("--profile", &profile).help("Enable profile logging");
444 ap.arg("--log-level %s:LEVEL")
445 .help("Log verbosity: fatal, error, warning, info, stats, debug")
446 .action([&](auto argv) { parse_string(argv, &log_level); });
447 ap.arg("--help", &help).help("Print help message");
448 ap.arg("--version", &version).help("Print version number");
449
450 if (ap.parse_args(argc, argv) < 0) {
451 fprintf(stderr, "%s\n", ap.geterror().c_str());
452 ap.print_help();
453 exit(EXIT_FAILURE);
454 }
455
456 if (!log_level.empty()) {
457 log_level_set(log_level);
458 }
459
460 if (list) {
462 printf("Devices:\n");
463
464 for (const DeviceInfo &info : devices) {
465 printf(" %-10s%s%s\n",
466 Device::string_from_type(info.type).c_str(),
467 info.description.c_str(),
468 (info.display_device) ? " (display)" : "");
469 }
470
471 exit(EXIT_SUCCESS);
472 }
473 else if (version) {
475 exit(EXIT_SUCCESS);
476 }
477 else if (help || options.filepath.empty()) {
478 ap.print_help();
479 exit(EXIT_SUCCESS);
480 }
481
482 options.session_params.use_profiling = profile;
483
484 if (ssname == "osl") {
485 options.scene_params.shadingsystem = SHADINGSYSTEM_OSL;
486 }
487 else if (ssname == "svm") {
488 options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
489 }
490
491#ifndef WITH_CYCLES_STANDALONE_GUI
492 options.session_params.background = true;
493#endif
494
495 if (options.session_params.tile_size > 0) {
496 options.session_params.use_auto_tile = true;
497 }
498
499 /* find matching device */
500 const DeviceType device_type = Device::type_from_string(devicename.c_str());
502
503 bool device_available = false;
504 if (!devices.empty()) {
505 options.session_params.device = devices.front();
506 device_available = true;
507 }
508
509 /* handle invalid configurations */
510 if (options.session_params.device.type == DEVICE_NONE || !device_available) {
511 fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
512 exit(EXIT_FAILURE);
513 }
514#ifdef WITH_OSL
515 else if (!(ssname == "osl" || ssname == "svm")) {
516 fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
517 exit(EXIT_FAILURE);
518 }
519 else if (options.scene_params.shadingsystem == SHADINGSYSTEM_OSL &&
520 options.session_params.device.type != DEVICE_CPU)
521 {
522 fprintf(stderr, "OSL shading system only works with CPU device\n");
523 exit(EXIT_FAILURE);
524 }
525#endif
526 else if (options.session_params.samples < 0) {
527 fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
528 exit(EXIT_FAILURE);
529 }
530 else if (options.filepath.empty()) {
531 fprintf(stderr, "No file path specified\n");
532 exit(EXIT_FAILURE);
533 }
534}
535
537
538using namespace ccl;
539
540int main(const int argc, const char **argv)
541{
542 log_init(nullptr);
543 path_init();
544 options_parse(argc, argv);
545
546#ifdef WITH_CYCLES_STANDALONE_GUI
547 if (options.session_params.background) {
548#endif
549 session_init();
550 options.session->wait();
551 session_exit();
552#ifdef WITH_CYCLES_STANDALONE_GUI
553 }
554 else {
555 const string title = "Cycles: " + path_filename(options.filepath);
556
557 /* init/exit are callback so they run while GL is initialized */
558 window_main_loop(title.c_str(),
559 options.width,
560 options.height,
563 resize,
564 display,
565 keyboard,
566 motion);
567 }
568#endif
569
570 return 0;
571}
#define DEG2RADF(_deg)
int full_width
Definition buffers.h:85
int full_height
Definition buffers.h:86
NODE_DECLARE int width
Definition buffers.h:70
static DeviceType type_from_string(const char *name)
static vector< DeviceType > available_types()
static string string_from_type(DeviceType type)
static vector< DeviceInfo > available_devices(const uint device_type_mask=DEVICE_MASK_ALL)
Definition pass.h:50
void get_status(string &status_, string &substatus_) const
Definition progress.h:290
void get_time(double &total_time_, double &render_time_) const
Definition progress.h:153
double get_progress() const
Definition progress.h:185
static void session_init()
static void options_parse(const int argc, const char **argv)
static void scene_init()
static BufferParams & session_buffer_params()
static void session_print_status()
static void session_exit()
static void parse_string(OIIO::cspan< const char * > argv, std::string *s)
CCL_NAMESPACE_BEGIN struct Options options
static void session_print(const string &str)
static void parse_int(OIIO::cspan< const char * > argv, int *i)
void xml_read_file(Scene *scene, const char *filepath)
#define CCL_NAMESPACE_END
#define DEVICE_MASK(type)
DeviceType
@ DEVICE_NONE
@ DEVICE_CPU
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define str(s)
#define assert(assertion)
#define main()
#define printf(...)
@ PASS_COMBINED
void log_init(const LogFunction func)
Definition log.cpp:72
void log_level_set(const LogLevel level)
Definition log.cpp:78
static char ** types
Definition makesdna.cc:71
MatBase< T, NumCol, NumRow > translate(const MatBase< T, NumCol, NumRow > &mat, const VectorT &translation)
string path_filename(const string &path)
Definition path.cpp:378
void path_init(const string &path, const string &user_path)
Definition path.cpp:324
const int status
#define make_float4
@ SHADINGSYSTEM_OSL
@ SHADINGSYSTEM_SVM
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
bool string_endswith(const string_view s, const string_view end)
Definition string.cpp:120
string string_to_lower(const string &s)
Definition string.cpp:210
SceneParams scene_params
SessionParams session_params
unique_ptr< Session > session
string output_filepath
float y
Definition sky_math.h:225
float z
Definition sky_math.h:225
float x
Definition sky_math.h:225
float w
Definition sky_math.h:225
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 Transform transform_rotate(const float angle, float3 axis)
Definition transform.h:290
ccl_device_inline Transform transform_translate(const float3 t)
Definition transform.h:270
uint len
#define CYCLES_VERSION_STRING
Definition version.h:19
void window_opengl_context_disable()
Definition window.cpp:258
bool window_opengl_context_enable()
Definition window.cpp:251
void window_display_info(const char *info)
Definition window.cpp:76
void window_main_loop(const char *title, const int width, const int height, WindowInitFunc initf, WindowExitFunc exitf, WindowResizeFunc resize, WindowDisplayFunc display, WindowKeyboardFunc keyboard, WindowMotionFunc motion)
Definition window.cpp:264
void window_redraw()
Definition window.cpp:352
void window_display_help()
Definition window.cpp:97
double total_time