Blender V4.5
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
12#include "GPU_debug.hh"
13
14#include "eevee_instance.hh"
15
17
18#include <cstdio>
19
20namespace blender::eevee {
21
22/* -------------------------------------------------------------------- */
25
27{
28 display_grids_enabled_ = inst_.draw_overlays;
29
30 /* This might become an option in the future. */
31 bool use_l2_band = false;
32 int sh_coef_len = use_l2_band ? 9 : 4;
34 int texel_byte_size = 8; /* Assumes GPU_RGBA16F. */
35 uint atlas_col_count = 0;
36 uint atlas_row_count = 0;
37
38 if (assign_if_different(irradiance_pool_size_,
39 uint(inst_.scene->eevee.gi_irradiance_pool_size)) ||
40 !irradiance_atlas_tx_.is_valid())
41 {
42 irradiance_atlas_tx_.free();
43 /* Find highest pool size within device limits. */
44 for (uint irradiance_pool_size = irradiance_pool_size_;
45 irradiance_pool_size >= 16 && !irradiance_atlas_tx_.is_valid();
46 irradiance_pool_size >>= 1)
47 {
48 int atlas_byte_size = 1024 * 1024 * irradiance_pool_size;
49 /* Reshape texture to improve grid occupancy within device limits. */
50 constexpr uint atlas_col_count_min = 16;
51 constexpr uint atlas_col_count_max = 16384;
52 for (uint atlas_col_count_try = atlas_col_count_min;
53 atlas_col_count_try <= atlas_col_count_max && !irradiance_atlas_tx_.is_valid();
54 atlas_col_count_try <<= 1)
55 {
56 int3 atlas_extent(IRRADIANCE_GRID_BRICK_SIZE);
57 atlas_extent.z *= sh_coef_len;
58 /* Add space for validity bits. */
59 atlas_extent.z += IRRADIANCE_GRID_BRICK_SIZE / 4;
60 atlas_extent.x *= atlas_col_count_try;
61
62 /* Determine the row count depending on the scene settings. */
63 int row_byte_size = math::reduce_mul(atlas_extent) * texel_byte_size;
64 atlas_row_count = divide_ceil_u(atlas_byte_size, row_byte_size);
65 atlas_extent.y *= atlas_row_count;
66
70 irradiance_atlas_tx_.ensure_3d(VOLUME_PROBE_FORMAT, atlas_extent, usage);
71 if (irradiance_atlas_tx_.is_valid()) {
72 do_full_update_ = true;
73 irradiance_pool_size_alloc_ = irradiance_pool_size;
74 atlas_col_count = atlas_col_count_try;
75 }
76 }
77 }
78 }
79 if (irradiance_pool_size_alloc_ != irradiance_pool_size_) {
80 inst_.info_append_i18n(
81 "Warning: Could not allocate light probes volume pool of {} MB, using {} MB instead.",
82 irradiance_pool_size_,
83 irradiance_pool_size_alloc_);
84 }
85
86 if (do_full_update_) {
87 do_full_update_ = false;
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 if (_a.y != _b.y) {
246 return _a.y < _b.y;
247 }
248 if (_a.z != _b.z) {
249 return _a.z < _b.z;
250 }
251 /* Fallback to memory address, since there's no good alternative. */
252 return a < b;
253 });
254
255 /* Insert grids in UBO in sorted order. */
256 int grids_len = 0;
257 for (VolumeProbe *grid : grid_loaded) {
258 grid->grid_index = grids_len;
259 grids_infos_buf_[grids_len++] = *grid;
260 }
261
262 /* Insert world grid last. */
263 world_grid_index = grids_len++;
264
265 VolumeProbeData grid;
267 grid.grid_size_padded = int3(1);
268 grid.brick_offset = bricks_infos_buf_.size();
269 grid.normal_bias = 0.0f;
270 grid.view_bias = 0.0f;
271 grid.facing_bias = 0.0f;
272 grids_infos_buf_[world_grid_index] = grid;
273
274 bricks_infos_buf_.append(world_brick_index_);
275
276 if (grids_len < IRRADIANCE_GRID_MAX) {
277 /* Tag last grid as invalid to stop the iteration. */
278 grids_infos_buf_[grids_len].grid_size_padded = int3(-1);
279 }
280
281 bricks_infos_buf_.push_update();
282 grids_infos_buf_.push_update();
283 }
284
285 /* Upload data for world. */
286 if (do_update_world_) {
287 grid_upload_ps_.init();
288 grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_WORLD));
289 grid_upload_ps_.bind_resources(inst_.uniform_data);
290 grid_upload_ps_.bind_ssbo("harmonic_buf", &inst_.sphere_probes.spherical_harmonics_buf());
291 grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
292 grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
293 grid_upload_ps_.push_constant("grid_index", world_grid_index);
294 grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
295 /* Sync with extraction. */
296 grid_upload_ps_.barrier(GPU_BARRIER_SHADER_STORAGE);
297 /* Only upload one brick. */
298 grid_upload_ps_.dispatch(int3(1));
299 /* Sync with next load. */
300 grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
301
302 inst_.manager->submit(grid_upload_ps_);
303 }
304
305 /* Upload data for each grid that need to be inserted in the atlas.
306 * Upload by order of dependency. */
307 /* Start at world index to not load any other grid (+1 because we decrement at loop start). */
308 int grid_start_index = grid_loaded.size() + 1;
309 for (auto it = grid_loaded.rbegin(); it != grid_loaded.rend(); ++it) {
310 grid_start_index--;
311
312 VolumeProbe *grid = *it;
313 if (!grid->do_update) {
314 continue;
315 }
316
317 grid->do_update = false;
318
319 LightProbeGridCacheFrame *cache = grid->cache->grid_static_cache;
320
321 /* Staging textures are recreated for each light grid to avoid increasing VRAM usage. */
322 draw::Texture irradiance_a_tx = {"irradiance_a_tx"};
323 draw::Texture irradiance_b_tx = {"irradiance_b_tx"};
324 draw::Texture irradiance_c_tx = {"irradiance_c_tx"};
325 draw::Texture irradiance_d_tx = {"irradiance_d_tx"};
326 draw::Texture validity_tx = {"validity_tx"};
327
329 int3 grid_size = int3(cache->size);
330 if (cache->baking.L0) {
331 irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L0);
332 irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_a);
333 irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_b);
334 irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_c);
335 validity_tx.ensure_3d(GPU_R16F, grid_size, usage, cache->baking.validity);
336 if (cache->baking.validity == nullptr) {
337 /* Avoid displaying garbage data. */
338 validity_tx.clear(float4(0.0));
339 }
340 }
341 else if (cache->irradiance.L0) {
342 irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L0);
343 irradiance_b_tx.ensure_3d(
344 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_a);
345 irradiance_c_tx.ensure_3d(
346 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_b);
347 irradiance_d_tx.ensure_3d(
348 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_c);
349 validity_tx.ensure_3d(GPU_R8, grid_size, usage);
350 if (cache->connectivity.validity) {
351 /* TODO(fclem): Make texture creation API work with different data types. */
352 GPU_texture_update_sub(validity_tx,
354 cache->connectivity.validity,
355 0,
356 0,
357 0,
358 UNPACK3(grid_size));
359 }
360 else {
361 /* Avoid displaying garbage data. */
362 validity_tx.clear(float4(0.0));
363 }
364 }
365 else {
366 continue;
367 }
368
369 if (irradiance_a_tx.is_valid() == false) {
370 inst_.info_append_i18n("Error: Could not allocate irradiance staging texture");
371 /* Avoid undefined behavior with uninitialized values. Still load a clear texture. */
372 const float4 zero(0.0f);
373 irradiance_a_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
374 irradiance_b_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
375 irradiance_c_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
376 irradiance_d_tx.ensure_3d(GPU_RGB16F, int3(1), usage, zero);
377 validity_tx.ensure_3d(GPU_R16F, int3(1), usage, zero);
378 }
379
380 bool visibility_available = cache->visibility.L0 != nullptr;
381 bool is_baking = cache->irradiance.L0 == nullptr;
382
383 draw::Texture visibility_a_tx = {"visibility_a_tx"};
384 draw::Texture visibility_b_tx = {"visibility_b_tx"};
385 draw::Texture visibility_c_tx = {"visibility_c_tx"};
386 draw::Texture visibility_d_tx = {"visibility_d_tx"};
387 if (visibility_available) {
388 visibility_a_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L0);
389 visibility_b_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L1_a);
390 visibility_c_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L1_b);
391 visibility_d_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->visibility.L1_c);
392
393 GPU_texture_swizzle_set(visibility_a_tx, "111r");
394 GPU_texture_swizzle_set(visibility_b_tx, "111r");
395 GPU_texture_swizzle_set(visibility_c_tx, "111r");
396 GPU_texture_swizzle_set(visibility_d_tx, "111r");
397 }
398 else if (!is_baking) {
399 /* Missing visibility. Load default visibility L0 = 1, L1 = (0, 0, 0). */
400 GPU_texture_swizzle_set(irradiance_a_tx, "rgb1");
401 GPU_texture_swizzle_set(irradiance_b_tx, "rgb0");
402 GPU_texture_swizzle_set(irradiance_c_tx, "rgb0");
403 GPU_texture_swizzle_set(irradiance_d_tx, "rgb0");
404 }
405
406 grid_upload_ps_.init();
407 grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD));
408
409 grid_upload_ps_.bind_resources(inst_.uniform_data);
410 grid_upload_ps_.push_constant("validity_threshold", grid->validity_threshold);
411 grid_upload_ps_.push_constant("dilation_threshold", grid->dilation_threshold);
412 grid_upload_ps_.push_constant("dilation_radius", grid->dilation_radius);
413 grid_upload_ps_.push_constant("grid_index", grid->grid_index);
414 grid_upload_ps_.push_constant("grid_start_index", grid_start_index);
415 grid_upload_ps_.push_constant("grid_local_to_world", grid->object_to_world);
416 grid_upload_ps_.push_constant("grid_intensity_factor", grid->intensity);
417 grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
418 grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
419 grid_upload_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
420 grid_upload_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
421 grid_upload_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
422 grid_upload_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
423 grid_upload_ps_.bind_texture("validity_tx", &validity_tx);
424 grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
425 /* NOTE: We are read and writing the same texture that we are sampling from. If that causes an
426 * issue, we should revert to manual trilinear interpolation. */
427 grid_upload_ps_.bind_texture("irradiance_atlas_tx", &irradiance_atlas_tx_);
428 /* If visibility is invalid, either it is still baking and visibility is stored with
429 * irradiance, or it is missing and we sample a completely uniform visibility. */
430 bool use_vis = visibility_available;
431 grid_upload_ps_.bind_texture("visibility_a_tx", use_vis ? &visibility_a_tx : &irradiance_a_tx);
432 grid_upload_ps_.bind_texture("visibility_b_tx", use_vis ? &visibility_b_tx : &irradiance_b_tx);
433 grid_upload_ps_.bind_texture("visibility_c_tx", use_vis ? &visibility_c_tx : &irradiance_c_tx);
434 grid_upload_ps_.bind_texture("visibility_d_tx", use_vis ? &visibility_d_tx : &irradiance_d_tx);
435
436 /* Runtime grid is padded for blending with surrounding probes. */
437 int3 grid_size_with_padding = grid_size + 2;
438 /* Note that we take into account the padding border of each brick. */
439 int3 grid_size_in_bricks = math::divide_ceil(grid_size_with_padding,
441 grid_upload_ps_.dispatch(grid_size_in_bricks);
442 /* Sync with next load. */
443 grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
444
445 inst_.manager->submit(grid_upload_ps_);
446
447 irradiance_a_tx.free();
448 irradiance_b_tx.free();
449 irradiance_c_tx.free();
450 irradiance_d_tx.free();
451 }
452
453 do_update_world_ = false;
454}
455
456void VolumeProbeModule::viewport_draw(View &view, GPUFrameBuffer *view_fb)
457{
458 if (!inst_.is_baking()) {
459 debug_pass_draw(view, view_fb);
460 display_pass_draw(view, view_fb);
461 }
462}
463
464void VolumeProbeModule::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
465{
466 switch (inst_.debug_mode) {
468 inst_.info_append("Debug Mode: Surfels Normal");
469 break;
471 inst_.info_append("Debug Mode: Surfels Cluster");
472 break;
474 inst_.info_append("Debug Mode: Surfels Irradiance");
475 break;
477 inst_.info_append("Debug Mode: Surfels Visibility");
478 break;
480 inst_.info_append("Debug Mode: Irradiance Validity");
481 break;
483 inst_.info_append("Debug Mode: Virtual Offset");
484 break;
485 default:
486 /* Nothing to display. */
487 return;
488 }
489
490 for (const VolumeProbe &grid : inst_.light_probes.volume_map_.values()) {
491 if (grid.cache == nullptr) {
492 continue;
493 }
494
495 LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
496
497 if (cache == nullptr) {
498 continue;
499 }
500
501 switch (inst_.debug_mode) {
502 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL:
503 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER:
504 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_VISIBILITY:
505 case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: {
506 if (cache->surfels == nullptr || cache->surfels_len == 0) {
507 continue;
508 }
509 float max_axis_len = math::reduce_max(math::to_scale(grid.object_to_world));
510 debug_ps_.init();
511 debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
512 DRW_STATE_CLIP_CONTROL_UNIT_RANGE | inst_.film.depth.test_state);
513 debug_ps_.framebuffer_set(&view_fb);
514 debug_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS));
515 debug_ps_.push_constant("debug_surfel_radius", 0.5f * max_axis_len / grid.surfel_density);
516 debug_ps_.push_constant("debug_mode", int(inst_.debug_mode));
517
518 debug_surfels_buf_.resize(cache->surfels_len);
519 /* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an
520 * input data. */
521 Span<Surfel> grid_surfels(static_cast<Surfel *>(cache->surfels), cache->surfels_len);
522 MutableSpan<Surfel>(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels);
523 debug_surfels_buf_.push_update();
524
525 debug_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_);
526 debug_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4);
527
528 inst_.manager->submit(debug_ps_, view);
529 break;
530 }
531
532 case eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY:
533 case eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET: {
534 int3 grid_size = int3(cache->size);
535 debug_ps_.init();
536 debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
537 DRW_STATE_CLIP_CONTROL_UNIT_RANGE | inst_.film.depth.test_state);
538 debug_ps_.framebuffer_set(&view_fb);
539 debug_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_IRRADIANCE_GRID));
540 debug_ps_.push_constant("debug_mode", int(inst_.debug_mode));
541 debug_ps_.push_constant("grid_mat", grid.object_to_world);
542
544 Texture debug_data_tx = {"debug_data_tx"};
545
546 if (inst_.debug_mode == eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY) {
547 const float *data;
548 if (cache->baking.validity) {
549 data = cache->baking.validity;
550 debug_data_tx.ensure_3d(GPU_R16F, grid_size, usage, data);
551 }
552 else if (cache->connectivity.validity) {
553 debug_data_tx.ensure_3d(GPU_R8, grid_size, usage);
554 /* TODO(fclem): Make texture creation API work with different data types. */
555 GPU_texture_update_sub(debug_data_tx,
557 cache->connectivity.validity,
558 0,
559 0,
560 0,
561 UNPACK3(grid_size));
562 }
563 else {
564 continue;
565 }
566 debug_ps_.push_constant("debug_value", grid.validity_threshold);
567 debug_ps_.bind_texture("debug_data_tx", debug_data_tx);
568 debug_ps_.draw_procedural(GPU_PRIM_POINTS, 1, grid_size.x * grid_size.y * grid_size.z);
569 }
570 else {
571 if (cache->baking.virtual_offset) {
572 const float *data = (const float *)cache->baking.virtual_offset;
573 debug_data_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, data);
574 }
575 else {
576 continue;
577 }
578 debug_ps_.bind_texture("debug_data_tx", debug_data_tx);
579 debug_ps_.draw_procedural(
580 GPU_PRIM_LINES, 1, grid_size.x * grid_size.y * grid_size.z * 2);
581 }
582
583 inst_.manager->submit(debug_ps_, view);
584 break;
585 }
586
587 default:
588 break;
589 }
590 }
591}
592
593void VolumeProbeModule::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
594{
595 if (!display_grids_enabled_) {
596 return;
597 }
598
599 for (const VolumeProbe &grid : inst_.light_probes.volume_map_.values()) {
600 if (!grid.viewport_display || grid.viewport_display_size == 0.0f || !grid.cache ||
601 !grid.cache->grid_static_cache)
602 {
603 continue;
604 }
605
606 LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
607
608 /* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */
609 draw::Texture irradiance_a_tx = {"irradiance_a_tx"};
610 draw::Texture irradiance_b_tx = {"irradiance_b_tx"};
611 draw::Texture irradiance_c_tx = {"irradiance_c_tx"};
612 draw::Texture irradiance_d_tx = {"irradiance_d_tx"};
613 draw::Texture validity_tx = {"validity_tx"};
614
616 int3 grid_size = int3(cache->size);
617 if (cache->baking.L0) {
618 irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L0);
619 irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_a);
620 irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_b);
621 irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (const float *)cache->baking.L1_c);
622 validity_tx.ensure_3d(GPU_R16F, grid_size, usage, (const float *)cache->baking.validity);
623 if (cache->baking.validity == nullptr) {
624 /* Avoid displaying garbage data. */
625 validity_tx.clear(float4(0.0));
626 }
627 }
628 else if (cache->irradiance.L0) {
629 irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L0);
630 irradiance_b_tx.ensure_3d(
631 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_a);
632 irradiance_c_tx.ensure_3d(
633 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_b);
634 irradiance_d_tx.ensure_3d(
635 GPU_RGB16F, grid_size, usage, (const float *)cache->irradiance.L1_c);
636 validity_tx.ensure_3d(GPU_R8, grid_size, usage);
637 if (cache->connectivity.validity) {
638 /* TODO(fclem): Make texture creation API work with different data types. */
639 GPU_texture_update_sub(validity_tx,
641 cache->connectivity.validity,
642 0,
643 0,
644 0,
645 UNPACK3(grid_size));
646 }
647 else {
648 /* Avoid displaying garbage data. */
649 validity_tx.clear(float4(0.0));
650 }
651 }
652 else {
653 continue;
654 }
655
656 display_grids_ps_.init();
657 display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
658 DRW_STATE_CLIP_CONTROL_UNIT_RANGE | inst_.film.depth.test_state |
660 display_grids_ps_.framebuffer_set(&view_fb);
661 display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_VOLUME));
662
663 display_grids_ps_.push_constant("sphere_radius", grid.viewport_display_size);
664 display_grids_ps_.push_constant("grid_resolution", grid_size);
665 display_grids_ps_.push_constant("grid_to_world", grid.object_to_world);
666 display_grids_ps_.push_constant("world_to_grid", grid.world_to_object);
667 /* TODO(fclem): Make it an option when display options are moved to probe DNA. */
668 display_grids_ps_.push_constant("display_validity", false);
669
670 display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
671 display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
672 display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
673 display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
674 display_grids_ps_.bind_texture("validity_tx", &validity_tx);
675
676 int sample_count = int(BKE_lightprobe_grid_cache_frame_sample_count(cache));
677 int triangle_count = sample_count * 2;
678 display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, triangle_count * 3);
679
680 inst_.manager->submit(display_grids_ps_, view);
681
682 irradiance_a_tx.free();
683 irradiance_b_tx.free();
684 irradiance_c_tx.free();
685 irradiance_d_tx.free();
686 }
687}
688
690
691/* -------------------------------------------------------------------- */
694
695void IrradianceBake::init(const Object &probe_object)
696{
697 float max_axis_len = math::reduce_max(math::to_scale(probe_object.object_to_world()));
698
699 const ::LightProbe &lightprobe = DRW_object_get_data_for_drawing<::LightProbe>(probe_object);
700 surfel_density_ = lightprobe.grid_surfel_density / max_axis_len;
701 min_distance_to_surface_ = lightprobe.grid_surface_bias;
702 max_virtual_offset_ = lightprobe.grid_escape_bias;
703 clip_distance_ = lightprobe.clipend;
704 capture_world_ = (lightprobe.grid_flag & LIGHTPROBE_GRID_CAPTURE_WORLD);
705 capture_indirect_ = (lightprobe.grid_flag & LIGHTPROBE_GRID_CAPTURE_INDIRECT);
706 capture_emission_ = (lightprobe.grid_flag & LIGHTPROBE_GRID_CAPTURE_EMISSION);
707
708 /* Initialize views data, since they're used by other modules. */
710}
711
713{
714 {
715 PassSimple &pass = surfel_light_eval_ps_;
716 pass.init();
717 /* Apply lights contribution to scene surfel representation. */
718 pass.shader_set(inst_.shaders.static_shader_get(SURFEL_LIGHT));
719 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
720 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
721 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
722 pass.bind_resources(inst_.uniform_data);
723 pass.bind_resources(inst_.lights);
724 pass.bind_resources(inst_.shadows);
725 /* Sync with the surfel creation stage. */
729 pass.dispatch(&dispatch_per_surfel_);
730 }
731 {
732 PassSimple &pass = surfel_cluster_build_ps_;
733 pass.init();
734 pass.shader_set(inst_.shaders.static_shader_get(SURFEL_CLUSTER_BUILD));
735 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
736 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
737 pass.bind_image("cluster_list_img", &cluster_list_tx_);
739 pass.dispatch(&dispatch_per_surfel_);
741 }
742 {
743 PassSimple &pass = surfel_ray_build_ps_;
744 pass.init();
745 {
746 PassSimple::Sub &sub = pass.sub("ListBuild");
747 sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_BUILD));
748 sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
749 sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
750 sub.bind_ssbo("list_start_buf", &list_start_buf_);
751 sub.bind_ssbo("list_info_buf", &list_info_buf_);
753 sub.dispatch(&dispatch_per_surfel_);
754 }
755 {
756 PassSimple::Sub &sub = pass.sub("ListSort");
757 sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT));
758 sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
759 sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
760 sub.bind_ssbo("list_start_buf", &list_start_buf_);
761 sub.bind_ssbo("list_info_buf", &list_info_buf_);
763 sub.dispatch(&dispatch_per_list_);
764 }
765 }
766 {
767 PassSimple &pass = surfel_light_propagate_ps_;
768 pass.init();
769 {
770 PassSimple::Sub &sub = pass.sub("RayEval");
771 sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY));
772 sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
773 sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
774 sub.bind_resources(inst_.sphere_probes);
775 sub.push_constant("radiance_src", &radiance_src_);
776 sub.push_constant("radiance_dst", &radiance_dst_);
778 sub.dispatch(&dispatch_per_surfel_);
779 }
780 }
781 {
782 PassSimple &pass = irradiance_capture_ps_;
783 pass.init();
784 pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_RAY));
785 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
786 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
787 pass.bind_resources(inst_.sphere_probes);
788 pass.bind_ssbo("list_start_buf", &list_start_buf_);
789 pass.bind_ssbo("list_info_buf", &list_info_buf_);
790 pass.push_constant("radiance_src", &radiance_src_);
791 pass.bind_image("irradiance_L0_img", &irradiance_L0_tx_);
792 pass.bind_image("irradiance_L1_a_img", &irradiance_L1_a_tx_);
793 pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_);
794 pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_);
795 pass.bind_image("validity_img", &validity_tx_);
796 pass.bind_image("virtual_offset_img", &virtual_offset_tx_);
798 pass.dispatch(&dispatch_per_grid_sample_);
799 }
800 {
801 PassSimple &pass = irradiance_offset_ps_;
802 pass.init();
803 pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_OFFSET));
804 pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
805 pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
806 pass.bind_ssbo("list_start_buf", &list_start_buf_);
807 pass.bind_ssbo("list_info_buf", &list_info_buf_);
808 pass.bind_image("cluster_list_img", &cluster_list_tx_);
809 pass.bind_image("virtual_offset_img", &virtual_offset_tx_);
811 pass.dispatch(&dispatch_per_grid_sample_);
812 }
813}
814
816 const float3 &scene_max,
817 const float4x4 &probe_to_world)
818{
819 using namespace blender::math;
820
821 float3 location, scale;
822 Quaternion rotation;
823 to_loc_rot_scale(probe_to_world, location, rotation, scale);
824 /* Remove scale from view matrix. */
825 float4x4 viewinv = from_loc_rot_scale<float4x4>(location, rotation, float3(1.0f));
826 float4x4 viewmat = invert(viewinv);
827
828 /* Compute the intersection between the grid and the scene extents. */
829 float3 extent_min = float3(FLT_MAX);
830 float3 extent_max = float3(-FLT_MAX);
831 for (int x : {0, 1}) {
832 for (int y : {0, 1}) {
833 for (int z : {0, 1}) {
834 float3 ws_corner = scene_min + ((scene_max - scene_min) * float3(x, y, z));
835 float3 ls_corner = transform_point(viewmat, ws_corner);
836 extent_min = min(extent_min, ls_corner);
837 extent_max = max(extent_max, ls_corner);
838 }
839 }
840 }
841 /* Clip distance is added to every axis in both directions, not just Z. */
842 float3 target_extent = scale + clip_distance_;
843 extent_min = max(extent_min, -target_extent);
844 extent_max = min(extent_max, target_extent);
845
846 grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * (extent_max - extent_min)));
847 grid_pixel_extent_ = min(grid_pixel_extent_, int3(16384));
848
849 float3 ls_midpoint = midpoint(extent_min, extent_max);
850 scene_bound_sphere_ = float4(transform_point(viewinv, ls_midpoint),
851 distance(extent_min, extent_max) / 2.0f);
852
853 /* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to
854 * make any difference. The bottleneck is still the light propagation loop. */
855 auto sync_view = [&](View &view, CartesianBasis basis) {
856 float4x4 capture_viewinv = viewinv * from_rotation<float4x4>(basis);
857
858 float3 capture_extent_min = transform_point(invert(basis), extent_min);
859 float3 capture_extent_max = transform_point(invert(basis), extent_max);
860
861 float4x4 capture_winmat = projection::orthographic(capture_extent_min.x,
862 capture_extent_max.x,
863 capture_extent_min.y,
864 capture_extent_max.y,
865 -capture_extent_min.z,
866 -capture_extent_max.z);
867
868 view.visibility_test(false);
869 view.sync(invert(capture_viewinv), capture_winmat);
870 };
871
872 sync_view(view_x_, basis_x_);
873 sync_view(view_y_, basis_y_);
874 sync_view(view_z_, basis_z_);
875}
876
877void IrradianceBake::surfels_create(const Object &probe_object)
878{
884 using namespace blender::math;
885
886 const ::LightProbe &lightprobe = DRW_object_get_data_for_drawing<::LightProbe>(probe_object);
887
888 int3 grid_resolution = int3(&lightprobe.grid_resolution_x);
889 float4x4 grid_local_to_world = invert(probe_object.world_to_object());
890 float3 grid_scale = math::to_scale(probe_object.object_to_world());
891
892 /* TODO(fclem): Options. */
893 capture_info_buf_.capture_world_direct = capture_world_;
894 capture_info_buf_.capture_world_indirect = capture_world_ && capture_indirect_;
895 capture_info_buf_.capture_visibility_direct = !capture_world_;
896 capture_info_buf_.capture_visibility_indirect = !(capture_world_ && capture_indirect_);
897 capture_info_buf_.capture_indirect = capture_indirect_;
898 capture_info_buf_.capture_emission = capture_emission_;
899
900 LightProbeModule &light_probes = inst_.light_probes;
901 SphereProbeData &world_data = *static_cast<SphereProbeData *>(&light_probes.world_sphere_);
902 capture_info_buf_.world_atlas_coord = world_data.atlas_coord;
903
904 dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE));
905 capture_info_buf_.irradiance_grid_size = grid_resolution;
906 capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world;
907 capture_info_buf_.irradiance_grid_world_to_local = probe_object.world_to_object();
908 capture_info_buf_.irradiance_grid_world_to_local_rotation = float4x4(
909 invert(normalize(float3x3(grid_local_to_world))));
910
911 capture_info_buf_.min_distance_to_surface = min_distance_to_surface_;
912 capture_info_buf_.max_virtual_offset = max_virtual_offset_;
913 capture_info_buf_.surfel_radius = 0.5f / surfel_density_;
914 /* Make virtual offset distances scale relative. */
915 float min_distance_between_grid_samples = math::reduce_min(grid_scale / float3(grid_resolution));
916 capture_info_buf_.min_distance_to_surface *= min_distance_between_grid_samples;
917 capture_info_buf_.max_virtual_offset *= min_distance_between_grid_samples;
918 capture_info_buf_.clamp_direct = (lightprobe.grid_clamp_direct > 0.0) ?
919 lightprobe.grid_clamp_direct :
920 1e20f;
921 capture_info_buf_.clamp_indirect = (lightprobe.grid_clamp_indirect > 0.0) ?
922 lightprobe.grid_clamp_indirect :
923 1e20f;
924
927
928 /* 32bit float is needed here otherwise we loose too much energy from rounding error during the
929 * accumulation when the sample count is above 500. */
930 irradiance_L0_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
931 irradiance_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
932 irradiance_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
933 irradiance_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
934 validity_tx_.ensure_3d(GPU_R32F, grid_resolution, texture_usage);
935 virtual_offset_tx_.ensure_3d(GPU_RGBA16F, grid_resolution, texture_usage);
936
937 if (!irradiance_L0_tx_.is_valid() || !irradiance_L1_a_tx_.is_valid() ||
938 !irradiance_L1_b_tx_.is_valid() || !irradiance_L1_c_tx_.is_valid() ||
939 !validity_tx_.is_valid() || !virtual_offset_tx_.is_valid())
940 {
941 inst_.info_append_i18n("Error: Not enough memory to bake {}.", probe_object.id.name);
942 do_break_ = true;
943 return;
944 }
945
946 irradiance_L0_tx_.clear(float4(0.0f));
947 irradiance_L1_a_tx_.clear(float4(0.0f));
948 irradiance_L1_b_tx_.clear(float4(0.0f));
949 irradiance_L1_c_tx_.clear(float4(0.0f));
950 validity_tx_.clear(float4(0.0f));
951 virtual_offset_tx_.clear(float4(0.0f));
952
953 GPU_debug_group_begin("IrradianceBake.SceneBounds");
954
955 {
956 draw::Manager &manager = *inst_.manager;
957 PassSimple &pass = irradiance_bounds_ps_;
958 pass.init();
959 pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_BOUNDS));
960 pass.bind_ssbo("capture_info_buf", &capture_info_buf_);
961 pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
962 pass.push_constant("resource_len", int(manager.resource_handle_count()));
963 pass.dispatch(
965 }
966
967 /* Raster the scene to query the number of surfel needed. */
968 capture_info_buf_.do_surfel_count = false;
969 capture_info_buf_.do_surfel_output = false;
970
971 const int neg_flt_max = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */
972 const int pos_flt_max = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */
973 capture_info_buf_.scene_bound_x_min = pos_flt_max;
974 capture_info_buf_.scene_bound_y_min = pos_flt_max;
975 capture_info_buf_.scene_bound_z_min = pos_flt_max;
976 capture_info_buf_.scene_bound_x_max = neg_flt_max;
977 capture_info_buf_.scene_bound_y_max = neg_flt_max;
978 capture_info_buf_.scene_bound_z_max = neg_flt_max;
979
980 capture_info_buf_.push_update();
981
982 inst_.manager->submit(irradiance_bounds_ps_);
983
985 capture_info_buf_.read();
986
987 if (capture_info_buf_.scene_bound_x_min == pos_flt_max) {
988 /* No valid object has been found. */
989 do_break_ = true;
990 return;
991 }
992
993 auto ordered_int_bits_to_float = [](int32_t int_value) -> float {
994 int32_t float_bits = (int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value;
995 return *reinterpret_cast<float *>(&float_bits);
996 };
997
998 float3 scene_min = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_min),
999 ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_min),
1000 ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_min));
1001 float3 scene_max = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_max),
1002 ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_max),
1003 ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_max));
1004 /* To avoid loosing any surface to the clipping planes, add some padding. */
1005 float epsilon = 1.0f / surfel_density_;
1006 scene_min -= epsilon;
1007 scene_max += epsilon;
1008 surfel_raster_views_sync(scene_min, scene_max, probe_object.object_to_world());
1009
1011
1012 /* WORKAROUND: Sync camera with correct bounds for light culling. */
1013 inst_.camera.sync();
1014 /* WORKAROUND: Sync shadows tile-maps count again with new camera bounds. Fixes issues with sun
1015 * lights. */
1016 inst_.shadows.end_sync();
1017 inst_.lights.end_sync();
1018
1019 GPU_debug_group_begin("IrradianceBake.SurfelsCount");
1020
1021 /* Raster the scene to query the number of surfel needed. */
1022 capture_info_buf_.do_surfel_count = true;
1023 capture_info_buf_.do_surfel_output = false;
1024 capture_info_buf_.surfel_len = 0u;
1025 capture_info_buf_.push_update();
1026
1027 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_x_), grid_pixel_extent_).xy()));
1028 inst_.pipelines.capture.render(view_x_);
1029 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_y_), grid_pixel_extent_).xy()));
1030 inst_.pipelines.capture.render(view_y_);
1031 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_z_), grid_pixel_extent_).xy()));
1032 inst_.pipelines.capture.render(view_z_);
1033
1035
1036 /* Allocate surfel pool. */
1038 capture_info_buf_.read();
1039 if (capture_info_buf_.surfel_len == 0) {
1040 /* No surfel to allocate. */
1041 return;
1042 }
1043
1044 if (capture_info_buf_.surfel_len > surfels_buf_.size()) {
1045 printf("IrradianceBake: Allocating %u surfels.\n", capture_info_buf_.surfel_len);
1046
1047 size_t max_size = GPU_max_storage_buffer_size();
1049 int total_mem_kb, free_mem_kb;
1050 GPU_mem_stats_get(&total_mem_kb, &free_mem_kb);
1051 /* Leave at least 128MByte for OS and stuffs.
1052 * Try to avoid crashes because of OUT_OF_MEMORY errors. */
1053 size_t max_alloc = (size_t(total_mem_kb) - 128 * 1024) * 1024;
1054 /* Cap to 95% of available memory. */
1055 size_t max_free = size_t((size_t(free_mem_kb) * 1024) * 0.95f);
1056
1057 max_size = min(max_size, min(max_alloc, max_free));
1058 }
1059
1060 size_t required_mem = sizeof(Surfel) * (capture_info_buf_.surfel_len - surfels_buf_.size());
1061 if (required_mem > max_size) {
1062 const bool is_ssbo_bound = (max_size == GPU_max_storage_buffer_size());
1063 const uint req_mb = required_mem / (1024 * 1024);
1064 const uint max_mb = max_size / (1024 * 1024);
1065
1066 if (is_ssbo_bound) {
1067 inst_.info_append_i18n(
1068 "Cannot allocate enough video memory to bake \"{}\" ({} / {} MBytes).\n"
1069 "Try reducing surfel resolution or capture distance to lower the size of the "
1070 "allocation.",
1071 probe_object.id.name,
1072 req_mb,
1073 max_mb);
1074 }
1075 else {
1076 inst_.info_append_i18n(
1077 "Not enough available video memory to bake \"{}\" ({} / {} MBytes).\n"
1078 "Try reducing surfel resolution or capture distance to lower the size of the "
1079 "allocation.",
1080 probe_object.id.name,
1081 req_mb,
1082 max_mb);
1083 }
1084
1085 if (G.background) {
1086 /* Print something in background mode instead of failing silently. */
1087 fprintf(stderr, "%s", inst_.info_get());
1088 }
1089
1090 do_break_ = true;
1091 return;
1092 }
1093 }
1094
1095 surfels_buf_.resize(capture_info_buf_.surfel_len);
1096 surfels_buf_.clear_to_zero();
1097
1098 dispatch_per_surfel_.x = divide_ceil_u(surfels_buf_.size(), SURFEL_GROUP_SIZE);
1099
1100 GPU_debug_group_begin("IrradianceBake.SurfelsCreate");
1101
1102 /* Raster the scene to generate the surfels. */
1103 capture_info_buf_.do_surfel_count = true;
1104 capture_info_buf_.do_surfel_output = true;
1105 capture_info_buf_.surfel_len = 0u;
1106 capture_info_buf_.push_update();
1107
1108 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_x_), grid_pixel_extent_).xy()));
1109 inst_.pipelines.capture.render(view_x_);
1110 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_y_), grid_pixel_extent_).xy()));
1111 inst_.pipelines.capture.render(view_y_);
1112 empty_raster_fb_.ensure(math::abs(transform_point(invert(basis_z_), grid_pixel_extent_).xy()));
1113 inst_.pipelines.capture.render(view_z_);
1114
1115 /* Sync with any other following pass using the surfel buffer. */
1117 /* Read back so that following push_update will contain correct surfel count. */
1118 capture_info_buf_.read();
1119
1121}
1122
1124{
1125 /* Use the last setup view. This should work since the view is orthographic. */
1126 /* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */
1127 inst_.render_buffers.acquire(int2(1));
1128 inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
1129 inst_.lights.set_view(view_z_, grid_pixel_extent_.xy());
1130 inst_.shadows.set_view(view_z_, grid_pixel_extent_.xy());
1132 /* There seems to be a synchronization issue with shadow rendering pass. If not waiting, the
1133 * surfels are lit without shadows. Waiting for sync here shouldn't be a huge bottleneck
1134 * anyway. */
1135 GPU_finish();
1136 }
1137 inst_.render_buffers.release();
1138
1139 inst_.manager->submit(surfel_light_eval_ps_, view_z_);
1140}
1141
1143{
1144 if (max_virtual_offset_ == 0.0f) {
1145 return;
1146 }
1149
1150 cluster_list_tx_.ensure_3d(GPU_R32I, capture_info_buf_.irradiance_grid_size, texture_usage);
1151 cluster_list_tx_.clear(int4(-1));
1152 /* View is not important here. It is only for validation. */
1153 inst_.manager->submit(surfel_cluster_build_ps_, view_z_);
1154}
1155
1157{
1158 if (max_virtual_offset_ == 0.0f) {
1159 /* NOTE: Virtual offset texture should already have been cleared to 0. */
1160 return;
1161 }
1162
1163 inst_.manager->submit(irradiance_offset_ps_, view_z_);
1164
1165 /* Not needed after this point. */
1166 cluster_list_tx_.free();
1167}
1168
1170{
1171 using namespace blender::math;
1172
1173 float2 rand_uv = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_LENS_U);
1174 const float3 ray_direction = Sampling::sample_sphere(rand_uv);
1175 const float3 up = ray_direction;
1176 const float3 forward = cross(up, normalize(orthogonal(up)));
1177 const float4x4 viewinv = from_orthonormal_axes<float4x4>(float3(0.0f), forward, up);
1178 const float4x4 viewmat = invert(viewinv);
1179
1180 /* Compute projection bounds. */
1181 float2 min, max;
1182 min = max = transform_point(viewmat, scene_bound_sphere_.xyz()).xy();
1183 min -= scene_bound_sphere_.w;
1184 max += scene_bound_sphere_.w;
1185
1186 /* This avoid light leaking by making sure that for one surface there will always be at least 1
1187 * surfel capture inside a ray list. Since the surface with the maximum distance (after
1188 * projection) between adjacent surfels is a slope that goes through 3 corners of a cube,
1189 * the distance the grid needs to cover is the diagonal of a cube face.
1190 *
1191 * The lower the number the more surfels it clumps together in the same surfel-list.
1192 * Biasing the grid_density like that will create many invalid link between coplanar surfels.
1193 * These are dealt with during the list sorting pass.
1194 *
1195 * This has a side effect of inflating shadows and emissive surfaces.
1196 *
1197 * We add an extra epsilon just in case. We really need this step to be leak free. */
1198 const float max_distance_between_neighbor_surfels_inv = M_SQRT1_2 - 1e-4;
1199 /* Surfel list per unit distance. */
1200 const float ray_grid_density = surfel_density_ * max_distance_between_neighbor_surfels_inv;
1201 /* Surfel list size in unit distance. */
1202 const float pixel_size = 1.0f / ray_grid_density;
1203 list_info_buf_.ray_grid_size = math::max(int2(1), int2(ray_grid_density * (max - min)));
1204
1205 /* Add a 2 pixels margin to have empty lists for irradiance grid samples to fall into (as they
1206 * are not considered by the scene bounds). The first pixel margin is because we are jittering
1207 * the grid position. */
1208 list_info_buf_.ray_grid_size += int2(4);
1209 min -= pixel_size * 2.0f;
1210 max += pixel_size * 2.0f;
1211
1212 /* Randomize grid center to avoid uneven inflating of corners in some directions. */
1213 const float2 aa_rand = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_FILTER_U);
1214 /* Offset in surfel list "pixel". */
1215 const float2 aa_offset = (aa_rand - 0.5f) * 0.499f;
1216 min += pixel_size * aa_offset;
1217
1218 list_info_buf_.list_max = list_info_buf_.ray_grid_size.x * list_info_buf_.ray_grid_size.y;
1219 list_info_buf_.push_update();
1220
1221 /* NOTE: Z values do not really matter since we are not doing any rasterization. */
1222 const float4x4 winmat = projection::orthographic<float>(min.x, max.x, min.y, max.y, 0, 1);
1223
1224 ray_view_.sync(viewmat, winmat);
1225
1226 dispatch_per_list_.x = divide_ceil_u(list_info_buf_.list_max, SURFEL_LIST_GROUP_SIZE);
1227
1228 list_start_buf_.resize(ceil_to_multiple_u(list_info_buf_.list_max, 4));
1229
1230 GPU_storagebuf_clear(list_start_buf_, -1);
1231 inst_.manager->submit(surfel_ray_build_ps_, ray_view_);
1232}
1233
1235{
1236 /* NOTE: Subtract 1 because after `sampling.step()`. */
1237 capture_info_buf_.sample_index = inst_.sampling.sample_index() - 1;
1238 capture_info_buf_.sample_count = inst_.sampling.sample_count();
1239 capture_info_buf_.push_update();
1240
1241 inst_.manager->submit(surfel_light_propagate_ps_, ray_view_);
1242
1243 std::swap(radiance_src_, radiance_dst_);
1244}
1245
1247{
1248 inst_.manager->submit(irradiance_capture_ps_, ray_view_);
1249}
1250
1251void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame)
1252{
1253 if (!ELEM(inst_.debug_mode,
1258 {
1259 return;
1260 }
1261
1263 capture_info_buf_.read();
1264 surfels_buf_.read();
1265
1266 cache_frame->surfels_len = capture_info_buf_.surfel_len;
1267 cache_frame->surfels = MEM_malloc_arrayN<Surfel>(cache_frame->surfels_len, __func__);
1268
1269 MutableSpan<Surfel> surfels_dst((Surfel *)cache_frame->surfels, cache_frame->surfels_len);
1270 Span<Surfel> surfels_src(surfels_buf_.data(), cache_frame->surfels_len);
1271 surfels_dst.copy_from(surfels_src);
1272}
1273
1274void IrradianceBake::read_virtual_offset(LightProbeGridCacheFrame *cache_frame)
1275{
1276 if (!ELEM(inst_.debug_mode, eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET)) {
1277 return;
1278 }
1279
1281
1282 cache_frame->baking.virtual_offset = (float(*)[4])virtual_offset_tx_.read<float4>(
1284}
1285
1287{
1289
1290 read_surfels(cache_frame);
1291 read_virtual_offset(cache_frame);
1292
1293 cache_frame->size[0] = irradiance_L0_tx_.width();
1294 cache_frame->size[1] = irradiance_L0_tx_.height();
1295 cache_frame->size[2] = irradiance_L0_tx_.depth();
1296
1298
1299 cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
1300 cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
1301 cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
1302 cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
1303 cache_frame->baking.validity = (float *)validity_tx_.read<float>(GPU_DATA_FLOAT);
1304
1305 return cache_frame;
1306}
1307
1309{
1311
1312 read_surfels(cache_frame);
1313 read_virtual_offset(cache_frame);
1314
1315 cache_frame->size[0] = irradiance_L0_tx_.width();
1316 cache_frame->size[1] = irradiance_L0_tx_.height();
1317 cache_frame->size[2] = irradiance_L0_tx_.depth();
1318
1320
1321 cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
1322 cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
1323 cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
1324 cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
1325 cache_frame->baking.validity = (float *)validity_tx_.read<float>(GPU_DATA_FLOAT);
1326
1327 int64_t sample_count = int64_t(irradiance_L0_tx_.width()) * irradiance_L0_tx_.height() *
1328 irradiance_L0_tx_.depth();
1329 size_t coefficient_texture_size = sizeof(*cache_frame->irradiance.L0) * sample_count;
1330 size_t validity_texture_size = sizeof(*cache_frame->connectivity.validity) * sample_count;
1331 cache_frame->irradiance.L0 = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1332 cache_frame->irradiance.L1_a = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1333 cache_frame->irradiance.L1_b = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1334 cache_frame->irradiance.L1_c = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__);
1335 cache_frame->connectivity.validity = (uint8_t *)MEM_mallocN(validity_texture_size, __func__);
1336
1337 size_t visibility_texture_size = sizeof(*cache_frame->irradiance.L0) * sample_count;
1338 cache_frame->visibility.L0 = (float *)MEM_mallocN(visibility_texture_size, __func__);
1339 cache_frame->visibility.L1_a = (float *)MEM_mallocN(visibility_texture_size, __func__);
1340 cache_frame->visibility.L1_b = (float *)MEM_mallocN(visibility_texture_size, __func__);
1341 cache_frame->visibility.L1_c = (float *)MEM_mallocN(visibility_texture_size, __func__);
1342
1343 /* TODO(fclem): This could be done on GPU if that's faster. */
1344 for (auto i : IndexRange(sample_count)) {
1345 copy_v3_v3(cache_frame->irradiance.L0[i], cache_frame->baking.L0[i]);
1346 copy_v3_v3(cache_frame->irradiance.L1_a[i], cache_frame->baking.L1_a[i]);
1347 copy_v3_v3(cache_frame->irradiance.L1_b[i], cache_frame->baking.L1_b[i]);
1348 copy_v3_v3(cache_frame->irradiance.L1_c[i], cache_frame->baking.L1_c[i]);
1349
1350 cache_frame->visibility.L0[i] = cache_frame->baking.L0[i][3];
1351 cache_frame->visibility.L1_a[i] = cache_frame->baking.L1_a[i][3];
1352 cache_frame->visibility.L1_b[i] = cache_frame->baking.L1_b[i][3];
1353 cache_frame->visibility.L1_c[i] = cache_frame->baking.L1_c[i][3];
1355 cache_frame->baking.validity[i]);
1356 }
1357
1358 MEM_SAFE_FREE(cache_frame->baking.L0);
1359 MEM_SAFE_FREE(cache_frame->baking.L1_a);
1360 MEM_SAFE_FREE(cache_frame->baking.L1_b);
1361 MEM_SAFE_FREE(cache_frame->baking.L1_c);
1362 MEM_SAFE_FREE(cache_frame->baking.validity);
1363
1364 return cache_frame;
1365}
1366
1368
1369} // 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:46
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()
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
@ GPU_DRIVER_ANY
@ GPU_OS_MAC
@ GPU_DEVICE_ANY
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_TRI_STRIP
@ GPU_PRIM_TRIS
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:385
void GPU_finish()
Definition gpu_state.cc:310
@ 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_R32F
@ GPU_R32I
@ GPU_R16F
@ GPU_RGBA32F
@ GPU_RGBA16F
@ GPU_R8
@ GPU_RGB16F
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
long long int int64_t
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
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:440
void bind_image(const char *name, GPUTexture *image)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:681
void dispatch(int group_len)
Definition draw_pass.hh:994
void barrier(eGPUBarrier type)
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:490
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)
static float3 sample_sphere(const float2 &rand)
Vector< IrradianceBrickPacked > bricks_alloc(int brick_len)
void bricks_free(Vector< IrradianceBrickPacked > &bricks)
void viewport_draw(View &view, GPUFrameBuffer *view_fb)
Mesh & DRW_object_get_data_for_drawing(const Object &object)
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_CLIP_CONTROL_UNIT_RANGE
Definition draw_state.hh:68
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ 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
VecBase< float, D > normalize(VecOp< float, D >) RET
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
#define printf(...)
float distance(VecOp< float, D >, VecOp< float, D >) RET
#define MEM_SAFE_FREE(v)
BLI_INLINE void grid_to_world(HairGrid *grid, float vecw[3], const float vec[3])
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
#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
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
char name[66]
Definition DNA_ID.h:415
LightProbeVisibilityData visibility
LightProbeConnectivityData connectivity
LightProbeIrradianceData irradiance
VecBase< T, 2 > xy() const
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
ccl_device_inline float3 transform_point(const ccl_private Transform *t, const float3 a)
Definition transform.h:56
int xy[2]
Definition wm_draw.cc:174