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