Blender V4.3
eevee_lightprobe_volume.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BKE_global.hh"
8#include "BKE_lightprobe.h"
9
10#include "GPU_capabilities.hh"
11#include "GPU_debug.hh"
12
13#include "BLI_math_rotation.hh"
14
15#include "eevee_instance.hh"
16
18
19#include <cstdio>
20
21namespace blender::eevee {
22
23/* -------------------------------------------------------------------- */
26
28{
29 display_grids_enabled_ = DRW_state_draw_support();
30
31 /* This might become an option in the future. */
32 bool use_l2_band = false;
33 int sh_coef_len = use_l2_band ? 9 : 4;
35 int texel_byte_size = 8; /* Assumes GPU_RGBA16F. */
36 uint atlas_col_count = 0;
37 uint atlas_row_count = 0;
38
39 if (assign_if_different(irradiance_pool_size_,
40 (uint)inst_.scene->eevee.gi_irradiance_pool_size) ||
41 !irradiance_atlas_tx_.is_valid())
42 {
43 irradiance_atlas_tx_.free();
44 /* Find highest pool size within device limits. */
45 for (uint irradiance_pool_size = irradiance_pool_size_;
46 irradiance_pool_size >= 16 && !irradiance_atlas_tx_.is_valid();
47 irradiance_pool_size >>= 1)
48 {
49 int atlas_byte_size = 1024 * 1024 * irradiance_pool_size;
50 /* Reshape texture to improve grid occupancy within device limits. */
51 constexpr uint atlas_col_count_min = 16;
52 constexpr uint atlas_col_count_max = 16384;
53 for (uint atlas_col_count_try = atlas_col_count_min;
54 atlas_col_count_try <= atlas_col_count_max && !irradiance_atlas_tx_.is_valid();
55 atlas_col_count_try <<= 1)
56 {
57 int3 atlas_extent(IRRADIANCE_GRID_BRICK_SIZE);
58 atlas_extent.z *= sh_coef_len;
59 /* Add space for validity bits. */
60 atlas_extent.z += IRRADIANCE_GRID_BRICK_SIZE / 4;
61 atlas_extent.x *= atlas_col_count_try;
62
63 /* Determine the row count depending on the scene settings. */
64 int row_byte_size = math::reduce_mul(atlas_extent) * texel_byte_size;
65 atlas_row_count = divide_ceil_u(atlas_byte_size, row_byte_size);
66 atlas_extent.y *= atlas_row_count;
67
71 irradiance_atlas_tx_.ensure_3d(VOLUME_PROBE_FORMAT, atlas_extent, usage);
72 if (irradiance_atlas_tx_.is_valid()) {
73 do_full_update_ = true;
74 irradiance_pool_size_alloc_ = irradiance_pool_size;
75 atlas_col_count = atlas_col_count_try;
76 }
77 }
78 }
79 }
80 if (irradiance_pool_size_alloc_ != irradiance_pool_size_) {
81 inst_.info_append_i18n(
82 "Warning: Could not allocate light probes volume pool of {} MB, using {} MB instead.",
83 irradiance_pool_size_,
84 irradiance_pool_size_alloc_);
85 }
86
87 if (do_full_update_) {
88 do_update_world_ = true;
89
90 /* Delete all references to existing bricks. */
91 for (VolumeProbe &grid : inst_.light_probes.volume_map_.values()) {
92 grid.bricks.clear();
93 }
94 brick_pool_.clear();
95 /* Fill with all the available bricks. */
96 for (auto i : IndexRange(atlas_row_count * atlas_col_count)) {
97 if (i == 0) {
98 /* Reserve one brick for the world. */
99 world_brick_index_ = 0;
100 }
101 else {
102 IrradianceBrick brick;
103 brick.atlas_coord = uint2(i % atlas_col_count, i / atlas_col_count) *
105 brick_pool_.append(irradiance_brick_pack(brick));
106 }
107 }
108
109 if (irradiance_atlas_tx_.is_valid()) {
110 /* Clear the pool to avoid any interpolation to undefined values. */
111 irradiance_atlas_tx_.clear(float4(0.0f));
112 }
113 }
114
115 if (irradiance_atlas_tx_.is_valid() == false) {
116 inst_.info_append_i18n("Irradiance Atlas texture could not be created");
117 }
118}
119
121{
122 if (inst_.is_baking()) {
123 bake.sync();
124 }
125}
126
128{
129 if (brick_pool_.size() < brick_len) {
130 /* Fail allocation. Not enough brick in the atlas. */
131 return {};
132 }
133 Vector<IrradianceBrickPacked> allocated(brick_len);
134 /* Copy bricks to return vector. */
135 allocated.as_mutable_span().copy_from(brick_pool_.as_span().take_back(brick_len));
136 /* Remove bricks from the pool. */
137 brick_pool_.resize(brick_pool_.size() - brick_len);
138
139 return allocated;
140}
141
143{
144 brick_pool_.extend(bricks.as_span());
145 bricks.clear();
146}
147
149{
150 Vector<VolumeProbe *> grid_loaded;
151
152 bool any_update = false;
153 /* First allocate the needed bricks and populate the brick buffer. */
154 bricks_infos_buf_.clear();
155 for (VolumeProbe &grid : inst_.light_probes.volume_map_.values()) {
156 LightProbeGridCacheFrame *cache = grid.cache ? grid.cache->grid_static_cache : nullptr;
157 if (cache == nullptr) {
158 continue;
159 }
160
161 if (cache->baking.L0 == nullptr && cache->irradiance.L0 == nullptr) {
162 /* No data. */
163 continue;
164 }
165
166 int3 grid_size = int3(cache->size);
167 if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) {
168 inst_.info_append_i18n("Error: Malformed irradiance grid data");
169 continue;
170 }
171
172 /* TODO frustum cull and only load visible grids. */
173
174 /* Note that we reserve 1 slot for the world irradiance. */
175 if (grid_loaded.size() >= IRRADIANCE_GRID_MAX - 1) {
176 inst_.info_append_i18n("Error: Too many irradiance grids in the scene");
177 /* TODO frustum cull and only load visible grids. */
178 // inst_.info_append_i18n("Error: Too many grid visible");
179 continue;
180 }
181
182 int3 grid_size_with_padding = grid_size + 2;
183 if (grid.bricks.is_empty()) {
184 int3 grid_size_in_bricks = math::divide_ceil(grid_size_with_padding,
186 int brick_len = grid_size_in_bricks.x * grid_size_in_bricks.y * grid_size_in_bricks.z;
187 grid.bricks = bricks_alloc(brick_len);
188
189 if (grid.bricks.is_empty()) {
190 inst_.info_append_i18n("Error: Irradiance grid allocation failed");
191 continue;
192 }
193 grid.do_update = true;
194 }
195
196 if (do_update_world_) {
197 /* Update grid composition if world changed. */
198 grid.do_update = true;
199 }
200
201 any_update = any_update || grid.do_update;
202
203 grid.brick_offset = bricks_infos_buf_.size();
204 bricks_infos_buf_.extend(grid.bricks);
205
206 float4x4 grid_to_world = grid.object_to_world * math::from_location<float4x4>(float3(-1.0f)) *
208 float3(2.0f / float3(grid_size_with_padding - 1))) *
210
211 grid.world_to_grid_transposed = float3x4(math::transpose(math::invert(grid_to_world)));
212 grid.grid_size_padded = grid_size_with_padding;
213 grid_loaded.append(&grid);
214 }
215
216 /* TODO: This is greedy update detection. We should check if a change can influence each grid
217 * before tagging update. But this is a bit too complex and update is quite cheap. So we update
218 * everything if there is any update on any grid. */
219 if (any_update) {
220 for (VolumeProbe *grid : grid_loaded) {
221 grid->do_update = true;
222 }
223 }
224
225 /* Then create brick & grid infos UBOs content. */
226 int world_grid_index = 0;
227 {
228 /* Stable sorting of grids. */
229 std::sort(
230 grid_loaded.begin(), grid_loaded.end(), [](const VolumeProbe *a, const VolumeProbe *b) {
231 float volume_a = math::determinant(float3x3(a->object_to_world));
232 float volume_b = math::determinant(float3x3(b->object_to_world));
233 if (volume_a != volume_b) {
234 /* Smallest first. */
235 return volume_a < volume_b;
236 }
237 /* Volumes are identical. Any arbitrary criteria can be used to sort them.
238 * Use position to avoid unstable result caused by depsgraph non deterministic eval
239 * order. This could also become a priority parameter. */
241 float3 _b = b->object_to_world.location();
242 if (_a.x != _b.x) {
243 return _a.x < _b.x;
244 }
245 else if (_a.y != _b.y) {
246 return _a.y < _b.y;
247 }
248 else if (_a.z != _b.z) {
249 return _a.z < _b.z;
250 }
251 else {
252 /* Fallback to memory address, since there's no good alternative. */
253 return a < b;
254 }
255 });
256
257 /* Insert grids in UBO in sorted order. */
258 int grids_len = 0;
259 for (VolumeProbe *grid : grid_loaded) {
260 grid->grid_index = grids_len;
261 grids_infos_buf_[grids_len++] = *grid;
262 }
263
264 /* Insert world grid last. */
265 world_grid_index = grids_len++;
266
267 VolumeProbeData grid;
269 grid.grid_size_padded = int3(1);
270 grid.brick_offset = bricks_infos_buf_.size();
271 grid.normal_bias = 0.0f;
272 grid.view_bias = 0.0f;
273 grid.facing_bias = 0.0f;
274 grids_infos_buf_[world_grid_index] = grid;
275
276 bricks_infos_buf_.append(world_brick_index_);
277
278 if (grids_len < IRRADIANCE_GRID_MAX) {
279 /* Tag last grid as invalid to stop the iteration. */
280 grids_infos_buf_[grids_len].grid_size_padded = int3(-1);
281 }
282
283 bricks_infos_buf_.push_update();
284 grids_infos_buf_.push_update();
285 }
286
287 /* Upload data for world. */
288 if (do_update_world_) {
289 grid_upload_ps_.init();
290 grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_WORLD));
291 grid_upload_ps_.bind_resources(inst_.uniform_data);
292 grid_upload_ps_.bind_ssbo("harmonic_buf", &inst_.sphere_probes.spherical_harmonics_buf());
293 grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
294 grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
295 grid_upload_ps_.push_constant("grid_index", world_grid_index);
296 grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
297 /* Sync with extraction. */
298 grid_upload_ps_.barrier(GPU_BARRIER_SHADER_STORAGE);
299 /* Only upload one brick. */
300 grid_upload_ps_.dispatch(int3(1));
301 /* Sync with next load. */
302 grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
303
304 inst_.manager->submit(grid_upload_ps_);
305 }
306
307 /* Upload data for each grid that need to be inserted in the atlas.
308 * Upload by order of dependency. */
309 /* Start at world index to not load any other grid (+1 because we decrement at loop start). */
310 int grid_start_index = grid_loaded.size() + 1;
311 for (auto it = grid_loaded.rbegin(); it != grid_loaded.rend(); ++it) {
312 grid_start_index--;
313
314 VolumeProbe *grid = *it;
315 if (!grid->do_update) {
316 continue;
317 }
318
319 grid->do_update = false;
320
321 LightProbeGridCacheFrame *cache = grid->cache->grid_static_cache;
322
323 /* Staging textures are recreated for each light grid to avoid increasing VRAM usage. */
324 draw::Texture irradiance_a_tx = {"irradiance_a_tx"};
325 draw::Texture irradiance_b_tx = {"irradiance_b_tx"};
326 draw::Texture irradiance_c_tx = {"irradiance_c_tx"};
327 draw::Texture irradiance_d_tx = {"irradiance_d_tx"};
328 draw::Texture validity_tx = {"validity_tx"};
329
331 int3 grid_size = int3(cache->size);
332 if (cache->baking.L0) {
333 irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L0);
334 irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_a);
335 irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_b);
336 irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_c);
337 validity_tx.ensure_3d(GPU_R16F, grid_size, usage, cache->baking.validity);
338 if (cache->baking.validity == nullptr) {
339 /* Avoid displaying garbage data. */
340 validity_tx.clear(float4(0.0));
341 }
342 }
343 else if (cache->irradiance.L0) {
344 irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L0);
345 irradiance_b_tx.ensure_3d(
346 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_a);
347 irradiance_c_tx.ensure_3d(
348 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_b);
349 irradiance_d_tx.ensure_3d(
350 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_c);
351 validity_tx.ensure_3d(GPU_R8, grid_size, usage);
352 if (cache->connectivity.validity) {
353 /* TODO(fclem): Make texture creation API work with different data types. */
354 GPU_texture_update_sub(validity_tx,
356 cache->connectivity.validity,
357 0,
358 0,
359 0,
360 UNPACK3(grid_size));
361 }
362 else {
363 /* Avoid displaying garbage data. */
364 validity_tx.clear(float4(0.0));
365 }
366 }
367 else {
368 continue;
369 }
370
371 if (irradiance_a_tx.is_valid() == false) {
372 inst_.info_append_i18n("Error: Could not allocate irradiance staging texture");
373 /* Avoid undefined behavior with uninitialized values. Still load a clear texture. */
374 const float4 zero(0.0f);
375 irradiance_a_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
376 irradiance_b_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
377 irradiance_c_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
378 irradiance_d_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
379 validity_tx.ensure_3d(GPU_R16F, int3(1), usage, zero);
380 }
381
382 bool visibility_available = cache->visibility.L0 != nullptr;
383 bool is_baking = cache->irradiance.L0 == nullptr;
384
385 draw::Texture visibility_a_tx = {"visibility_a_tx"};
386 draw::Texture visibility_b_tx = {"visibility_b_tx"};
387 draw::Texture visibility_c_tx = {"visibility_c_tx"};
388 draw::Texture visibility_d_tx = {"visibility_d_tx"};
389 if (visibility_available) {
390 visibility_a_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L0);
391 visibility_b_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L1_a);
392 visibility_c_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L1_b);
393 visibility_d_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L1_c);
394
395 GPU_texture_swizzle_set(visibility_a_tx, "111r");
396 GPU_texture_swizzle_set(visibility_b_tx, "111r");
397 GPU_texture_swizzle_set(visibility_c_tx, "111r");
398 GPU_texture_swizzle_set(visibility_d_tx, "111r");
399 }
400 else if (!is_baking) {
401 /* Missing visibility. Load default visibility L0 = 1, L1 = (0, 0, 0). */
402 GPU_texture_swizzle_set(irradiance_a_tx, "rgb1");
403 GPU_texture_swizzle_set(irradiance_b_tx, "rgb0");
404 GPU_texture_swizzle_set(irradiance_c_tx, "rgb0");
405 GPU_texture_swizzle_set(irradiance_d_tx, "rgb0");
406 }
407
408 grid_upload_ps_.init();
409 grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD));
410
411 grid_upload_ps_.bind_resources(inst_.uniform_data);
412 grid_upload_ps_.push_constant("validity_threshold", grid->validity_threshold);
413 grid_upload_ps_.push_constant("dilation_threshold", grid->dilation_threshold);
414 grid_upload_ps_.push_constant("dilation_radius", grid->dilation_radius);
415 grid_upload_ps_.push_constant("grid_index", grid->grid_index);
416 grid_upload_ps_.push_constant("grid_start_index", grid_start_index);
417 grid_upload_ps_.push_constant("grid_local_to_world", grid->object_to_world);
418 grid_upload_ps_.push_constant("grid_intensity_factor", grid->intensity);
419 grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
420 grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
421 grid_upload_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
422 grid_upload_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
423 grid_upload_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
424 grid_upload_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
425 grid_upload_ps_.bind_texture("validity_tx", &validity_tx);
426 grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
427 /* NOTE: We are read and writing the same texture that we are sampling from. If that causes an
428 * issue, we should revert to manual trilinear interpolation. */
429 grid_upload_ps_.bind_texture("irradiance_atlas_tx", &irradiance_atlas_tx_);
430 /* If visibility is invalid, either it is still baking and visibility is stored with
431 * irradiance, or it is missing and we sample a completely uniform visibility. */
432 bool use_vis = visibility_available;
433 grid_upload_ps_.bind_texture("visibility_a_tx", use_vis ? &visibility_a_tx : &irradiance_a_tx);
434 grid_upload_ps_.bind_texture("visibility_b_tx", use_vis ? &visibility_b_tx : &irradiance_b_tx);
435 grid_upload_ps_.bind_texture("visibility_c_tx", use_vis ? &visibility_c_tx : &irradiance_c_tx);
436 grid_upload_ps_.bind_texture("visibility_d_tx", use_vis ? &visibility_d_tx : &irradiance_d_tx);
437
438 /* Runtime grid is padded for blending with surrounding probes. */
439 int3 grid_size_with_padding = grid_size + 2;
440 /* Note that we take into account the padding border of each brick. */
441 int3 grid_size_in_bricks = math::divide_ceil(grid_size_with_padding,
443 grid_upload_ps_.dispatch(grid_size_in_bricks);
444 /* Sync with next load. */
445 grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
446
447 inst_.manager->submit(grid_upload_ps_);
448
449 irradiance_a_tx.free();
450 irradiance_b_tx.free();
451 irradiance_c_tx.free();
452 irradiance_d_tx.free();
453 }
454
455 do_full_update_ = false;
456 do_update_world_ = false;
457}
458
459void VolumeProbeModule::viewport_draw(View &view, GPUFrameBuffer *view_fb)
460{
461 if (!inst_.is_baking()) {
462 debug_pass_draw(view, view_fb);
463 display_pass_draw(view, view_fb);
464 }
465}
466
467void VolumeProbeModule::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
468{
469 switch (inst_.debug_mode) {
471 inst_.info_append("Debug Mode: Surfels Normal");
472 break;
474 inst_.info_append("Debug Mode: Surfels Cluster");
475 break;
477 inst_.info_append("Debug Mode: Surfels Irradiance");
478 break;
480 inst_.info_append("Debug Mode: Surfels Visibility");
481 break;
483 inst_.info_append("Debug Mode: Irradiance Validity");
484 break;
486 inst_.info_append("Debug Mode: Virtual Offset");
487 break;
488 default:
489 /* Nothing to display. */
490 return;
491 }
492
493 for (const VolumeProbe &grid : inst_.light_probes.volume_map_.values()) {
494 if (grid.cache == nullptr) {
495 continue;
496 }
497
498 LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
499
500 if (cache == nullptr) {
501 continue;
502 }
503
504 switch (inst_.debug_mode) {
505 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL:
506 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER:
507 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_VISIBILITY:
508 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: {
509 if (cache->surfels == nullptr || cache->surfels_len == 0) {
510 continue;
511 }
512 float max_axis_len = math::reduce_max(math::to_scale(grid.object_to_world));
513 debug_ps_.init();
514 debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
516 debug_ps_.framebuffer_set(&view_fb);
517 debug_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS));
518 debug_ps_.push_constant("debug_surfel_radius", 0.5f * max_axis_len / grid.surfel_density);
519 debug_ps_.push_constant("debug_mode", int(inst_.debug_mode));
520
521 debug_surfels_buf_.resize(cache->surfels_len);
522 /* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an
523 * input data. */
524 Span<Surfel> grid_surfels(static_cast<Surfel *>(cache->surfels), cache->surfels_len);
525 MutableSpan<Surfel>(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels);
526 debug_surfels_buf_.push_update();
527
528 debug_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_);
529 debug_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4);
530
531 inst_.manager->submit(debug_ps_, view);
532 break;
533 }
534
535 case eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY:
536 case eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET: {
537 int3 grid_size = int3(cache->size);
538 debug_ps_.init();
539 debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
541 debug_ps_.framebuffer_set(&view_fb);
542 debug_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_IRRADIANCE_GRID));
543 debug_ps_.push_constant("debug_mode", int(inst_.debug_mode));
544 debug_ps_.push_constant("grid_mat", grid.object_to_world);
545
547 Texture debug_data_tx = {"debug_data_tx"};
548
549 if (inst_.debug_mode == eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY) {
550 const float *data;
551 if (cache->baking.validity) {
552 data = cache->baking.validity;
553 debug_data_tx.ensure_3d(GPU_R16F, grid_size, usage, data);
554 }
555 else if (cache->connectivity.validity) {
556 debug_data_tx.ensure_3d(GPU_R8, grid_size, usage);
557 /* TODO(fclem): Make texture creation API work with different data types. */
558 GPU_texture_update_sub(debug_data_tx,
560 cache->connectivity.validity,
561 0,
562 0,
563 0,
564 UNPACK3(grid_size));
565 }
566 else {
567 continue;
568 }
569 debug_ps_.push_constant("debug_value", grid.validity_threshold);
570 debug_ps_.bind_texture("debug_data_tx", debug_data_tx);
571 debug_ps_.draw_procedural(GPU_PRIM_POINTS, 1, grid_size.x * grid_size.y * grid_size.z);
572 }
573 else {
574 if (cache->baking.virtual_offset) {
575 const float *data = (const float *)cache->baking.virtual_offset;
576 debug_data_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, data);
577 }
578 else {
579 continue;
580 }
581 debug_ps_.bind_texture("debug_data_tx", debug_data_tx);
582 debug_ps_.draw_procedural(
583 GPU_PRIM_LINES, 1, grid_size.x * grid_size.y * grid_size.z * 2);
584 }
585
586 inst_.manager->submit(debug_ps_, view);
587 break;
588 }
589
590 default:
591 break;
592 }
593 }
594}
595
596void VolumeProbeModule::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
597{
598 if (!display_grids_enabled_) {
599 return;
600 }
601
602 for (const VolumeProbe &grid : inst_.light_probes.volume_map_.values()) {
603 if (!grid.viewport_display || grid.viewport_display_size == 0.0f || !grid.cache ||
604 !grid.cache->grid_static_cache)
605 {
606 continue;
607 }
608
609 LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
610
611 /* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */
612 draw::Texture irradiance_a_tx = {"irradiance_a_tx"};
613 draw::Texture irradiance_b_tx = {"irradiance_b_tx"};
614 draw::Texture irradiance_c_tx = {"irradiance_c_tx"};
615 draw::Texture irradiance_d_tx = {"irradiance_d_tx"};
616 draw::Texture validity_tx = {"validity_tx"};
617
619 int3 grid_size = int3(cache->size);
620 if (cache->baking.L0) {
621 irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L0);
622 irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_a);
623 irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_b);
624 irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_c);
625 validity_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->baking.validity);
626 if (cache->baking.validity == nullptr) {
627 /* Avoid displaying garbage data. */
628 validity_tx.clear(float4(0.0));
629 }
630 }
631 else if (cache->irradiance.L0) {
632 irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L0);
633 irradiance_b_tx.ensure_3d(
634 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_a);
635 irradiance_c_tx.ensure_3d(
636 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_b);
637 irradiance_d_tx.ensure_3d(
638 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_c);
639 validity_tx.ensure_3d(GPU_R8, grid_size, usage);
640 if (cache->connectivity.validity) {
641 /* TODO(fclem): Make texture creation API work with different data types. */
642 GPU_texture_update_sub(validity_tx,
644 cache->connectivity.validity,
645 0,
646 0,
647 0,
648 UNPACK3(grid_size));
649 }
650 else {
651 /* Avoid displaying garbage data. */
652 validity_tx.clear(float4(0.0));
653 }
654 }
655 else {
656 continue;
657 }
658
659 display_grids_ps_.init();
660 display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
662 display_grids_ps_.framebuffer_set(&view_fb);
663 display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_VOLUME));
664
665 display_grids_ps_.push_constant("sphere_radius", grid.viewport_display_size);
666 display_grids_ps_.push_constant("grid_resolution", grid_size);
667 display_grids_ps_.push_constant("grid_to_world", grid.object_to_world);
668 display_grids_ps_.push_constant("world_to_grid", grid.world_to_object);
669 /* TODO(fclem): Make it an option when display options are moved to probe DNA. */
670 display_grids_ps_.push_constant("display_validity", false);
671
672 display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
673 display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
674 display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
675 display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
676 display_grids_ps_.bind_texture("validity_tx", &validity_tx);
677
678 int sample_count = int(BKE_lightprobe_grid_cache_frame_sample_count(cache));
679 int triangle_count = sample_count * 2;
680 display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, triangle_count * 3);
681
682 inst_.manager->submit(display_grids_ps_, view);
683
684 irradiance_a_tx.free();
685 irradiance_b_tx.free();
686 irradiance_c_tx.free();
687 irradiance_d_tx.free();
688 }
689}
690
692
693/* -------------------------------------------------------------------- */
696
697void IrradianceBake::init(const Object &probe_object)
698{
699 float max_axis_len = math::reduce_max(math::to_scale(probe_object.object_to_world()));
700
701 const ::LightProbe *lightprobe = static_cast<::LightProbe *>(probe_object.data);
702 surfel_density_ = lightprobe->grid_surfel_density / max_axis_len;
703 min_distance_to_surface_ = lightprobe->grid_surface_bias;
704 max_virtual_offset_ = lightprobe->grid_escape_bias;
705 clip_distance_ = lightprobe->clipend;
706 capture_world_ = (lightprobe->grid_flag & LIGHTPROBE_GRID_CAPTURE_WORLD);
707 capture_indirect_ = (lightprobe->grid_flag & LIGHTPROBE_GRID_CAPTURE_INDIRECT);
708 capture_emission_ = (lightprobe->grid_flag & LIGHTPROBE_GRID_CAPTURE_EMISSION);
709
710 /* Initialize views data, since they're used by other modules. */
712}
713
715{
716 {
717 PassSimple &pass = surfel_light_eval_ps_;
718 pass.init();
719 /* Apply lights contribution to scene surfel representation. */
720 pass.shader_set(inst_.shaders.static_shader_get(SURFEL_LIGHT));
721 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
722 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
723 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
724 pass.bind_resources(inst_.uniform_data);
725 pass.bind_resources(inst_.lights);
726 pass.bind_resources(inst_.shadows);
727 /* Sync with the surfel creation stage. */
731 pass.dispatch(&dispatch_per_surfel_);
732 }
733 {
734 PassSimple &pass = surfel_cluster_build_ps_;
735 pass.init();
736 pass.shader_set(inst_.shaders.static_shader_get(SURFEL_CLUSTER_BUILD));
737 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
738 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
739 pass.bind_image("cluster_list_img", &cluster_list_tx_);
741 pass.dispatch(&dispatch_per_surfel_);
743 }
744 {
745 PassSimple &pass = surfel_ray_build_ps_;
746 pass.init();
747 {
748 PassSimple::Sub &sub = pass.sub("ListBuild");
749 sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_BUILD));
750 sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
751 sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
752 sub.bind_ssbo("list_start_buf", &list_start_buf_);
753 sub.bind_ssbo("list_info_buf", &list_info_buf_);
755 sub.dispatch(&dispatch_per_surfel_);
756 }
757 {
758 PassSimple::Sub &sub = pass.sub("ListSort");
759 sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT));
760 sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
761 sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
762 sub.bind_ssbo("list_start_buf", &list_start_buf_);
763 sub.bind_ssbo("list_info_buf", &list_info_buf_);
765 sub.dispatch(&dispatch_per_list_);
766 }
767 }
768 {
769 PassSimple &pass = surfel_light_propagate_ps_;
770 pass.init();
771 {
772 PassSimple::Sub &sub = pass.sub("RayEval");
773 sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY));
774 sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
775 sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
776 sub.bind_resources(inst_.sphere_probes);
777 sub.push_constant("radiance_src", &radiance_src_);
778 sub.push_constant("radiance_dst", &radiance_dst_);
780 sub.dispatch(&dispatch_per_surfel_);
781 }
782 }
783 {
784 PassSimple &pass = irradiance_capture_ps_;
785 pass.init();
786 pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_RAY));
787 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
788 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
789 pass.bind_resources(inst_.sphere_probes);
790 pass.bind_ssbo("list_start_buf", &list_start_buf_);
791 pass.bind_ssbo("list_info_buf", &list_info_buf_);
792 pass.push_constant("radiance_src", &radiance_src_);
793 pass.bind_image("irradiance_L0_img", &irradiance_L0_tx_);
794 pass.bind_image("irradiance_L1_a_img", &irradiance_L1_a_tx_);
795 pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_);
796 pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_);
797 pass.bind_image("validity_img", &validity_tx_);
798 pass.bind_image("virtual_offset_img", &virtual_offset_tx_);
800 pass.dispatch(&dispatch_per_grid_sample_);
801 }
802 {
803 PassSimple &pass = irradiance_offset_ps_;
804 pass.init();
805 pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_OFFSET));
806 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
807 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
808 pass.bind_ssbo("list_start_buf", &list_start_buf_);
809 pass.bind_ssbo("list_info_buf", &list_info_buf_);
810 pass.bind_image("cluster_list_img", &cluster_list_tx_);
811 pass.bind_image("virtual_offset_img", &virtual_offset_tx_);
813 pass.dispatch(&dispatch_per_grid_sample_);
814 }
815}
816
818 const float3 &scene_max,
819 const float4x4 &probe_to_world)
820{
821 using namespace blender::math;
822
823 float3 location, scale;
824 Quaternion rotation;
825 to_loc_rot_scale(probe_to_world, location, rotation, scale);
826 /* Remove scale from view matrix. */
827 float4x4 viewinv = from_loc_rot_scale<float4x4>(location, rotation, float3(1.0f));
828 float4x4 viewmat = invert(viewinv);
829
830 /* Compute the intersection between the grid and the scene extents. */
831 float3 extent_min = float3(FLT_MAX);
832 float3 extent_max = float3(-FLT_MAX);
833 for (int x : {0, 1}) {
834 for (int y : {0, 1}) {
835 for (int z : {0, 1}) {
836 float3 ws_corner = scene_min + ((scene_max - scene_min) * float3(x, y, z));
837 float3 ls_corner = transform_point(viewmat, ws_corner);
838 extent_min = min(extent_min, ls_corner);
839 extent_max = max(extent_max, ls_corner);
840 }
841 }
842 }
843 /* Clip distance is added to every axis in both directions, not just Z. */
844 float3 target_extent = scale + clip_distance_;
845 extent_min = max(extent_min, -target_extent);
846 extent_max = min(extent_max, target_extent);
847
848 grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * (extent_max - extent_min)));
849 grid_pixel_extent_ = min(grid_pixel_extent_, int3(16384));
850
851 float3 ls_midpoint = midpoint(extent_min, extent_max);
852 scene_bound_sphere_ = float4(transform_point(viewinv, ls_midpoint),
853 distance(extent_min, extent_max) / 2.0f);
854
855 /* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to
856 * make any difference. The bottleneck is still the light propagation loop. */
857 auto sync_view = [&](View &view, CartesianBasis basis) {
858 float4x4 capture_viewinv = viewinv * from_rotation<float4x4>(basis);
859
860 float3 capture_extent_min = transform_point(invert(basis), extent_min);
861 float3 capture_extent_max = transform_point(invert(basis), extent_max);
862
863 float4x4 capture_winmat = projection::orthographic(capture_extent_min.x,
864 capture_extent_max.x,
865 capture_extent_min.y,
866 capture_extent_max.y,
867 -capture_extent_min.z,
868 -capture_extent_max.z);
869
870 view.visibility_test(false);
871 view.sync(invert(capture_viewinv), capture_winmat);
872 };
873
874 sync_view(view_x_, basis_x_);
875 sync_view(view_y_, basis_y_);
876 sync_view(view_z_, basis_z_);
877}
878
879void IrradianceBake::surfels_create(const Object &probe_object)
880{
886 using namespace blender::math;
887
888 const ::LightProbe *lightprobe = static_cast<::LightProbe *>(probe_object.data);
889
890 int3 grid_resolution = int3(&lightprobe->grid_resolution_x);
891 float4x4 grid_local_to_world = invert(probe_object.world_to_object());
892 float3 grid_scale = math::to_scale(probe_object.object_to_world());
893
894 /* TODO(fclem): Options. */
895 capture_info_buf_.capture_world_direct = capture_world_;
896 capture_info_buf_.capture_world_indirect = capture_world_ && capture_indirect_;
897 capture_info_buf_.capture_visibility_direct = !capture_world_;
898 capture_info_buf_.capture_visibility_indirect = !(capture_world_ && capture_indirect_);
899 capture_info_buf_.capture_indirect = capture_indirect_;
900 capture_info_buf_.capture_emission = capture_emission_;
901
902 LightProbeModule &light_probes = inst_.light_probes;
903 SphereProbeData &world_data = *static_cast<SphereProbeData *>(&light_probes.world_sphere_);
904 capture_info_buf_.world_atlas_coord = world_data.atlas_coord;
905
906 dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE));
907 capture_info_buf_.irradiance_grid_size = grid_resolution;
908 capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world;
909 capture_info_buf_.irradiance_grid_world_to_local = probe_object.world_to_object();
910 capture_info_buf_.irradiance_grid_world_to_local_rotation = float4x4(
911 invert(normalize(float3x3(grid_local_to_world))));
912
913 capture_info_buf_.min_distance_to_surface = min_distance_to_surface_;
914 capture_info_buf_.max_virtual_offset = max_virtual_offset_;
915 capture_info_buf_.surfel_radius = 0.5f / surfel_density_;
916 /* Make virtual offset distances scale relative. */
917 float min_distance_between_grid_samples = math::reduce_min(grid_scale / float3(grid_resolution));
918 capture_info_buf_.min_distance_to_surface *= min_distance_between_grid_samples;
919 capture_info_buf_.max_virtual_offset *= min_distance_between_grid_samples;
920 capture_info_buf_.clamp_direct = (lightprobe->grid_clamp_direct > 0.0) ?
921 lightprobe->grid_clamp_direct :
922 1e20f;
923 capture_info_buf_.clamp_indirect = (lightprobe->grid_clamp_indirect > 0.0) ?
924 lightprobe->grid_clamp_indirect :
925 1e20f;
926
929
930 /* 32bit float is needed here otherwise we loose too much energy from rounding error during the
931 * accumulation when the sample count is above 500. */
932 irradiance_L0_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
933 irradiance_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
934 irradiance_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
935 irradiance_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
936 validity_tx_.ensure_3d(GPU_R32F, grid_resolution, texture_usage);
937 virtual_offset_tx_.ensure_3d(GPU_RGBA16F, grid_resolution, texture_usage);
938
939 if (!irradiance_L0_tx_.is_valid() || !irradiance_L1_a_tx_.is_valid() ||
940 !irradiance_L1_b_tx_.is_valid() || !irradiance_L1_c_tx_.is_valid() ||
941 !validity_tx_.is_valid() || !virtual_offset_tx_.is_valid())
942 {
943 inst_.info_append_i18n("Error: Not enough memory to bake {}.", probe_object.id.name);
944 do_break_ = true;
945 return;
946 }
947
948 irradiance_L0_tx_.clear(float4(0.0f));
949 irradiance_L1_a_tx_.clear(float4(0.0f));
950 irradiance_L1_b_tx_.clear(float4(0.0f));
951 irradiance_L1_c_tx_.clear(float4(0.0f));
952 validity_tx_.clear(float4(0.0f));
953 virtual_offset_tx_.clear(float4(0.0f));
954
955 DRW_stats_group_start("IrradianceBake.SceneBounds");
956
957 {
958 draw::Manager &manager = *inst_.manager;
959 PassSimple &pass = irradiance_bounds_ps_;
960 pass.init();
961 pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_BOUNDS));
962 pass.bind_ssbo("capture_info_buf", &capture_info_buf_);
963 pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
964 pass.push_constant("resource_len", int(manager.resource_handle_count()));
965 pass.dispatch(
967 }
968
969 /* Raster the scene to query the number of surfel needed. */
970 capture_info_buf_.do_surfel_count = false;
971 capture_info_buf_.do_surfel_output = false;
972
973 const int neg_flt_max = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */
974 const int pos_flt_max = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */
975 capture_info_buf_.scene_bound_x_min = pos_flt_max;
976 capture_info_buf_.scene_bound_y_min = pos_flt_max;
977 capture_info_buf_.scene_bound_z_min = pos_flt_max;
978 capture_info_buf_.scene_bound_x_max = neg_flt_max;
979 capture_info_buf_.scene_bound_y_max = neg_flt_max;
980 capture_info_buf_.scene_bound_z_max = neg_flt_max;
981
982 capture_info_buf_.push_update();
983
984 inst_.manager->submit(irradiance_bounds_ps_);
985
987 capture_info_buf_.read();
988
989 if (capture_info_buf_.scene_bound_x_min == pos_flt_max) {
990 /* No valid object has been found. */
991 do_break_ = true;
992 return;
993 }
994
995 auto ordered_int_bits_to_float = [](int32_t int_value) -> float {
996 int32_t float_bits = (int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value;
997 return *reinterpret_cast<float *>(&float_bits);
998 };
999
1000 float3 scene_min = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_min),
1001 ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_min),
1002 ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_min));
1003 float3 scene_max = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_max),
1004 ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_max),
1005 ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_max));
1006 /* To avoid loosing any surface to the clipping planes, add some padding. */
1007 float epsilon = 1.0f / surfel_density_;
1008 scene_min -= epsilon;
1009 scene_max += epsilon;
1010 surfel_raster_views_sync(scene_min, scene_max, probe_object.object_to_world());
1011
1013
1014 /* WORKAROUND: Sync camera with correct bounds for light culling. */
1015 inst_.camera.sync();
1016 /* WORKAROUND: Sync shadows tile-maps count again with new camera bounds. Fixes issues with sun
1017 * lights. */
1018 inst_.shadows.end_sync();
1019 inst_.lights.end_sync();
1020
1021 DRW_stats_group_start("IrradianceBake.SurfelsCount");
1022
1023 /* Raster the scene to query the number of surfel needed. */
1024 capture_info_buf_.do_surfel_count = true;
1025 capture_info_buf_.do_surfel_output = false;
1026 capture_info_buf_.surfel_len = 0u;
1027 capture_info_buf_.push_update();
1028
1029 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_x_), grid_pixel_extent_).xy()));
1030 inst_.pipelines.capture.render(view_x_);
1031 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_y_), grid_pixel_extent_).xy()));
1032 inst_.pipelines.capture.render(view_y_);
1033 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_z_), grid_pixel_extent_).xy()));
1034 inst_.pipelines.capture.render(view_z_);
1035
1037
1038 /* Allocate surfel pool. */
1040 capture_info_buf_.read();
1041 if (capture_info_buf_.surfel_len == 0) {
1042 /* No surfel to allocate. */
1043 return;
1044 }
1045
1046 if (capture_info_buf_.surfel_len > surfels_buf_.size()) {
1047 printf("IrradianceBake: Allocating %u surfels.\n", capture_info_buf_.surfel_len);
1048
1049 size_t max_size = GPU_max_storage_buffer_size();
1051 int total_mem_kb, free_mem_kb;
1052 GPU_mem_stats_get(&total_mem_kb, &free_mem_kb);
1053 /* Leave at least 128MByte for OS and stuffs.
1054 * Try to avoid crashes because of OUT_OF_MEMORY errors. */
1055 size_t max_alloc = (size_t(total_mem_kb) - 128 * 1024) * 1024;
1056 /* Cap to 95% of available memory. */
1057 size_t max_free = size_t((size_t(free_mem_kb) * 1024) * 0.95f);
1058
1059 max_size = min(max_size, min(max_alloc, max_free));
1060 }
1061
1062 size_t required_mem = sizeof(Surfel) * (capture_info_buf_.surfel_len - surfels_buf_.size());
1063 if (required_mem > max_size) {
1064 const bool is_ssbo_bound = (max_size == GPU_max_storage_buffer_size());
1065 const uint req_mb = required_mem / (1024 * 1024);
1066 const uint max_mb = max_size / (1024 * 1024);
1067
1068 if (is_ssbo_bound) {
1069 inst_.info_append_i18n(
1070 "Cannot allocate enough video memory to bake \"{}\" ({} / {} MBytes).\n"
1071 "Try reducing surfel resolution or capture distance to lower the size of the "
1072 "allocation.",
1073 probe_object.id.name,
1074 req_mb,
1075 max_mb);
1076 }
1077 else {
1078 inst_.info_append_i18n(
1079 "Not enough available video memory to bake \"{}\" ({} / {} MBytes).\n"
1080 "Try reducing surfel resolution or capture distance to lower the size of the "
1081 "allocation.",
1082 probe_object.id.name,
1083 req_mb,
1084 max_mb);
1085 }
1086
1087 if (G.background) {
1088 /* Print something in background mode instead of failing silently. */
1089 fprintf(stderr, "%s", inst_.info_get());
1090 }
1091
1092 do_break_ = true;
1093 return;
1094 }
1095 }
1096
1097 surfels_buf_.resize(capture_info_buf_.surfel_len);
1098 surfels_buf_.clear_to_zero();
1099
1100 dispatch_per_surfel_.x = divide_ceil_u(surfels_buf_.size(), SURFEL_GROUP_SIZE);
1101
1102 DRW_stats_group_start("IrradianceBake.SurfelsCreate");
1103
1104 /* Raster the scene to generate the surfels. */
1105 capture_info_buf_.do_surfel_count = true;
1106 capture_info_buf_.do_surfel_output = true;
1107 capture_info_buf_.surfel_len = 0u;
1108 capture_info_buf_.push_update();
1109
1110 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_x_), grid_pixel_extent_).xy()));
1111 inst_.pipelines.capture.render(view_x_);
1112 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_y_), grid_pixel_extent_).xy()));
1113 inst_.pipelines.capture.render(view_y_);
1114 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_z_), grid_pixel_extent_).xy()));
1115 inst_.pipelines.capture.render(view_z_);
1116
1117 /* Sync with any other following pass using the surfel buffer. */
1119 /* Read back so that following push_update will contain correct surfel count. */
1120 capture_info_buf_.read();
1121
1123}
1124
1126{
1127 /* Use the last setup view. This should work since the view is orthographic. */
1128 /* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */
1129 inst_.render_buffers.acquire(int2(1));
1130 inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
1131 inst_.lights.set_view(view_z_, grid_pixel_extent_.xy());
1132 inst_.shadows.set_view(view_z_, grid_pixel_extent_.xy());
1133 inst_.render_buffers.release();
1134
1135 inst_.manager->submit(surfel_light_eval_ps_, view_z_);
1136}
1137
1139{
1140 if (max_virtual_offset_ == 0.0f) {
1141 return;
1142 }
1145
1146 cluster_list_tx_.ensure_3d(GPU_R32I, capture_info_buf_.irradiance_grid_size, texture_usage);
1147 cluster_list_tx_.clear(int4(-1));
1148 /* View is not important here. It is only for validation. */
1149 inst_.manager->submit(surfel_cluster_build_ps_, view_z_);
1150}
1151
1153{
1154 if (max_virtual_offset_ == 0.0f) {
1155 /* NOTE: Virtual offset texture should already have been cleared to 0. */
1156 return;
1157 }
1158
1159 inst_.manager->submit(irradiance_offset_ps_, view_z_);
1160
1161 /* Not needed after this point. */
1162 cluster_list_tx_.free();
1163}
1164
1166{
1167 using namespace blender::math;
1168
1169 float2 rand_uv = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_LENS_U);
1170 const float3 ray_direction = inst_.sampling.sample_sphere(rand_uv);
1171 const float3 up = ray_direction;
1172 const float3 forward = cross(up, normalize(orthogonal(up)));
1173 const float4x4 viewinv = from_orthonormal_axes<float4x4>(float3(0.0f), forward, up);
1174 const float4x4 viewmat = invert(viewinv);
1175
1176 /* Compute projection bounds. */
1177 float2 min, max;
1178 min = max = transform_point(viewmat, scene_bound_sphere_.xyz()).xy();
1179 min -= scene_bound_sphere_.w;
1180 max += scene_bound_sphere_.w;
1181
1182 /* This avoid light leaking by making sure that for one surface there will always be at least 1
1183 * surfel capture inside a ray list. Since the surface with the maximum distance (after
1184 * projection) between adjacent surfels is a slope that goes through 3 corners of a cube,
1185 * the distance the grid needs to cover is the diagonal of a cube face.
1186 *
1187 * The lower the number the more surfels it clumps together in the same surfel-list.
1188 * Biasing the grid_density like that will create many invalid link between coplanar surfels.
1189 * These are dealt with during the list sorting pass.
1190 *
1191 * This has a side effect of inflating shadows and emissive surfaces.
1192 *
1193 * We add an extra epsilon just in case. We really need this step to be leak free. */
1194 const float max_distance_between_neighbor_surfels_inv = M_SQRT1_2 - 1e-4;
1195 /* Surfel list per unit distance. */
1196 const float ray_grid_density = surfel_density_ * max_distance_between_neighbor_surfels_inv;
1197 /* Surfel list size in unit distance. */
1198 const float pixel_size = 1.0f / ray_grid_density;
1199 list_info_buf_.ray_grid_size = math::max(int2(1), int2(ray_grid_density * (max - min)));
1200
1201 /* Add a 2 pixels margin to have empty lists for irradiance grid samples to fall into (as they
1202 * are not considered by the scene bounds). The first pixel margin is because we are jittering
1203 * the grid position. */
1204 list_info_buf_.ray_grid_size += int2(4);
1205 min -= pixel_size * 2.0f;
1206 max += pixel_size * 2.0f;
1207
1208 /* Randomize grid center to avoid uneven inflating of corners in some directions. */
1209 const float2 aa_rand = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_FILTER_U);
1210 /* Offset in surfel list "pixel". */
1211 const float2 aa_offset = (aa_rand - 0.5f) * 0.499f;
1212 min += pixel_size * aa_offset;
1213
1214 list_info_buf_.list_max = list_info_buf_.ray_grid_size.x * list_info_buf_.ray_grid_size.y;
1215 list_info_buf_.push_update();
1216
1217 /* NOTE: Z values do not really matter since we are not doing any rasterization. */
1218 const float4x4 winmat = projection::orthographic<float>(min.x, max.x, min.y, max.y, 0, 1);
1219
1220 ray_view_.sync(viewmat, winmat);
1221
1222 dispatch_per_list_.x = divide_ceil_u(list_info_buf_.list_max, SURFEL_LIST_GROUP_SIZE);
1223
1224 list_start_buf_.resize(ceil_to_multiple_u(list_info_buf_.list_max, 4));
1225
1226 GPU_storagebuf_clear(list_start_buf_, -1);
1227 inst_.manager->submit(surfel_ray_build_ps_, ray_view_);
1228}
1229
1231{
1232 /* NOTE: Subtract 1 because after `sampling.step()`. */
1233 capture_info_buf_.sample_index = inst_.sampling.sample_index() - 1;
1234 capture_info_buf_.sample_count = inst_.sampling.sample_count();
1235 capture_info_buf_.push_update();
1236
1237 inst_.manager->submit(surfel_light_propagate_ps_, ray_view_);
1238
1239 std::swap(radiance_src_, radiance_dst_);
1240}
1241
1243{
1244 inst_.manager->submit(irradiance_capture_ps_, ray_view_);
1245}
1246
1247void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame)
1248{
1249 if (!ELEM(inst_.debug_mode,
1254 {
1255 return;
1256 }
1257
1259 capture_info_buf_.read();
1260 surfels_buf_.read();
1261
1262 cache_frame->surfels_len = capture_info_buf_.surfel_len;
1263 cache_frame->surfels = MEM_malloc_arrayN(cache_frame->surfels_len, sizeof(Surfel), __func__);
1264
1265 MutableSpan<Surfel> surfels_dst((Surfel *)cache_frame->surfels, cache_frame->surfels_len);
1266 Span<Surfel> surfels_src(surfels_buf_.data(), cache_frame->surfels_len);
1267 surfels_dst.copy_from(surfels_src);
1268}
1269
1270void IrradianceBake::read_virtual_offset(LightProbeGridCacheFrame *cache_frame)
1271{
1272 if (!ELEM(inst_.debug_mode, eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET)) {
1273 return;
1274 }
1275
1277
1278 cache_frame->baking.virtual_offset = (float(*)[4])virtual_offset_tx_.read<float4>(
1280}
1281
1283{
1285
1286 read_surfels(cache_frame);
1287 read_virtual_offset(cache_frame);
1288
1289 cache_frame->size[0] = irradiance_L0_tx_.width();
1290 cache_frame->size[1] = irradiance_L0_tx_.height();
1291 cache_frame->size[2] = irradiance_L0_tx_.depth();
1292
1294
1295 cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
1296 cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
1297 cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
1298 cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
1299 cache_frame->baking.validity = (float *)validity_tx_.read<float>(GPU_DATA_FLOAT);
1300
1301 return cache_frame;
1302}
1303
1305{
1307
1308 read_surfels(cache_frame);
1309 read_virtual_offset(cache_frame);
1310
1311 cache_frame->size[0] = irradiance_L0_tx_.width();
1312 cache_frame->size[1] = irradiance_L0_tx_.height();
1313 cache_frame->size[2] = irradiance_L0_tx_.depth();
1314
1316
1317 cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
1318 cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
1319 cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
1320 cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
1321 cache_frame->baking.validity = (float *)validity_tx_.read<float>(GPU_DATA_FLOAT);
1322
1323 int64_t sample_count = int64_t(irradiance_L0_tx_.width()) * irradiance_L0_tx_.height() *
1324 irradiance_L0_tx_.depth();
1325 size_t coefficient_texture_size = sizeof(*cache_frame->irradiance.L0) * sample_count;
1326 size_t validity_texture_size = sizeof(*cache_frame->connectivity.validity) * sample_count;
1327 cache_frame->irradiance.L0 = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1328 cache_frame->irradiance.L1_a = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1329 cache_frame->irradiance.L1_b = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1330 cache_frame->irradiance.L1_c = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1331 cache_frame->connectivity.validity = (uint8_t *)MEM_mallocN(validity_texture_size, __func__);
1332
1333 size_t visibility_texture_size = sizeof(*cache_frame->irradiance.L0) * sample_count;
1334 cache_frame->visibility.L0 = (float *)MEM_mallocN(visibility_texture_size, __func__);
1335 cache_frame->visibility.L1_a = (float *)MEM_mallocN(visibility_texture_size, __func__);
1336 cache_frame->visibility.L1_b = (float *)MEM_mallocN(visibility_texture_size, __func__);
1337 cache_frame->visibility.L1_c = (float *)MEM_mallocN(visibility_texture_size, __func__);
1338
1339 /* TODO(fclem): This could be done on GPU if that's faster. */
1340 for (auto i : IndexRange(sample_count)) {
1341 copy_v3_v3(cache_frame->irradiance.L0[i], cache_frame->baking.L0[i]);
1342 copy_v3_v3(cache_frame->irradiance.L1_a[i], cache_frame->baking.L1_a[i]);
1343 copy_v3_v3(cache_frame->irradiance.L1_b[i], cache_frame->baking.L1_b[i]);
1344 copy_v3_v3(cache_frame->irradiance.L1_c[i], cache_frame->baking.L1_c[i]);
1345
1346 cache_frame->visibility.L0[i] = cache_frame->baking.L0[i][3];
1347 cache_frame->visibility.L1_a[i] = cache_frame->baking.L1_a[i][3];
1348 cache_frame->visibility.L1_b[i] = cache_frame->baking.L1_b[i][3];
1349 cache_frame->visibility.L1_c[i] = cache_frame->baking.L1_c[i][3];
1351 cache_frame->baking.validity[i]);
1352 }
1353
1354 MEM_SAFE_FREE(cache_frame->baking.L0);
1355 MEM_SAFE_FREE(cache_frame->baking.L1_a);
1356 MEM_SAFE_FREE(cache_frame->baking.L1_b);
1357 MEM_SAFE_FREE(cache_frame->baking.L1_c);
1358 MEM_SAFE_FREE(cache_frame->baking.validity);
1359
1360 return cache_frame;
1361}
1362
1364
1365} // namespace blender::eevee
General operations for probes.
struct LightProbeGridCacheFrame * BKE_lightprobe_grid_cache_frame_create(void)
int64_t BKE_lightprobe_grid_cache_frame_sample_count(const struct LightProbeGridCacheFrame *cache)
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE uint ceil_to_multiple_u(uint a, uint b)
MINLINE uint divide_ceil_u(uint a, uint b)
#define M_SQRT1_2
MINLINE void copy_v3_v3(float r[3], const float a[3])
unsigned int uint
#define UNPACK3(a)
#define ELEM(...)
struct LightProbeGridCacheFrame LightProbeGridCacheFrame
@ LIGHTPROBE_GRID_CAPTURE_EMISSION
@ LIGHTPROBE_GRID_CAPTURE_WORLD
@ LIGHTPROBE_GRID_CAPTURE_INDIRECT
static AppView * view
static void View(GHOST_IWindow *window, bool stereo, int eye=0)
void GPU_mem_stats_get(int *r_totalmem, int *r_freemem)
bool GPU_mem_stats_supported()
size_t GPU_max_storage_buffer_size()
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_TRI_STRIP
@ GPU_PRIM_TRIS
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:374
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
@ GPU_BARRIER_BUFFER_UPDATE
Definition GPU_state.hh:56
@ GPU_BARRIER_SHADER_IMAGE_ACCESS
Definition GPU_state.hh:35
@ GPU_BARRIER_TEXTURE_UPDATE
Definition GPU_state.hh:39
void GPU_storagebuf_clear(GPUStorageBuf *ssbo, uint32_t clear_value)
@ GPU_DATA_UBYTE
@ GPU_DATA_FLOAT
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_SHADER_WRITE
@ GPU_TEXTURE_USAGE_HOST_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_TEXTURE_USAGE_ATOMIC
void GPU_texture_update_sub(GPUTexture *texture, eGPUDataFormat data_format, const void *pixels, int offset_x, int offset_y, int offset_z, int width, int height, int depth)
@ GPU_R32I
@ GPU_R8
@ GPU_RGB16F
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
#define MEM_SAFE_FREE(v)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Texture
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
int64_t size() const
void append(const T &value)
MutableSpan< T > as_mutable_span()
void extend(Span< T > array)
Span< T > as_span() const
SwapChain< ObjectBoundsBuf, 2 > bounds_buf
uint resource_handle_count() const
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
void bind_resources(U &resources)
Definition draw_pass.hh:426
void bind_image(const char *name, GPUTexture *image)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:616
void dispatch(int group_len)
Definition draw_pass.hh:874
void barrier(eGPUBarrier type)
Definition draw_pass.hh:943
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:462
void init(const Object &probe_object)
LightProbeGridCacheFrame * read_result_packed()
LightProbeGridCacheFrame * read_result_unpacked()
void surfels_create(const Object &probe_object)
void surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max, const float4x4 &probe_to_world)
Vector< IrradianceBrickPacked > bricks_alloc(int brick_len)
void bricks_free(Vector< IrradianceBrickPacked > &bricks)
void viewport_draw(View &view, GPUFrameBuffer *view_fb)
local_group_size(16, 16) .push_constant(Type b
additional_info("compositor_sum_float_shared") .push_constant(Type additional_info("compositor_sum_float_shared") .push_constant(Type GPU_RGBA32F
#define printf
bool DRW_state_draw_support()
void DRW_stats_group_start(const char *name)
void DRW_stats_group_end()
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_LESS_EQUAL
Definition draw_state.hh:38
@ DRW_STATE_CULL_BACK
Definition draw_state.hh:43
#define SURFEL_GROUP_SIZE
#define IRRADIANCE_BOUNDS_GROUP_SIZE
#define RBUFS_UTILITY_TEX_SLOT
#define CAPTURE_BUF_SLOT
#define SURFEL_LIST_GROUP_SIZE
#define IRRADIANCE_GRID_MAX
#define IRRADIANCE_GRID_BRICK_SIZE
#define IRRADIANCE_GRID_GROUP_SIZE
#define VOLUME_PROBE_FORMAT
#define SURFEL_BUF_SLOT
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
BLI_INLINE void grid_to_world(HairGrid *grid, float vecw[3], const float vec[3])
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
#define unit_float_to_uchar_clamp(val)
ccl_device_inline float cross(const float2 a, const float2 b)
#define G(x, y, z)
detail::Pass< command::DrawCommandBuf > PassSimple
static IrradianceBrickPacked irradiance_brick_pack(IrradianceBrick brick)
MatBase< T, 4, 4 > orthographic(T left, T right, T bottom, T top, T near_clip, T far_clip)
Create an orthographic projection matrix using OpenGL coordinate convention: Maps each axis range to ...
QuaternionBase< float > Quaternion
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
T reduce_max(const VecBase< T, Size > &a)
VecBase< T, Size > divide_ceil(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T reduce_min(const VecBase< T, Size > &a)
T min(const T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
T midpoint(const T &a, const T &b)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
T reduce_mul(const VecBase< T, Size > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
VecBase< T, 3 > orthogonal(const VecBase< T, 3 > &v)
T max(const T &a, const T &b)
T abs(const T &a)
MatT from_location(const typename MatT::loc_type &location)
void to_loc_rot_scale(const MatBase< T, 3, 3 > &mat, VecBase< T, 2 > &r_location, AngleRadianBase< T > &r_rotation, VecBase< T, 2 > &r_scale)
CartesianBasis from_orthonormal_axes(const AxisSigned forward, const AxisSigned up)
MatT from_rotation(const RotationT &rotation)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
VecBase< uint32_t, 2 > uint2
VecBase< int32_t, 4 > int4
bool assign_if_different(T &old_value, T new_value)
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 4 > float3x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
float distance(float a, float b)
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
__int64 int64_t
Definition stdint.h:89
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
char name[66]
Definition DNA_ID.h:425
LightProbeVisibilityData visibility
LightProbeConnectivityData connectivity
LightProbeIrradianceData irradiance
VecBase< T, 2 > xy() const
CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN ccl_device_inline float3 transform_point(ccl_private const Transform *t, const float3 a)
Definition transform.h:63
float max
int xy[2]
Definition wm_draw.cc:170