Blender V4.3
path_trace.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6
7#include "device/cpu/device.h"
8#include "device/device.h"
13#include "scene/pass.h"
14#include "scene/scene.h"
15#include "session/tile.h"
16#include "util/algorithm.h"
17#include "util/log.h"
18#include "util/math.h"
19#include "util/progress.h"
20#include "util/tbb.h"
21#include "util/time.h"
22
24
26 Device *denoise_device,
27 Film *film,
28 DeviceScene *device_scene,
29 RenderScheduler &render_scheduler,
30 TileManager &tile_manager)
31 : device_(device),
32 denoise_device_(denoise_device),
33 film_(film),
34 device_scene_(device_scene),
35 render_scheduler_(render_scheduler),
36 tile_manager_(tile_manager)
37{
38 DCHECK_NE(device_, nullptr);
39
40 {
41 vector<DeviceInfo> cpu_devices;
42 device_cpu_info(cpu_devices);
43
44 cpu_device_.reset(
45 device_cpu_create(cpu_devices[0], device->stats, device->profiler, device_->headless));
46 }
47
48 /* Create path tracing work in advance, so that it can be reused by incremental sampling as much
49 * as possible. */
50 device_->foreach_device([&](Device *path_trace_device) {
51 unique_ptr<PathTraceWork> work = PathTraceWork::create(
52 path_trace_device, film, device_scene, &render_cancel_.is_requested);
53 if (work) {
54 path_trace_works_.emplace_back(std::move(work));
55 }
56 });
57
60
61 render_scheduler.set_need_schedule_rebalance(path_trace_works_.size() > 1);
62}
63
68
70{
71 if (denoiser_) {
72 /* Activate graphics interop while denoiser device is created, so that it can choose a device
73 * that supports interop for faster display updates. */
74 if (display_ && path_trace_works_.size() > 1) {
75 display_->graphics_interop_activate();
76 }
77
78 denoiser_->load_kernels(progress_);
79
80 if (display_ && path_trace_works_.size() > 1) {
81 display_->graphics_interop_deactivate();
82 }
83 }
84}
85
87{
88 for (auto &&path_trace_work : path_trace_works_) {
89 path_trace_work->alloc_work_memory();
90 }
91}
92
94{
95 /* The logic here is optimized for the best feedback in the viewport, which implies having a GPU
96 * display. Of there is no such display, the logic here will break. */
98
99 /* The logic here tries to provide behavior which feels the most interactive feel to artists.
100 * General idea is to be able to reset as quickly as possible, while still providing interactive
101 * feel.
102 *
103 * If the render result was ever drawn after previous reset, consider that reset is now possible.
104 * This way camera navigation gives the quickest feedback of rendered pixels, regardless of
105 * whether CPU or GPU drawing pipeline is used.
106 *
107 * Consider reset happening after redraw "slow" enough to not clog anything. This is a bit
108 * arbitrary, but seems to work very well with viewport navigation in Blender. */
109
111 return true;
112 }
113
114 return false;
115}
116
117void PathTrace::reset(const BufferParams &full_params,
118 const BufferParams &big_tile_params,
119 const bool reset_rendering)
120{
121 if (big_tile_params_.modified(big_tile_params)) {
122 big_tile_params_ = big_tile_params;
123 render_state_.need_reset_params = true;
124 }
125
126 full_params_ = full_params;
127
128 /* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation.
129 * It is requires to inform about reset whenever it happens, so that the redraw state tracking is
130 * properly updated. */
131 if (display_) {
132 display_->reset(big_tile_params, reset_rendering);
133 }
134
135 render_state_.has_denoised_result = false;
136 render_state_.tile_written = false;
137
138 did_draw_after_reset_ = false;
139}
140
142{
143 /* Free render buffers used by the path trace work to reduce memory peak. */
144 BufferParams empty_params;
145 empty_params.pass_stride = 0;
146 empty_params.update_offset_stride();
147 for (auto &&path_trace_work : path_trace_works_) {
148 path_trace_work->get_render_buffers()->reset(empty_params);
149 }
150 render_state_.need_reset_params = true;
151}
152
154{
155 progress_ = progress;
156}
157
158void PathTrace::render(const RenderWork &render_work)
159{
160 /* Indicate that rendering has started and that it can be requested to cancel. */
161 {
163 if (render_cancel_.is_requested) {
164 return;
165 }
166 render_cancel_.is_rendering = true;
167 }
168
169 render_pipeline(render_work);
170
171 /* Indicate that rendering has finished, making it so thread which requested `cancel()` can carry
172 * on. */
173 {
175 render_cancel_.is_rendering = false;
176 render_cancel_.condition.notify_one();
177 }
178}
179
181{
182 /* NOTE: Only check for "instant" cancel here. The user-requested cancel via progress is
183 * checked in Session and the work in the event of cancel is to be finished here. */
184
186 0);
187
189
191
192 init_render_buffers(render_work);
193
194 rebalance(render_work);
195
196 /* Prepare all per-thread guiding structures before we start with the next rendering
197 * iteration/progression. */
198 const bool use_guiding = device_scene_->data.integrator.use_guiding;
199 if (use_guiding) {
201 }
202
203 path_trace(render_work);
204 if (render_cancel_.is_requested) {
205 return;
206 }
207
208 /* Update the guiding field using the training data/samples collected during the rendering
209 * iteration/progression. */
210 const bool train_guiding = device_scene_->data.integrator.train_guiding;
211 if (use_guiding && train_guiding) {
213 }
214
215 adaptive_sample(render_work);
216 if (render_cancel_.is_requested) {
217 return;
218 }
219
220 cryptomatte_postprocess(render_work);
221 if (render_cancel_.is_requested) {
222 return;
223 }
224
225 denoise(render_work);
226 if (render_cancel_.is_requested) {
227 return;
228 }
229
230 write_tile_buffer(render_work);
231 update_display(render_work);
232
233 progress_update_if_needed(render_work);
234
235 finalize_full_buffer_on_disk(render_work);
236}
237
239{
240 for (auto &&path_trace_work : path_trace_works_) {
241 path_trace_work->init_execution();
242 }
243}
244
245/* TODO(sergey): Look into `std::function` rather than using a template. Should not be a
246 * measurable performance impact at runtime, but will make compilation faster and binary somewhat
247 * smaller. */
248template<typename Callback>
249static void foreach_sliced_buffer_params(const vector<unique_ptr<PathTraceWork>> &path_trace_works,
250 const vector<WorkBalanceInfo> &work_balance_infos,
251 const BufferParams &buffer_params,
252 const int overscan,
253 const Callback &callback)
254{
255 const int num_works = path_trace_works.size();
256 const int window_height = buffer_params.window_height;
257
258 int current_y = 0;
259 for (int i = 0; i < num_works; ++i) {
260 const double weight = work_balance_infos[i].weight;
261 const int slice_window_full_y = buffer_params.full_y + buffer_params.window_y + current_y;
262 const int slice_window_height = max(lround(window_height * weight), 1);
263
264 /* Disallow negative values to deal with situations when there are more compute devices than
265 * scan-lines. */
266 const int remaining_window_height = max(0, window_height - current_y);
267
268 BufferParams slice_params = buffer_params;
269
270 slice_params.full_y = max(slice_window_full_y - overscan, buffer_params.full_y);
271 slice_params.window_y = slice_window_full_y - slice_params.full_y;
272
273 if (i < num_works - 1) {
274 slice_params.window_height = min(slice_window_height, remaining_window_height);
275 }
276 else {
277 slice_params.window_height = remaining_window_height;
278 }
279
280 slice_params.height = slice_params.window_y + slice_params.window_height + overscan;
281 slice_params.height = min(slice_params.height,
282 buffer_params.height + buffer_params.full_y - slice_params.full_y);
283
284 slice_params.update_offset_stride();
285
286 callback(path_trace_works[i].get(), slice_params);
287
288 current_y += slice_params.window_height;
289 }
290}
291
293{
294 const int overscan = tile_manager_.get_tile_overscan();
298 overscan,
299 [](PathTraceWork *path_trace_work, const BufferParams &params) {
300 RenderBuffers *buffers = path_trace_work->get_render_buffers();
302 });
303}
304
305static BufferParams scale_buffer_params(const BufferParams &params, int resolution_divider)
306{
307 BufferParams scaled_params = params;
308
309 scaled_params.width = max(1, params.width / resolution_divider);
310 scaled_params.height = max(1, params.height / resolution_divider);
311
312 scaled_params.window_x = params.window_x / resolution_divider;
313 scaled_params.window_y = params.window_y / resolution_divider;
314 scaled_params.window_width = max(1, params.window_width / resolution_divider);
315 scaled_params.window_height = max(1, params.window_height / resolution_divider);
316
317 scaled_params.full_x = params.full_x / resolution_divider;
318 scaled_params.full_y = params.full_y / resolution_divider;
319 scaled_params.full_width = max(1, params.full_width / resolution_divider);
320 scaled_params.full_height = max(1, params.full_height / resolution_divider);
321
322 scaled_params.update_offset_stride();
323
324 return scaled_params;
325}
326
328{
329 const int resolution_divider = render_work.resolution_divider;
330
332 const BufferParams scaled_big_tile_params = scale_buffer_params(big_tile_params_,
334
335 const int overscan = tile_manager_.get_tile_overscan();
336
339 scaled_big_tile_params,
340 overscan,
341 [&](PathTraceWork *path_trace_work, const BufferParams params) {
342 path_trace_work->set_effective_buffer_params(
343 scaled_full_params, scaled_big_tile_params, params);
344 });
345
346 render_state_.effective_big_tile_params = scaled_big_tile_params;
347}
348
350{
351 if (render_state_.need_reset_params) {
353 }
354
355 if (render_state_.need_reset_params ||
356 render_state_.resolution_divider != render_work.resolution_divider)
357 {
359 }
360
361 render_state_.resolution_divider = render_work.resolution_divider;
362 render_state_.need_reset_params = false;
363}
364
366{
368
369 /* Handle initialization scheduled by the render scheduler. */
370 if (render_work.init_render_buffers) {
371 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
372 path_trace_work->zero_render_buffers();
373 });
374
376 }
377}
378
380{
381 if (!render_work.path_trace.num_samples) {
382 return;
383 }
384
385 VLOG_WORK << "Will path trace " << render_work.path_trace.num_samples
386 << " samples at the resolution divider " << render_work.resolution_divider;
387
388 const double start_time = time_dt();
389
390 const int num_works = path_trace_works_.size();
391
393
394 parallel_for(0, num_works, [&](int i) {
395 const double work_start_time = time_dt();
396 const int num_samples = render_work.path_trace.num_samples;
397
398 PathTraceWork *path_trace_work = path_trace_works_[i].get();
399 if (path_trace_work->get_device()->have_error()) {
400 return;
401 }
402
404 path_trace_work->render_samples(statistics,
405 render_work.path_trace.start_sample,
406 num_samples,
407 render_work.path_trace.sample_offset);
408
409 DCHECK(isfinite(statistics.occupancy));
410
411 const double work_time = time_dt() - work_start_time;
412 work_balance_infos_[i].time_spent += work_time;
413 work_balance_infos_[i].occupancy = statistics.occupancy;
414
415 VLOG_INFO << "Rendered " << num_samples << " samples in " << work_time << " seconds ("
416 << work_time / num_samples
417 << " seconds per sample), occupancy: " << statistics.occupancy;
418 });
419
420 float occupancy_accum = 0.0f;
421 for (const WorkBalanceInfo &balance_info : work_balance_infos_) {
422 occupancy_accum += balance_info.occupancy;
423 }
424 const float occupancy = occupancy_accum / num_works;
425 render_scheduler_.report_path_trace_occupancy(render_work, occupancy);
426
428 render_work, time_dt() - start_time, is_cancel_requested());
429}
430
432{
433 if (!render_work.adaptive_sampling.filter) {
434 return;
435 }
436
437 bool did_reschedule_on_idle = false;
438
439 while (true) {
440 VLOG_WORK << "Will filter adaptive stopping buffer, threshold "
441 << render_work.adaptive_sampling.threshold;
442 if (render_work.adaptive_sampling.reset) {
443 VLOG_WORK << "Will re-calculate convergency flag for currently converged pixels.";
444 }
445
446 const double start_time = time_dt();
447
448 uint num_active_pixels = 0;
449 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
450 const uint num_active_pixels_in_work =
451 path_trace_work->adaptive_sampling_converge_filter_count_active(
452 render_work.adaptive_sampling.threshold, render_work.adaptive_sampling.reset);
453 if (num_active_pixels_in_work) {
454 atomic_add_and_fetch_u(&num_active_pixels, num_active_pixels_in_work);
455 }
456 });
457
459 render_work, time_dt() - start_time, is_cancel_requested());
460
461 if (num_active_pixels == 0) {
462 VLOG_WORK << "All pixels converged.";
464 break;
465 }
466 VLOG_WORK << "Continuing with lower threshold.";
467 }
468 else if (did_reschedule_on_idle) {
469 break;
470 }
471 else if (num_active_pixels < 128 * 128) {
472 /* NOTE: The hardcoded value of 128^2 is more of an empirical value to keep GPU busy so that
473 * there is no performance loss from the progressive noise floor feature.
474 *
475 * A better heuristic is possible here: for example, use maximum of 128^2 and percentage of
476 * the final resolution. */
478 VLOG_WORK << "Rescheduling is not possible: final threshold is reached.";
479 break;
480 }
481 VLOG_WORK << "Rescheduling lower threshold.";
482 did_reschedule_on_idle = true;
483 }
484 else {
485 break;
486 }
487 }
488}
489
491{
492 if (!params.use) {
493 denoiser_.reset();
494 return;
495 }
496
497 Device *effective_denoise_device;
498 Device *cpu_fallback_device = cpu_device_.get();
499 DenoiseParams effective_denoise_params = get_effective_denoise_params(
500 denoise_device_, cpu_fallback_device, params, effective_denoise_device);
501
502 bool need_to_recreate_denoiser = false;
503 if (denoiser_) {
504 const DenoiseParams old_denoiser_params = denoiser_->get_params();
505
506 const bool is_cpu_denoising = old_denoiser_params.type == DENOISER_OPENIMAGEDENOISE &&
507 old_denoiser_params.use_gpu == false;
508 const bool requested_gpu_denoising = effective_denoise_params.type == DENOISER_OPTIX ||
509 (effective_denoise_params.type ==
511 effective_denoise_params.use_gpu == true);
512 if (requested_gpu_denoising && is_cpu_denoising &&
513 effective_denoise_device->info.type == DEVICE_CPU)
514 {
515 /* It won't be possible to use GPU denoising when according to user settings we have
516 * only CPU as available denoising device. So we just exiting early to avoid
517 * unnecessary denoiser recreation or parameters update. */
518 return;
519 }
520
521 const bool is_same_denoising_device_type = old_denoiser_params.use_gpu ==
522 effective_denoise_params.use_gpu;
523 /* Optix Denoiser is not supporting CPU devices, so use_gpu option is not
524 * shown in the UI and changes in the option value should not be checked. */
525 if (old_denoiser_params.type == effective_denoise_params.type &&
526 (is_same_denoising_device_type || effective_denoise_params.type == DENOISER_OPTIX))
527 {
528 denoiser_->set_params(effective_denoise_params);
529 }
530 else {
531 need_to_recreate_denoiser = true;
532 }
533 }
534 else {
535 /* if there is no denoiser and param.use is true, then we need to create it. */
536 need_to_recreate_denoiser = true;
537 }
538
539 if (need_to_recreate_denoiser) {
541 effective_denoise_device, cpu_fallback_device, effective_denoise_params);
542
543 /* Only take into account the "immediate" cancel to have interactive rendering responding to
544 * navigation as quickly as possible, but allow to run denoiser after user hit Escape key while
545 * doing offline rendering. */
546 denoiser_->is_cancelled_cb = [this]() { return render_cancel_.is_requested; };
547 }
548
549 /* Use actual parameters, if available */
550 if (denoise_device_)
552 else
553 render_scheduler_.set_denoiser_params(effective_denoise_params);
554}
555
557{
558 render_scheduler_.set_adaptive_sampling(adaptive_sampling);
559}
560
562{
563 if (!render_work.cryptomatte.postprocess) {
564 return;
565 }
566 VLOG_WORK << "Perform cryptomatte work.";
567
568 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
569 path_trace_work->cryptomatte_postproces();
570 });
571}
572
573void PathTrace::denoise(const RenderWork &render_work)
574{
575 if (!render_work.tile.denoise) {
576 return;
577 }
578
579 if (!denoiser_) {
580 /* Denoiser was not configured, so nothing to do here. */
581 return;
582 }
583
584 VLOG_WORK << "Perform denoising work.";
585
586 const double start_time = time_dt();
587
588 RenderBuffers *buffer_to_denoise = nullptr;
589 bool allow_inplace_modification = false;
590
591 Device *denoiser_device = denoiser_->get_denoiser_device();
592 if (path_trace_works_.size() > 1 && denoiser_device && !big_tile_denoise_work_) {
594 }
595
597 big_tile_denoise_work_->set_effective_buffer_params(render_state_.effective_big_tile_params,
598 render_state_.effective_big_tile_params,
599 render_state_.effective_big_tile_params);
600
601 buffer_to_denoise = big_tile_denoise_work_->get_render_buffers();
602 buffer_to_denoise->reset(render_state_.effective_big_tile_params);
603
604 copy_to_render_buffers(buffer_to_denoise);
605
606 allow_inplace_modification = true;
607 }
608 else {
609 DCHECK_EQ(path_trace_works_.size(), 1);
610
611 buffer_to_denoise = path_trace_works_.front()->get_render_buffers();
612 }
613
614 if (denoiser_->denoise_buffer(render_state_.effective_big_tile_params,
615 buffer_to_denoise,
617 allow_inplace_modification))
618 {
619 render_state_.has_denoised_result = true;
620 }
621
622 render_scheduler_.report_denoise_time(render_work, time_dt() - start_time);
623}
624
625void PathTrace::set_output_driver(unique_ptr<OutputDriver> driver)
626{
627 output_driver_ = std::move(driver);
628}
629
630void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver)
631{
632 /* The display driver is the source of the drawing context which might be used by
633 * path trace works. Make sure there is no graphics interop using resources from
634 * the old display, as it might no longer be available after this call. */
636
637 if (driver) {
638 display_ = make_unique<PathTraceDisplay>(std::move(driver));
639 }
640 else {
641 display_ = nullptr;
642 }
643}
644
646{
647 if (display_) {
648 display_->clear();
649 }
650}
651
653{
654 if (!display_) {
655 return;
656 }
657
659}
660
662{
663 if (!display_) {
664 return;
665 }
666
667 display_->flush();
668}
669
670void PathTrace::update_display(const RenderWork &render_work)
671{
672 if (!render_work.display.update) {
673 return;
674 }
675
676 if (!display_ && !output_driver_) {
677 VLOG_WORK << "Ignore display update.";
678 return;
679 }
680
681 if (full_params_.width == 0 || full_params_.height == 0) {
682 VLOG_WORK << "Skipping PathTraceDisplay update due to 0 size of the render buffer.";
683 return;
684 }
685
686 const double start_time = time_dt();
687
688 if (output_driver_) {
689 VLOG_WORK << "Invoke buffer update callback.";
690
691 PathTraceTile tile(*this);
692 output_driver_->update_render_tile(tile);
693 }
694
695 if (display_) {
696 VLOG_WORK << "Perform copy to GPUDisplay work.";
697
698 const int texture_width = render_state_.effective_big_tile_params.window_width;
699 const int texture_height = render_state_.effective_big_tile_params.window_height;
700 if (!display_->update_begin(texture_width, texture_height)) {
701 LOG(ERROR) << "Error beginning GPUDisplay update.";
702 return;
703 }
704
705 const PassMode pass_mode = render_work.display.use_denoised_result &&
706 render_state_.has_denoised_result ?
709
710 /* TODO(sergey): When using multi-device rendering map the GPUDisplay once and copy data from
711 * all works in parallel. */
712 const int num_samples = get_num_samples_in_buffer();
713 if (big_tile_denoise_work_ && render_state_.has_denoised_result) {
714 big_tile_denoise_work_->copy_to_display(display_.get(), pass_mode, num_samples);
715 }
716 else {
717 for (auto &&path_trace_work : path_trace_works_) {
718 path_trace_work->copy_to_display(display_.get(), pass_mode, num_samples);
719 }
720 }
721
722 display_->update_end();
723 }
724
725 render_scheduler_.report_display_update_time(render_work, time_dt() - start_time);
726}
727
728void PathTrace::rebalance(const RenderWork &render_work)
729{
730 if (!render_work.rebalance) {
731 return;
732 }
733
734 const int num_works = path_trace_works_.size();
735
736 if (num_works == 1) {
737 VLOG_WORK << "Ignoring rebalance work due to single device render.";
738 return;
739 }
740
741 const double start_time = time_dt();
742
743 if (VLOG_IS_ON(3)) {
744 VLOG_WORK << "Perform rebalance work.";
745 VLOG_WORK << "Per-device path tracing time (seconds):";
746 for (int i = 0; i < num_works; ++i) {
747 VLOG_WORK << path_trace_works_[i]->get_device()->info.description << ": "
748 << work_balance_infos_[i].time_spent;
749 }
750 }
751
752 const bool did_rebalance = work_balance_do_rebalance(work_balance_infos_);
753
754 if (VLOG_IS_ON(3)) {
755 VLOG_WORK << "Calculated per-device weights for works:";
756 for (int i = 0; i < num_works; ++i) {
757 VLOG_WORK << path_trace_works_[i]->get_device()->info.description << ": "
758 << work_balance_infos_[i].weight;
759 }
760 }
761
762 if (!did_rebalance) {
763 VLOG_WORK << "Balance in path trace works did not change.";
764 render_scheduler_.report_rebalance_time(render_work, time_dt() - start_time, false);
765 return;
766 }
767
768 RenderBuffers big_tile_cpu_buffers(cpu_device_.get());
769 big_tile_cpu_buffers.reset(render_state_.effective_big_tile_params);
770
771 copy_to_render_buffers(&big_tile_cpu_buffers);
772
773 render_state_.need_reset_params = true;
775
776 copy_from_render_buffers(&big_tile_cpu_buffers);
777
778 render_scheduler_.report_rebalance_time(render_work, time_dt() - start_time, true);
779}
780
782{
783 if (!render_work.tile.write) {
784 return;
785 }
786
787 VLOG_WORK << "Write tile result.";
788
789 render_state_.tile_written = true;
790
791 const bool has_multiple_tiles = tile_manager_.has_multiple_tiles();
792
793 /* Write render tile result, but only if not using tiled rendering.
794 *
795 * Tiles are written to a file during rendering, and written to the software at the end
796 * of rendering (wither when all tiles are finished, or when rendering was requested to be
797 * canceled).
798 *
799 * Important thing is: tile should be written to the software via callback only once. */
800 if (!has_multiple_tiles) {
801 VLOG_WORK << "Write tile result via buffer write callback.";
803 }
804 /* Write tile to disk, so that the render work's render buffer can be re-used for the next tile.
805 */
806 else {
807 VLOG_WORK << "Write tile result to disk.";
809 }
810}
811
813{
814 if (!render_work.full.write) {
815 return;
816 }
817
818 VLOG_WORK << "Handle full-frame render buffer work.";
819
821 VLOG_WORK << "No tiles on disk.";
822 return;
823 }
824
825 /* Make sure writing to the file is fully finished.
826 * This will include writing all possible missing tiles, ensuring validness of the file. */
828
829 /* NOTE: The rest of full-frame post-processing (such as full-frame denoising) will be done after
830 * all scenes and layers are rendered by the Session (which happens after freeing Session memory,
831 * so that we never hold scene and full-frame buffer in memory at the same time). */
832}
833
835{
837
838 render_cancel_.is_requested = true;
839
840 while (render_cancel_.is_rendering) {
841 render_cancel_.condition.wait(lock);
842 }
843
844 render_cancel_.is_requested = false;
845}
846
851
853{
854 if (render_cancel_.is_requested) {
855 return true;
856 }
857
858 if (progress_ != nullptr) {
859 if (progress_->get_cancel()) {
860 return true;
861 }
862 }
863
864 return false;
865}
866
868{
869 if (!output_driver_) {
870 return;
871 }
872
873 PathTraceTile tile(*this);
874 output_driver_->write_render_tile(tile);
875}
876
878{
879 if (!device_scene_->data.bake.use) {
880 return;
881 }
882
883 if (!output_driver_) {
884 return;
885 }
886
887 /* Read buffers back from device. */
888 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
889 path_trace_work->copy_render_buffers_from_device();
890 });
891
892 /* Read (subset of) passes from output driver. */
893 PathTraceTile tile(*this);
894 if (output_driver_->read_render_tile(tile)) {
895 /* Copy buffers to device again. */
896 parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) {
897 path_trace_work->copy_render_buffers_to_device();
898 });
899 }
900}
901
903{
904 /* Sample count pass is required to support per-tile partial results stored in the file. */
906
907 const int num_rendered_samples = render_scheduler_.get_num_rendered_samples();
908
909 if (num_rendered_samples == 0) {
910 /* The tile has zero samples, no need to write it. */
911 return;
912 }
913
914 /* Get access to the CPU-side render buffers of the current big tile. */
916 RenderBuffers big_tile_cpu_buffers(cpu_device_.get());
917
918 if (path_trace_works_.size() == 1) {
919 path_trace_works_[0]->copy_render_buffers_from_device();
920 buffers = path_trace_works_[0]->get_render_buffers();
921 }
922 else {
923 big_tile_cpu_buffers.reset(render_state_.effective_big_tile_params);
924 copy_to_render_buffers(&big_tile_cpu_buffers);
925
926 buffers = &big_tile_cpu_buffers;
927 }
928
930 device_->set_error("Error writing tile to file");
931 }
932}
933
935{
936 if (progress_ != nullptr) {
937 const int2 tile_size = get_render_tile_size();
938 const uint64_t num_samples_added = uint64_t(tile_size.x) * tile_size.y *
939 render_work.path_trace.num_samples;
940 const int current_sample = render_work.path_trace.start_sample +
941 render_work.path_trace.num_samples -
942 render_work.path_trace.sample_offset;
943 progress_->add_samples(num_samples_added, current_sample);
944 }
945
946 if (progress_update_cb) {
948 }
949}
950
951void PathTrace::progress_set_status(const string &status, const string &substatus)
952{
953 if (progress_ != nullptr) {
954 progress_->set_status(status, substatus);
955 }
956}
957
959{
960 parallel_for_each(path_trace_works_,
961 [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
962 path_trace_work->copy_to_render_buffers(render_buffers);
963 });
965}
966
968{
970 parallel_for_each(path_trace_works_,
971 [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
972 path_trace_work->copy_from_render_buffers(render_buffers);
973 });
974}
975
977{
978 if (full_frame_state_.render_buffers) {
979 /* Full-frame buffer is always allocated on CPU. */
980 return true;
981 }
982
983 if (big_tile_denoise_work_ && render_state_.has_denoised_result) {
984 return big_tile_denoise_work_->copy_render_buffers_from_device();
985 }
986
987 bool success = true;
988
989 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
990 if (!success) {
991 return;
992 }
993 if (!path_trace_work->copy_render_buffers_from_device()) {
994 success = false;
995 }
996 });
997
998 return success;
999}
1000
1002{
1003 string result;
1004
1005 if (buffers.params.layer.size()) {
1006 result += string(buffers.params.layer);
1007 }
1008
1009 if (buffers.params.view.size()) {
1010 if (!result.empty()) {
1011 result += ", ";
1012 }
1013 result += string(buffers.params.view);
1014 }
1015
1016 return result;
1017}
1018
1020{
1021 VLOG_WORK << "Processing full frame buffer file " << filename;
1022
1023 progress_set_status("Reading full buffer from disk");
1024
1025 RenderBuffers full_frame_buffers(cpu_device_.get());
1026
1027 DenoiseParams denoise_params;
1028 if (!tile_manager_.read_full_buffer_from_disk(filename, &full_frame_buffers, &denoise_params)) {
1029 const string error_message = "Error reading tiles from file";
1030 if (progress_) {
1031 progress_->set_error(error_message);
1032 progress_->set_cancel(error_message);
1033 }
1034 else {
1035 LOG(ERROR) << error_message;
1036 }
1037 return;
1038 }
1039
1040 const string layer_view_name = get_layer_view_name(full_frame_buffers);
1041
1042 render_state_.has_denoised_result = false;
1043
1044 if (denoise_params.use) {
1045 progress_set_status(layer_view_name, "Denoising");
1046
1047 /* If GPU should be used is not based on file metadata. */
1049
1050 /* Re-use the denoiser as much as possible, avoiding possible device re-initialization.
1051 *
1052 * It will not conflict with the regular rendering as:
1053 * - Rendering is supposed to be finished here.
1054 * - The next rendering will go via Session's `run_update_for_next_iteration` which will
1055 * ensure proper denoiser is used. */
1056 set_denoiser_params(denoise_params);
1057
1058 /* Number of samples doesn't matter too much, since the samples count pass will be used. */
1059 denoiser_->denoise_buffer(full_frame_buffers.params, &full_frame_buffers, 0, false);
1060
1061 render_state_.has_denoised_result = true;
1062 }
1063
1064 full_frame_state_.render_buffers = &full_frame_buffers;
1065
1066 progress_set_status(layer_view_name, "Finishing");
1067
1068 /* Write the full result pretending that there is a single tile.
1069 * Requires some state change, but allows to use same communication API with the software. */
1071
1072 full_frame_state_.render_buffers = nullptr;
1073}
1074
1076{
1077 if (full_frame_state_.render_buffers) {
1078 return full_frame_state_.render_buffers->params.samples;
1079 }
1080
1082}
1083
1085 const PassAccessor::Destination &destination)
1086{
1087 if (full_frame_state_.render_buffers) {
1088 return pass_accessor.get_render_tile_pixels(full_frame_state_.render_buffers, destination);
1089 }
1090
1091 if (big_tile_denoise_work_ && render_state_.has_denoised_result) {
1092 /* Only use the big tile denoised buffer to access the denoised passes.
1093 * The guiding passes are allowed to be modified in-place for the needs of the denoiser,
1094 * so copy those from the original devices buffers. */
1095 if (pass_accessor.get_pass_access_info().mode == PassMode::DENOISED) {
1096 return big_tile_denoise_work_->get_render_tile_pixels(pass_accessor, destination);
1097 }
1098 }
1099
1100 bool success = true;
1101
1102 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
1103 if (!success) {
1104 return;
1105 }
1106 if (!path_trace_work->get_render_tile_pixels(pass_accessor, destination)) {
1107 success = false;
1108 }
1109 });
1110
1111 return success;
1112}
1113
1115 const PassAccessor::Source &source)
1116{
1117 bool success = true;
1118
1119 parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
1120 if (!success) {
1121 return;
1122 }
1123 if (!path_trace_work->set_render_tile_pixels(pass_accessor, source)) {
1124 success = false;
1125 }
1126 });
1127
1128 return success;
1129}
1130
1132{
1133 if (full_frame_state_.render_buffers) {
1134 return make_int2(full_frame_state_.render_buffers->params.window_width,
1135 full_frame_state_.render_buffers->params.window_height);
1136 }
1137
1139 return make_int2(tile.window_width, tile.window_height);
1140}
1141
1143{
1144 if (full_frame_state_.render_buffers) {
1145 return make_int2(0, 0);
1146 }
1147
1149 return make_int2(tile.x + tile.window_x, tile.y + tile.window_y);
1150}
1151
1153{
1154 return tile_manager_.get_size();
1155}
1156
1158{
1159 if (full_frame_state_.render_buffers) {
1160 return full_frame_state_.render_buffers->params;
1161 }
1162
1163 return big_tile_params_;
1164}
1165
1167{
1168 return render_state_.has_denoised_result;
1169}
1170
1172{
1173 /* Destroy any GPU resource which was used for graphics interop.
1174 * Need to have access to the PathTraceDisplay as it is the only source of drawing context which
1175 * is used for interop. */
1176 if (display_) {
1177 for (auto &&path_trace_work : path_trace_works_) {
1178 path_trace_work->destroy_gpu_resources(display_.get());
1179 }
1180
1182 big_tile_denoise_work_->destroy_gpu_resources(display_.get());
1183 }
1184 }
1185}
1186
1187/* --------------------------------------------------------------------
1188 * Report generation.
1189 */
1190
1191static const char *device_type_for_description(const DeviceType type)
1192{
1193 switch (type) {
1194 case DEVICE_NONE:
1195 return "None";
1196
1197 case DEVICE_CPU:
1198 return "CPU";
1199 case DEVICE_CUDA:
1200 return "CUDA";
1201 case DEVICE_OPTIX:
1202 return "OptiX";
1203 case DEVICE_HIP:
1204 return "HIP";
1205 case DEVICE_HIPRT:
1206 return "HIPRT";
1207 case DEVICE_ONEAPI:
1208 return "oneAPI";
1209 case DEVICE_DUMMY:
1210 return "Dummy";
1211 case DEVICE_MULTI:
1212 return "Multi";
1213 case DEVICE_METAL:
1214 return "Metal";
1215 }
1216
1217 return "UNKNOWN";
1218}
1219
1220/* Construct description of the device which will appear in the full report. */
1221/* TODO(sergey): Consider making it more reusable utility. */
1222static string full_device_info_description(const DeviceInfo &device_info)
1223{
1224 string full_description = device_info.description;
1225
1226 full_description += " (" + string(device_type_for_description(device_info.type)) + ")";
1227
1228 if (device_info.display_device) {
1229 full_description += " (display)";
1230 }
1231
1232 if (device_info.type == DEVICE_CPU) {
1233 full_description += " (" + to_string(device_info.cpu_threads) + " threads)";
1234 }
1235
1236 full_description += " [" + device_info.id + "]";
1237
1238 return full_description;
1239}
1240
1241/* Construct string which will contain information about devices, possibly multiple of the devices.
1242 *
1243 * In the simple case the result looks like:
1244 *
1245 * Message: Full Device Description
1246 *
1247 * If there are multiple devices then the result looks like:
1248 *
1249 * Message: Full First Device Description
1250 * Full Second Device Description
1251 *
1252 * Note that the newlines are placed in a way so that the result can be easily concatenated to the
1253 * full report. */
1254static string device_info_list_report(const string &message, const DeviceInfo &device_info)
1255{
1256 string result = "\n" + message + ": ";
1257 const string pad(message.length() + 2, ' ');
1258
1259 if (device_info.multi_devices.empty()) {
1260 result += full_device_info_description(device_info) + "\n";
1261 return result;
1262 }
1263
1264 bool is_first = true;
1265 for (const DeviceInfo &sub_device_info : device_info.multi_devices) {
1266 if (!is_first) {
1267 result += pad;
1268 }
1269
1270 result += full_device_info_description(sub_device_info) + "\n";
1271
1272 is_first = false;
1273 }
1274
1275 return result;
1276}
1277
1278static string path_trace_devices_report(const vector<unique_ptr<PathTraceWork>> &path_trace_works)
1279{
1280 DeviceInfo device_info;
1281 device_info.type = DEVICE_MULTI;
1282
1283 for (auto &&path_trace_work : path_trace_works) {
1284 device_info.multi_devices.push_back(path_trace_work->get_device()->info);
1285 }
1286
1287 return device_info_list_report("Path tracing on", device_info);
1288}
1289
1290static string denoiser_device_report(const Denoiser *denoiser)
1291{
1292 if (!denoiser) {
1293 return "";
1294 }
1295
1296 if (!denoiser->get_params().use) {
1297 return "";
1298 }
1299
1300 const Device *denoiser_device = denoiser->get_denoiser_device();
1301 if (!denoiser_device) {
1302 return "";
1303 }
1304
1305 return device_info_list_report("Denoising on", denoiser_device->info);
1306}
1307
1309{
1310 string result = "\nFull path tracing report\n";
1311
1313 result += denoiser_device_report(denoiser_.get());
1314
1315 /* Report from the render scheduler, which includes:
1316 * - Render mode (interactive, offline, headless)
1317 * - Adaptive sampling and denoiser parameters
1318 * - Breakdown of timing. */
1319 result += render_scheduler_.full_report();
1320
1321 return result;
1322}
1323
1324void PathTrace::set_guiding_params(const GuidingParams &guiding_params, const bool reset)
1325{
1326#ifdef WITH_PATH_GUIDING
1327 if (guiding_params_.modified(guiding_params)) {
1328 guiding_params_ = guiding_params;
1329
1330# if !(OPENPGL_VERSION_MAJOR == 0 && OPENPGL_VERSION_MINOR <= 5)
1331# define OPENPGL_USE_FIELD_CONFIG
1332# endif
1333
1334 if (guiding_params_.use) {
1335# ifdef OPENPGL_USE_FIELD_CONFIG
1336 openpgl::cpp::FieldConfig field_config;
1337# else
1338 PGLFieldArguments field_args;
1339# endif
1340 switch (guiding_params_.type) {
1341 default:
1342 /* Parallax-aware von Mises-Fisher mixture models. */
1344# ifdef OPENPGL_USE_FIELD_CONFIG
1345 field_config.Init(
1346 PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
1347 PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_PARALLAX_AWARE_VMM,
1348 guiding_params.deterministic);
1349# else
1350 pglFieldArgumentsSetDefaults(
1351 field_args,
1352 PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
1353 PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_PARALLAX_AWARE_VMM);
1354# endif
1355 break;
1356 }
1357 /* Directional quad-trees. */
1359# ifdef OPENPGL_USE_FIELD_CONFIG
1360 field_config.Init(
1361 PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
1362 PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_QUADTREE,
1363 guiding_params.deterministic);
1364# else
1365 pglFieldArgumentsSetDefaults(
1366 field_args,
1367 PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
1368 PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_QUADTREE);
1369# endif
1370 break;
1371 }
1372 /* von Mises-Fisher mixture models. */
1373 case GUIDING_TYPE_VMM: {
1374# ifdef OPENPGL_USE_FIELD_CONFIG
1375 field_config.Init(PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
1376 PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_VMM,
1377 guiding_params.deterministic);
1378# else
1379 pglFieldArgumentsSetDefaults(
1380 field_args,
1381 PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
1382 PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_VMM);
1383# endif
1384 break;
1385 }
1386 }
1387# ifdef OPENPGL_USE_FIELD_CONFIG
1388 field_config.SetSpatialStructureArgMaxDepth(16);
1389# else
1390 field_args.deterministic = guiding_params.deterministic;
1391 reinterpret_cast<PGLKDTreeArguments *>(field_args.spatialSturctureArguments)->maxDepth = 16;
1392# endif
1393 openpgl::cpp::Device *guiding_device = static_cast<openpgl::cpp::Device *>(
1395 if (guiding_device) {
1396 guiding_sample_data_storage_ = make_unique<openpgl::cpp::SampleStorage>();
1397# ifdef OPENPGL_USE_FIELD_CONFIG
1398 guiding_field_ = make_unique<openpgl::cpp::Field>(guiding_device, field_config);
1399# else
1400 guiding_field_ = make_unique<openpgl::cpp::Field>(guiding_device, field_args);
1401# endif
1402 }
1403 else {
1404 guiding_sample_data_storage_ = nullptr;
1405 guiding_field_ = nullptr;
1406 }
1407 }
1408 else {
1409 guiding_sample_data_storage_ = nullptr;
1410 guiding_field_ = nullptr;
1411 }
1412 }
1413 else if (reset) {
1414 if (guiding_field_) {
1415 guiding_field_->Reset();
1416 }
1417 }
1418#else
1419 (void)guiding_params;
1420 (void)reset;
1421#endif
1422}
1423
1425{
1426#ifdef WITH_PATH_GUIDING
1427 const bool train = (guiding_params_.training_samples == 0) ||
1428 (guiding_field_->GetIteration() < guiding_params_.training_samples);
1429
1430 for (auto &&path_trace_work : path_trace_works_) {
1431 path_trace_work->guiding_init_kernel_globals(
1432 guiding_field_.get(), guiding_sample_data_storage_.get(), train);
1433 }
1434
1435 if (train) {
1436 /* For training the guiding distribution we need to force the number of samples
1437 * per update to be limited, for reproducible results and reasonable training size.
1438 *
1439 * Idea: we could stochastically discard samples with a probability of 1/num_samples_per_update
1440 * we can then update only after the num_samples_per_update iterations are rendered. */
1442 }
1443 else {
1445 }
1446#endif
1447}
1448
1450{
1451#ifdef WITH_PATH_GUIDING
1452 VLOG_WORK << "Update path guiding structures";
1453
1454 VLOG_DEBUG << "Number of surface samples: " << guiding_sample_data_storage_->GetSizeSurface();
1455 VLOG_DEBUG << "Number of volume samples: " << guiding_sample_data_storage_->GetSizeVolume();
1456
1457 const size_t num_valid_samples = guiding_sample_data_storage_->GetSizeSurface() +
1458 guiding_sample_data_storage_->GetSizeVolume();
1459
1460 /* we wait until we have at least 1024 samples */
1461 if (num_valid_samples >= 1024) {
1462 guiding_field_->Update(*guiding_sample_data_storage_);
1463 guiding_update_count++;
1464
1465 VLOG_DEBUG << "Path guiding field valid: " << guiding_field_->Validate();
1466
1467 guiding_sample_data_storage_->Clear();
1468 }
1469#endif
1470}
1471
unsigned int uint
ATOMIC_INLINE unsigned int atomic_add_and_fetch_u(unsigned int *p, unsigned int x)
int pad[32 - sizeof(int)]
volatile int lock
void reset()
clear internal cached data and reset random seed
int pass_stride
Definition buffers.h:94
int full_width
Definition buffers.h:87
int get_pass_offset(PassType type, PassMode mode=PassMode::NOISY) const
Definition buffers.cpp:167
int window_y
Definition buffers.h:80
void update_offset_stride()
Definition buffers.cpp:221
bool modified(const BufferParams &other) const
Definition buffers.cpp:227
int full_height
Definition buffers.h:88
int window_height
Definition buffers.h:82
int window_width
Definition buffers.h:81
NODE_DECLARE int width
Definition buffers.h:72
int window_x
Definition buffers.h:79
DenoiserType type
Definition denoise.h:61
bool use_gpu
Definition denoise.h:75
NODE_DECLARE bool use
Definition denoise.h:58
Device * get_denoiser_device() const
Definition denoiser.cpp:256
static unique_ptr< Denoiser > create(Device *denoise_device, Device *cpu_fallback_device, const DenoiseParams &params)
Definition denoiser.cpp:141
const DenoiseParams & get_params() const
Definition denoiser.cpp:218
vector< DeviceInfo > multi_devices
bool display_device
DeviceType type
string description
KernelData data
Definition devicescene.h:95
Profiler & profiler
Stats & stats
bool headless
virtual void foreach_device(const function< void(Device *)> &callback)
virtual void set_error(const string &error)
DeviceInfo info
virtual void * get_guiding_device() const
bool have_error()
Definition film.h:30
bool get_render_tile_pixels(const RenderBuffers *render_buffers, const Destination &destination) const
const PassAccessInfo & get_pass_access_info() const
RenderBuffers * get_render_buffers()
void set_effective_buffer_params(const BufferParams &effective_full_params, const BufferParams &effective_big_tile_params, const BufferParams &effective_buffer_params)
virtual void render_samples(RenderStatistics &statistics, int start_sample, int samples_num, int sample_offset)=0
static unique_ptr< PathTraceWork > create(Device *device, Film *film, DeviceScene *device_scene, bool *cancel_requested_flag)
Device * get_device() const
void device_free()
Progress * progress_
Definition path_trace.h:334
function< void(void)> progress_update_cb
Definition path_trace.h:184
void denoise(const RenderWork &render_work)
int2 get_render_tile_size() const
void finalize_full_buffer_on_disk(const RenderWork &render_work)
struct PathTrace::@1422 render_state_
void process_full_buffer_from_disk(string_view filename)
void tile_buffer_write()
Device * device_
Definition path_trace.h:253
unique_ptr< Denoiser > denoiser_
Definition path_trace.h:286
void update_effective_work_buffer_params(const RenderWork &render_work)
BufferParams full_params_
Definition path_trace.h:282
void rebalance(const RenderWork &render_work)
bool is_cancel_requested()
void init_render_buffers(const RenderWork &render_work)
void copy_to_render_buffers(RenderBuffers *render_buffers)
void destroy_gpu_resources()
void set_progress(Progress *progress)
vector< unique_ptr< PathTraceWork > > path_trace_works_
Definition path_trace.h:276
void copy_from_render_buffers(RenderBuffers *render_buffers)
Device * denoise_device_
Definition path_trace.h:257
void set_denoiser_params(const DenoiseParams &params)
void progress_update_if_needed(const RenderWork &render_work)
void update_display(const RenderWork &render_work)
void progress_set_status(const string &status, const string &substatus="")
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling)
void set_output_driver(unique_ptr< OutputDriver > driver)
void reset(const BufferParams &full_params, const BufferParams &big_tile_params, bool reset_rendering)
DeviceScene * device_scene_
Definition path_trace.h:263
bool ready_to_reset()
bool get_render_tile_pixels(const PassAccessor &pass_accessor, const PassAccessor::Destination &destination)
void cancel()
void guiding_update_structures()
void guiding_prepare_structures()
int2 get_render_tile_offset() const
unique_ptr< Device > cpu_device_
Definition path_trace.h:260
void set_guiding_params(const GuidingParams &params, const bool reset)
void tile_buffer_write_to_disk()
void set_display_driver(unique_ptr< DisplayDriver > driver)
void clear_display()
void path_trace(RenderWork &render_work)
void render_pipeline(RenderWork render_work)
unique_ptr< OutputDriver > output_driver_
Definition path_trace.h:272
unique_ptr< PathTraceDisplay > display_
Definition path_trace.h:269
vector< WorkBalanceInfo > work_balance_infos_
Definition path_trace.h:279
RenderBuffers * render_buffers
Definition path_trace.h:356
bool copy_render_tile_from_device()
bool has_denoised_result
Definition path_trace.h:326
PathTrace(Device *device, Device *denoiser_device, Film *film, DeviceScene *device_scene, RenderScheduler &render_scheduler, TileManager &tile_manager)
bool did_draw_after_reset_
Definition path_trace.h:352
void render_init_kernel_execution()
int2 get_render_size() const
TileManager & tile_manager_
Definition path_trace.h:266
unique_ptr< PathTraceWork > big_tile_denoise_work_
Definition path_trace.h:289
void update_work_buffer_params_if_needed(const RenderWork &render_work)
void alloc_work_memory()
BufferParams big_tile_params_
Definition path_trace.h:283
void draw()
void write_tile_buffer(const RenderWork &render_work)
void tile_buffer_read()
bool set_render_tile_pixels(PassAccessor &pass_accessor, const PassAccessor::Source &source)
void render(const RenderWork &render_work)
Film * film_
Definition path_trace.h:262
string full_report() const
void adaptive_sample(RenderWork &render_work)
void load_kernels()
const BufferParams & get_render_tile_params() const
int resolution_divider
Definition path_trace.h:320
int get_num_render_tile_samples() const
struct PathTrace::@1424 full_frame_state_
struct PathTrace::@1423 render_cancel_
void flush_display()
RenderScheduler & render_scheduler_
Definition path_trace.h:265
int get_num_samples_in_buffer()
void cryptomatte_postprocess(const RenderWork &render_work)
void update_allocated_work_buffer_params()
void set_cancel(const string &cancel_message_)
Definition progress.h:86
bool get_cancel() const
Definition progress.h:93
void set_status(const string &status_, const string &substatus_="")
Definition progress.h:263
void add_samples(uint64_t pixel_samples_, int tile_sample)
Definition progress.h:215
void set_error(const string &error_message_)
Definition progress.h:113
BufferParams params
Definition buffers.h:157
bool copy_from_device()
Definition buffers.cpp:289
void copy_to_device()
Definition buffers.cpp:302
void reset(const BufferParams &params)
Definition buffers.cpp:274
void report_display_update_time(const RenderWork &render_work, double time)
void report_adaptive_filter_time(const RenderWork &render_work, double time, bool is_cancelled)
void set_need_schedule_rebalance(bool need_schedule_rebalance)
int get_num_rendered_samples() const
string full_report() const
void report_rebalance_time(const RenderWork &render_work, double time, bool balance_changed)
void report_denoise_time(const RenderWork &render_work, double time)
void set_denoiser_params(const DenoiseParams &params)
bool render_work_reschedule_on_idle(RenderWork &render_work)
void report_path_trace_time(const RenderWork &render_work, double time, bool is_cancelled)
bool is_denoiser_gpu_used() const
void report_path_trace_occupancy(const RenderWork &render_work, float occupancy)
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling)
bool render_work_reschedule_on_converge(RenderWork &render_work)
void report_work_begin(const RenderWork &render_work)
void set_need_schedule_cryptomatte(bool need_schedule_cryptomatte)
void set_limit_samples_per_update(const int limit_samples)
struct RenderWork::@1432 full
bool use_denoised_result
bool init_render_buffers
struct RenderWork::@1430 cryptomatte
struct RenderWork::@1429 adaptive_sampling
struct RenderWork::@1431 tile
struct RenderWork::@1433 display
struct RenderWork::@1428 path_trace
bool has_multiple_tiles() const
bool has_written_tiles() const
const int2 get_size() const
const Tile & get_current_tile() const
bool write_tile(const RenderBuffers &tile_buffers)
bool read_full_buffer_from_disk(string_view filename, RenderBuffers *buffers, DenoiseParams *denoise_params)
void finish_write_tiles()
int get_tile_overscan() const
@ DENOISER_OPTIX
Definition denoise.h:14
@ DENOISER_OPENIMAGEDENOISE
Definition denoise.h:15
DenoiseParams get_effective_denoise_params(Device *denoiser_device, Device *cpu_fallback_device, const DenoiseParams &params, Device *&single_denoiser_device)
Definition denoiser.cpp:99
DEGForeachIDComponentCallback callback
CCL_NAMESPACE_BEGIN Device * device_cpu_create(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
void device_cpu_info(vector< DeviceInfo > &devices)
#define CCL_NAMESPACE_END
DeviceType
@ DEVICE_DUMMY
@ DEVICE_NONE
@ DEVICE_METAL
@ DEVICE_MULTI
@ DEVICE_CUDA
@ DEVICE_CPU
@ DEVICE_HIPRT
@ DEVICE_OPTIX
@ DEVICE_HIP
@ DEVICE_ONEAPI
ccl_device_forceinline int2 make_int2(const int x, const int y)
static const char * to_string(const Interpolation &interp)
Definition gl_shader.cc:82
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_global const KernelWorkTile * tile
#define PASS_UNUSED
@ PASS_SAMPLE_COUNT
@ GUIDING_TYPE_VMM
@ GUIDING_TYPE_DIRECTIONAL_QUAD_TREE
@ GUIDING_TYPE_PARALLAX_AWARE_VMM
#define VLOG_INFO
Definition log.h:72
#define VLOG_IS_ON(severity)
Definition log.h:36
#define DCHECK(expression)
Definition log.h:51
#define DCHECK_EQ(a, b)
Definition log.h:59
#define VLOG_WORK
Definition log.h:75
#define LOG(severity)
Definition log.h:33
#define DCHECK_NE(a, b)
Definition log.h:58
#define VLOG_DEBUG
Definition log.h:81
PassMode
Definition pass.h:20
static string device_info_list_report(const string &message, const DeviceInfo &device_info)
static void foreach_sliced_buffer_params(const vector< unique_ptr< PathTraceWork > > &path_trace_works, const vector< WorkBalanceInfo > &work_balance_infos, const BufferParams &buffer_params, const int overscan, const Callback &callback)
static string path_trace_devices_report(const vector< unique_ptr< PathTraceWork > > &path_trace_works)
static string full_device_info_description(const DeviceInfo &device_info)
static BufferParams scale_buffer_params(const BufferParams &params, int resolution_divider)
static string get_layer_view_name(const RenderBuffers &buffers)
static const char * device_type_for_description(const DeviceType type)
static string denoiser_device_report(const Denoiser *denoiser)
#define min(a, b)
Definition sort.c:32
unsigned __int64 uint64_t
Definition stdint.h:90
int x
Definition types_int2.h:15
int y
Definition types_int2.h:15
static void thread_capture_fp_settings()
Definition tbb.h:33
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:30
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:36
float max
char * buffers[2]
CCL_NAMESPACE_BEGIN void work_balance_do_initial(vector< WorkBalanceInfo > &work_balance_infos)
bool work_balance_do_rebalance(vector< WorkBalanceInfo > &work_balance_infos)