Cuda use streams and async to avoid busywaiting
[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 "buffers.h"
20 #include "camera.h"
21 #include "device.h"
22 #include "scene.h"
23 #include "session.h"
24
25 #include "util_args.h"
26 #include "util_foreach.h"
27 #include "util_function.h"
28 #include "util_path.h"
29 #include "util_progress.h"
30 #include "util_string.h"
31 #include "util_time.h"
32 #include "util_transform.h"
33
34 #ifdef WITH_CYCLES_STANDALONE_GUI
35 #include "util_view.h"
36 #endif
37
38 #include "cycles_xml.h"
39
40 CCL_NAMESPACE_BEGIN
41
42 struct Options {
43         Session *session;
44         Scene *scene;
45         string filepath;
46         int width, height;
47         SceneParams scene_params;
48         SessionParams session_params;
49         bool quiet;
50         bool show_help, interactive, pause;
51 } options;
52
53 static 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         int len = str.size();
61         maxlen = max(len, maxlen);
62
63         for(int i = len; i < maxlen; i++)
64                 printf(" ");
65
66         /* flush because we don't write an end of line */
67         fflush(stdout);
68 }
69
70 static void session_print_status()
71 {
72         int sample, tile;
73         double total_time, sample_time;
74         string status, substatus;
75
76         /* get status */
77         sample = options.session->progress.get_sample();
78         options.session->progress.get_tile(tile, total_time, sample_time);
79         options.session->progress.get_status(status, substatus);
80
81         if(substatus != "")
82                 status += ": " + substatus;
83
84         /* print status */
85         status = string_printf("Sample %d   %s", sample, 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 session_init()
101 {
102         options.session = new Session(options.session_params);
103         options.session->reset(session_buffer_params(), options.session_params.samples);
104         options.session->scene = options.scene;
105
106         if(options.session_params.background && !options.quiet)
107                 options.session->progress.set_update_callback(function_bind(&session_print_status));
108 #ifdef WITH_CYCLES_STANDALONE_GUI
109         else
110                 options.session->progress.set_update_callback(function_bind(&view_redraw));
111 #endif
112
113         options.session->start();
114
115         options.scene = NULL;
116 }
117
118 static void scene_init()
119 {
120         options.scene = new Scene(options.scene_params, options.session_params.device);
121
122         /* Read XML */
123         xml_read_file(options.scene, options.filepath.c_str());
124
125         /* Camera width/height override? */
126         if (!(options.width == 0 || options.height == 0)) {
127                 options.scene->camera->width = options.width;
128                 options.scene->camera->height = options.height;
129         }
130         else {
131                 options.width = options.scene->camera->width;
132                 options.height = options.scene->camera->height;
133         }
134
135         /* Calculate Viewplane */
136         options.scene->camera->compute_auto_viewplane();
137 }
138
139 static void session_exit()
140 {
141         if(options.session) {
142                 delete options.session;
143                 options.session = NULL;
144         }
145         if(options.scene) {
146                 delete options.scene;
147                 options.scene = NULL;
148         }
149
150         if(options.session_params.background && !options.quiet) {
151                 session_print("Finished Rendering.");
152                 printf("\n");
153         }
154 }
155
156 #ifdef WITH_CYCLES_STANDALONE_GUI
157 static void display_info(Progress& progress)
158 {
159         static double latency = 0.0;
160         static double last = 0;
161         double elapsed = time_dt();
162         string str, interactive;
163
164         latency = (elapsed - last);
165         last = elapsed;
166
167         int sample, tile;
168         double total_time, sample_time;
169         string status, substatus;
170
171         sample = progress.get_sample();
172         progress.get_tile(tile, total_time, sample_time);
173         progress.get_status(status, substatus);
174
175         if(substatus != "")
176                 status += ": " + substatus;
177
178         interactive = options.interactive? "On":"Off";
179
180         str = string_printf("%s        Time: %.2f        Latency: %.4f        Sample: %d        Average: %.4f        Interactive: %s",
181                                                 status.c_str(), total_time, latency, sample, sample_time, interactive.c_str());
182
183         view_display_info(str.c_str());
184
185         if(options.show_help)
186                 view_display_help();
187 }
188
189 static void display()
190 {
191         options.session->draw(session_buffer_params());
192
193         display_info(options.session->progress);
194 }
195
196 static void motion(int x, int y, int button)
197 {
198         if(options.interactive) {
199                 Transform matrix = options.session->scene->camera->matrix;
200
201                 /* Translate */
202                 if(button == 0) {
203                         float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
204                         matrix = matrix * transform_translate(translate);
205                 }
206
207                 /* Rotate */
208                 else if(button == 2) {
209                         float4 r1= make_float4(x * 0.1f, 0.0f, 1.0f, 0.0f);
210                         matrix = matrix * transform_rotate(r1.x * M_PI/180.0f, make_float3(r1.y, r1.z, r1.w));
211
212                         float4 r2 = make_float4(y * 0.1, 1.0f, 0.0f, 0.0f);
213                         matrix = matrix * transform_rotate(r2.x * M_PI/180.0f, make_float3(r2.y, r2.z, r2.w));
214                 }
215
216                 /* Update and Reset */
217                 options.session->scene->camera->matrix = matrix;
218                 options.session->scene->camera->need_update = true;
219                 options.session->scene->camera->need_device_update = true;
220
221                 options.session->reset(session_buffer_params(), options.session_params.samples);
222         }
223 }
224
225 static void resize(int width, int height)
226 {
227         options.width = width;
228         options.height = height;
229
230         if(options.session) {
231                 /* Update camera */
232                 options.session->scene->camera->width = width;
233                 options.session->scene->camera->height = height;
234                 options.session->scene->camera->compute_auto_viewplane();
235                 options.session->scene->camera->need_update = true;
236                 options.session->scene->camera->need_device_update = true;
237
238                 options.session->reset(session_buffer_params(), options.session_params.samples);
239         }
240 }
241
242 static void keyboard(unsigned char key)
243 {
244         /* Toggle help */
245         if(key == 'h')
246                 options.show_help = !(options.show_help);
247
248         /* Reset */
249         else if(key == 'r')
250                 options.session->reset(session_buffer_params(), options.session_params.samples);
251
252         /* Cancel */
253         else if(key == 27) // escape
254                 options.session->progress.set_cancel("Canceled");
255
256         /* Pause */
257         else if(key == 'p') {
258                 options.pause = !options.pause;
259                 options.session->set_pause(options.pause);
260         }
261
262         /* Interactive Mode */
263         else if(key == 'i')
264                 options.interactive = !(options.interactive);
265
266         else if(options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
267                 Transform matrix = options.session->scene->camera->matrix;
268                 float3 translate;
269
270                 if(key == 'w')
271                         translate = make_float3(0.0f, 0.0f, 0.1f);
272                 else if(key == 's')
273                         translate = make_float3(0.0f, 0.0f, -0.1f);
274                 else if(key == 'a')
275                         translate = make_float3(-0.1f, 0.0f, 0.0f);
276                 else if(key == 'd')
277                         translate = make_float3(0.1f, 0.0f, 0.0f);
278
279                 matrix = matrix * transform_translate(translate);
280
281                 /* Update and Reset */
282                 options.session->scene->camera->matrix = matrix;
283                 options.session->scene->camera->need_update = true;
284                 options.session->scene->camera->need_device_update = true;
285
286                 options.session->reset(session_buffer_params(), options.session_params.samples);
287         }
288 }
289 #endif
290
291 static int files_parse(int argc, const char *argv[])
292 {
293         if(argc > 0)
294                 options.filepath = argv[0];
295
296         return 0;
297 }
298
299 static void options_parse(int argc, const char **argv)
300 {
301         options.width = 0;
302         options.height = 0;
303         options.filepath = "";
304         options.session = NULL;
305         options.quiet = false;
306
307         /* device names */
308         string device_names = "";
309         string devicename = "cpu";
310         bool list = false;
311
312         vector<DeviceType>& types = Device::available_types();
313
314         foreach(DeviceType type, types) {
315                 if(device_names != "")
316                         device_names += ", ";
317
318                 device_names += Device::string_from_type(type);
319         }
320
321         /* shading system */
322         string ssname = "svm";
323
324         /* parse options */
325         ArgParse ap;
326         bool help = false;
327
328         ap.options ("Usage: cycles [options] file.xml",
329                 "%*", files_parse, "",
330                 "--device %s", &devicename, ("Devices to use: " + device_names).c_str(),
331 #ifdef WITH_OSL
332                 "--shadingsys %s", &ssname, "Shading system to use: svm, osl",
333 #endif
334                 "--background", &options.session_params.background, "Render in background, without user interface",
335                 "--quiet", &options.quiet, "In background mode, don't print progress messages",
336                 "--samples %d", &options.session_params.samples, "Number of samples to render",
337                 "--output %s", &options.session_params.output_path, "File path to write output image",
338                 "--threads %d", &options.session_params.threads, "CPU Rendering Threads",
339                 "--width  %d", &options.width, "Window width in pixel",
340                 "--height %d", &options.height, "Window height in pixel",
341                 "--list-devices", &list, "List information about all available devices",
342                 "--help", &help, "Print help message",
343                 NULL);
344
345         if(ap.parse(argc, argv) < 0) {
346                 fprintf(stderr, "%s\n", ap.geterror().c_str());
347                 ap.usage();
348                 exit(EXIT_FAILURE);
349         }
350         else if(list) {
351                 vector<DeviceInfo>& devices = Device::available_devices();
352                 printf("Devices:\n");
353
354                 foreach(DeviceInfo& info, devices) {
355                         printf("    %s%s\n",
356                                 info.description.c_str(),
357                                 (info.display_device)? " (display)": "");
358                 }
359
360                 exit(EXIT_SUCCESS);
361         }
362         else if(help || options.filepath == "") {
363                 ap.usage();
364                 exit(EXIT_SUCCESS);
365         }
366
367         if(ssname == "osl")
368                 options.scene_params.shadingsystem = SceneParams::OSL;
369         else if(ssname == "svm")
370                 options.scene_params.shadingsystem = SceneParams::SVM;
371
372 #ifdef WITH_CYCLES_STANDALONE_GUI
373         /* Progressive rendering for GUI */
374         if(!options.session_params.background)
375                 options.session_params.progressive = true;
376 #else
377         /* When building without GUI, set background */
378         options.session_params.background = true;
379 #endif
380
381         /* find matching device */
382         DeviceType device_type = Device::type_from_string(devicename.c_str());
383         vector<DeviceInfo>& devices = Device::available_devices();
384         DeviceInfo device_info;
385         bool device_available = false;
386
387         foreach(DeviceInfo& device, devices) {
388                 if(device_type == device.type) {
389                         options.session_params.device = device;
390                         device_available = true;
391                         break;
392                 }
393         }
394
395         /* handle invalid configurations */
396         if(options.session_params.device.type == DEVICE_NONE || !device_available) {
397                 fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
398                 exit(EXIT_FAILURE);
399         }
400 #ifdef WITH_OSL
401         else if(!(ssname == "osl" || ssname == "svm")) {
402                 fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
403                 exit(EXIT_FAILURE);
404         }
405         else if(options.scene_params.shadingsystem == SceneParams::OSL && options.session_params.device.type != DEVICE_CPU) {
406                 fprintf(stderr, "OSL shading system only works with CPU device\n");
407                 exit(EXIT_FAILURE);
408         }
409 #endif
410         else if(options.session_params.samples < 0) {
411                 fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
412                 exit(EXIT_FAILURE);
413         }
414         else if(options.filepath == "") {
415                 fprintf(stderr, "No file path specified\n");
416                 exit(EXIT_FAILURE);
417         }
418
419         /* For smoother Viewport */
420         options.session_params.start_resolution = 64;
421
422         /* load scene */
423         scene_init();
424 }
425
426 CCL_NAMESPACE_END
427
428 using namespace ccl;
429
430 int main(int argc, const char **argv)
431 {
432         path_init();
433         options_parse(argc, argv);
434
435 #ifdef WITH_CYCLES_STANDALONE_GUI
436         if(options.session_params.background) {
437 #endif
438                 session_init();
439                 options.session->wait();
440                 session_exit();
441 #ifdef WITH_CYCLES_STANDALONE_GUI
442         }
443         else {
444                 string title = "Cycles: " + path_filename(options.filepath);
445
446                 /* init/exit are callback so they run while GL is initialized */
447                 view_main_loop(title.c_str(), options.width, options.height,
448                         session_init, session_exit, resize, display, keyboard, motion);
449         }
450 #endif
451
452         return 0;
453 }
454