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