cycles_standalone.cpp 15 KB

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