Fix Cycles gtests build on macOS.
[blender.git] / intern / cycles / app / cycles_standalone.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18
19 #include "render/buffers.h"
20 #include "render/camera.h"
21 #include "device/device.h"
22 #include "render/scene.h"
23 #include "render/session.h"
24 #include "render/integrator.h"
25
26 #include "util/util_args.h"
27 #include "util/util_foreach.h"
28 #include "util/util_function.h"
29 #include "util/util_logging.h"
30 #include "util/util_path.h"
31 #include "util/util_progress.h"
32 #include "util/util_string.h"
33 #include "util/util_time.h"
34 #include "util/util_transform.h"
35 #include "util/util_version.h"
36
37 #ifdef WITH_CYCLES_STANDALONE_GUI
38 #include "util/util_view.h"
39 #endif
40
41 #include "app/cycles_xml.h"
42
43 CCL_NAMESPACE_BEGIN
44
45 struct Options {
46         Session *session;
47         Scene *scene;
48         string filepath;
49         int width, height;
50         SceneParams scene_params;
51         SessionParams session_params;
52         bool quiet;
53         bool show_help, interactive, pause;
54 } options;
55
56 static void session_print(const string& str)
57 {
58         /* print with carriage return to overwrite previous */
59         printf("\r%s", str.c_str());
60
61         /* add spaces to overwrite longer previous print */
62         static int maxlen = 0;
63         int len = str.size();
64         maxlen = max(len, maxlen);
65
66         for(int i = len; i < maxlen; i++)
67                 printf(" ");
68
69         /* flush because we don't write an end of line */
70         fflush(stdout);
71 }
72
73 static void session_print_status()
74 {
75         string status, substatus;
76
77         /* get status */
78         float progress = options.session->progress.get_progress();
79         options.session->progress.get_status(status, substatus);
80
81         if(substatus != "")
82                 status += ": " + substatus;
83
84         /* print status */
85         status = string_printf("Progress %05.2f   %s", (double) progress*100, status.c_str());
86         session_print(status);
87 }
88
89 static BufferParams& session_buffer_params()
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
100 static void scene_init()
101 {
102         options.scene = new Scene(options.scene_params, options.session->device);
103
104         /* Read XML */
105         xml_read_file(options.scene, options.filepath.c_str());
106
107         /* Camera width/height override? */
108         if(!(options.width == 0 || options.height == 0)) {
109                 options.scene->camera->width = options.width;
110                 options.scene->camera->height = options.height;
111         }
112         else {
113                 options.width = options.scene->camera->width;
114                 options.height = options.scene->camera->height;
115         }
116
117         /* Calculate Viewplane */
118         options.scene->camera->compute_auto_viewplane();
119 }
120
121 static void session_init()
122 {
123         options.session = new Session(options.session_params);
124         options.session->reset(session_buffer_params(), options.session_params.samples);
125
126         if(options.session_params.background && !options.quiet)
127                 options.session->progress.set_update_callback(function_bind(&session_print_status));
128 #ifdef WITH_CYCLES_STANDALONE_GUI
129         else
130                 options.session->progress.set_update_callback(function_bind(&view_redraw));
131 #endif
132
133         options.session->start();
134
135         /* load scene */
136         scene_init();
137         options.session->scene = options.scene;
138 }
139
140 static void session_exit()
141 {
142         if(options.session) {
143                 delete options.session;
144                 options.session = NULL;
145         }
146         if(options.scene) {
147                 delete options.scene;
148                 options.scene = NULL;
149         }
150
151         if(options.session_params.background && !options.quiet) {
152                 session_print("Finished Rendering.");
153                 printf("\n");
154         }
155 }
156
157 #ifdef WITH_CYCLES_STANDALONE_GUI
158 static void display_info(Progress& progress)
159 {
160         static double latency = 0.0;
161         static double last = 0;
162         double elapsed = time_dt();
163         string str, interactive;
164
165         latency = (elapsed - last);
166         last = elapsed;
167
168         double total_time, sample_time;
169         string status, substatus;
170
171         progress.get_time(total_time, sample_time);
172         progress.get_status(status, substatus);
173         float progress_val = progress.get_progress();
174
175         if(substatus != "")
176                 status += ": " + substatus;
177
178         interactive = options.interactive? "On":"Off";
179
180         str = string_printf(
181                 "%s"
182                 "        Time: %.2f"
183                 "        Latency: %.4f"
184                 "        Progress: %05.2f"
185                 "        Average: %.4f"
186                 "        Interactive: %s",
187                 status.c_str(), total_time, latency, (double) progress_val*100, sample_time, interactive.c_str());
188
189         view_display_info(str.c_str());
190
191         if(options.show_help)
192                 view_display_help();
193 }
194
195 static void display()
196 {
197         static DeviceDrawParams draw_params = DeviceDrawParams();
198
199         options.session->draw(session_buffer_params(), draw_params);
200
201         display_info(options.session->progress);
202 }
203
204 static void motion(int x, int y, int button)
205 {
206         if(options.interactive) {
207                 Transform matrix = options.session->scene->camera->matrix;
208
209                 /* Translate */
210                 if(button == 0) {
211                         float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
212                         matrix = matrix * transform_translate(translate);
213                 }
214
215                 /* Rotate */
216                 else if(button == 2) {
217                         float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f);
218                         matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w));
219
220                         float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f);
221                         matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w));
222                 }
223
224                 /* Update and Reset */
225                 options.session->scene->camera->matrix = matrix;
226                 options.session->scene->camera->need_update = true;
227                 options.session->scene->camera->need_device_update = true;
228
229                 options.session->reset(session_buffer_params(), options.session_params.samples);
230         }
231 }
232
233 static void resize(int width, int height)
234 {
235         options.width = width;
236         options.height = height;
237
238         if(options.session) {
239                 /* Update camera */
240                 options.session->scene->camera->width = width;
241                 options.session->scene->camera->height = height;
242                 options.session->scene->camera->compute_auto_viewplane();
243                 options.session->scene->camera->need_update = true;
244                 options.session->scene->camera->need_device_update = true;
245
246                 options.session->reset(session_buffer_params(), options.session_params.samples);
247         }
248 }
249
250 static void keyboard(unsigned char key)
251 {
252         /* Toggle help */
253         if(key == 'h')
254                 options.show_help = !(options.show_help);
255
256         /* Reset */
257         else if(key == 'r')
258                 options.session->reset(session_buffer_params(), options.session_params.samples);
259
260         /* Cancel */
261         else if(key == 27) // escape
262                 options.session->progress.set_cancel("Canceled");
263
264         /* Pause */
265         else if(key == 'p') {
266                 options.pause = !options.pause;
267                 options.session->set_pause(options.pause);
268         }
269
270         /* Interactive Mode */
271         else if(key == 'i')
272                 options.interactive = !(options.interactive);
273
274         /* Navigation */
275         else if(options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
276                 Transform matrix = options.session->scene->camera->matrix;
277                 float3 translate;
278
279                 if(key == 'w')
280                         translate = make_float3(0.0f, 0.0f, 0.1f);
281                 else if(key == 's')
282                         translate = make_float3(0.0f, 0.0f, -0.1f);
283                 else if(key == 'a')
284                         translate = make_float3(-0.1f, 0.0f, 0.0f);
285                 else if(key == 'd')
286                         translate = make_float3(0.1f, 0.0f, 0.0f);
287
288                 matrix = matrix * transform_translate(translate);
289
290                 /* Update and Reset */
291                 options.session->scene->camera->matrix = matrix;
292                 options.session->scene->camera->need_update = true;
293                 options.session->scene->camera->need_device_update = true;
294
295                 options.session->reset(session_buffer_params(), options.session_params.samples);
296         }
297
298         /* Set Max Bounces */
299         else if(options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) {
300                 int bounce;
301                 switch(key) {
302                         case '0': bounce = 0; break;
303                         case '1': bounce = 1; break;
304                         case '2': bounce = 2; break;
305                         case '3': bounce = 3; break;
306                         default: bounce = 0; break;
307                 }
308
309                 options.session->scene->integrator->max_bounce = bounce;
310
311                 /* Update and Reset */
312                 options.session->scene->integrator->need_update = true;
313
314                 options.session->reset(session_buffer_params(), options.session_params.samples);
315         }
316 }
317 #endif
318
319 static int files_parse(int argc, const char *argv[])
320 {
321         if(argc > 0)
322                 options.filepath = argv[0];
323
324         return 0;
325 }
326
327 static void options_parse(int argc, const char **argv)
328 {
329         options.width = 0;
330         options.height = 0;
331         options.filepath = "";
332         options.session = NULL;
333         options.quiet = false;
334
335         /* device names */
336         string device_names = "";
337         string devicename = "CPU";
338         bool list = false;
339
340         vector<DeviceType>& types = Device::available_types();
341
342         /* TODO(sergey): Here's a feedback loop happens: on the one hand we want
343          * the device list to be printed in help message, on the other hand logging
344          * is not initialized yet so we wouldn't have debug log happening in the
345          * device initialization.
346          */
347         foreach(DeviceType type, types) {
348                 if(device_names != "")
349                         device_names += ", ";
350
351                 device_names += Device::string_from_type(type);
352         }
353
354         /* shading system */
355         string ssname = "svm";
356
357         /* parse options */
358         ArgParse ap;
359         bool help = false, debug = false, version = false;
360         int verbosity = 1;
361
362         ap.options ("Usage: cycles [options] file.xml",
363                 "%*", files_parse, "",
364                 "--device %s", &devicename, ("Devices to use: " + device_names).c_str(),
365 #ifdef WITH_OSL
366                 "--shadingsys %s", &ssname, "Shading system to use: svm, osl",
367 #endif
368                 "--background", &options.session_params.background, "Render in background, without user interface",
369                 "--quiet", &options.quiet, "In background mode, don't print progress messages",
370                 "--samples %d", &options.session_params.samples, "Number of samples to render",
371                 "--output %s", &options.session_params.output_path, "File path to write output image",
372                 "--threads %d", &options.session_params.threads, "CPU Rendering Threads",
373                 "--width  %d", &options.width, "Window width in pixel",
374                 "--height %d", &options.height, "Window height in pixel",
375                 "--tile-width %d", &options.session_params.tile_size.x, "Tile width in pixels",
376                 "--tile-height %d", &options.session_params.tile_size.y, "Tile height in pixels",
377                 "--list-devices", &list, "List information about all available devices",
378 #ifdef WITH_CYCLES_LOGGING
379                 "--debug", &debug, "Enable debug logging",
380                 "--verbose %d", &verbosity, "Set verbosity of the logger",
381 #endif
382                 "--help", &help, "Print help message",
383                 "--version", &version, "Print version number",
384                 NULL);
385
386         if(ap.parse(argc, argv) < 0) {
387                 fprintf(stderr, "%s\n", ap.geterror().c_str());
388                 ap.usage();
389                 exit(EXIT_FAILURE);
390         }
391
392         if(debug) {
393                 util_logging_start();
394                 util_logging_verbosity_set(verbosity);
395         }
396
397         if(list) {
398                 vector<DeviceInfo>& devices = Device::available_devices();
399                 printf("Devices:\n");
400
401                 foreach(DeviceInfo& info, devices) {
402                         printf("    %-10s%s%s\n",
403                                 Device::string_from_type(info.type).c_str(),
404                                 info.description.c_str(),
405                                 (info.display_device)? " (display)": "");
406                 }
407
408                 exit(EXIT_SUCCESS);
409         }
410         else if(version) {
411                 printf("%s\n", CYCLES_VERSION_STRING);
412                 exit(EXIT_SUCCESS);
413         }
414         else if(help || options.filepath == "") {
415                 ap.usage();
416                 exit(EXIT_SUCCESS);
417         }
418
419         if(ssname == "osl")
420                 options.scene_params.shadingsystem = SHADINGSYSTEM_OSL;
421         else if(ssname == "svm")
422                 options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
423
424 #ifndef WITH_CYCLES_STANDALONE_GUI
425         options.session_params.background = true;
426 #endif
427
428         /* Use progressive rendering */
429         options.session_params.progressive = true;
430
431         /* find matching device */
432         DeviceType device_type = Device::type_from_string(devicename.c_str());
433         vector<DeviceInfo>& devices = Device::available_devices();
434         bool device_available = false;
435
436         foreach(DeviceInfo& device, devices) {
437                 if(device_type == device.type) {
438                         options.session_params.device = device;
439                         device_available = true;
440                         break;
441                 }
442         }
443
444         /* handle invalid configurations */
445         if(options.session_params.device.type == DEVICE_NONE || !device_available) {
446                 fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
447                 exit(EXIT_FAILURE);
448         }
449 #ifdef WITH_OSL
450         else if(!(ssname == "osl" || ssname == "svm")) {
451                 fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
452                 exit(EXIT_FAILURE);
453         }
454         else if(options.scene_params.shadingsystem == SHADINGSYSTEM_OSL && options.session_params.device.type != DEVICE_CPU) {
455                 fprintf(stderr, "OSL shading system only works with CPU device\n");
456                 exit(EXIT_FAILURE);
457         }
458 #endif
459         else if(options.session_params.samples < 0) {
460                 fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
461                 exit(EXIT_FAILURE);
462         }
463         else if(options.filepath == "") {
464                 fprintf(stderr, "No file path specified\n");
465                 exit(EXIT_FAILURE);
466         }
467
468         /* For smoother Viewport */
469         options.session_params.start_resolution = 64;
470 }
471
472 CCL_NAMESPACE_END
473
474 using namespace ccl;
475
476 int main(int argc, const char **argv)
477 {
478         util_logging_init(argv[0]);
479         path_init();
480         options_parse(argc, argv);
481
482 #ifdef WITH_CYCLES_STANDALONE_GUI
483         if(options.session_params.background) {
484 #endif
485                 session_init();
486                 options.session->wait();
487                 session_exit();
488 #ifdef WITH_CYCLES_STANDALONE_GUI
489         }
490         else {
491                 string title = "Cycles: " + path_filename(options.filepath);
492
493                 /* init/exit are callback so they run while GL is initialized */
494                 view_main_loop(title.c_str(), options.width, options.height,
495                         session_init, session_exit, resize, display, keyboard, motion);
496         }
497 #endif
498
499         return 0;
500 }
501