WIP: eevee-next-world-irradiance #108304

Closed
Jeroen Bakker wants to merge 79 commits from Jeroen-Bakker:eevee-next-world-irradiance into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
18 changed files with 478 additions and 657 deletions
Showing only changes of commit a5310b7720 - Show all commits

View File

@ -187,6 +187,8 @@ static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader,
cache->baking.L1_a = nullptr;
cache->baking.L1_b = nullptr;
cache->baking.L1_c = nullptr;
cache->surfels = nullptr;
cache->surfels_len = 0;
if (cache->irradiance.L0 != nullptr) {
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L0);
@ -257,6 +259,7 @@ void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache)
spherical_harmonic_free(cache->irradiance);
spherical_harmonic_free(cache->visibility);
MEM_SAFE_FREE(cache->connectivity.bitmask);
MEM_SAFE_FREE(cache->surfels);
MEM_SAFE_FREE(cache);
}

View File

@ -107,7 +107,6 @@
#include "BLO_read_write.h"
#include "engines/eevee/eevee_lightcache.h"
#include "engines/eevee_next/eevee_lightcache.h"
#include "PIL_time.h"
@ -1444,10 +1443,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
}
}
RenderEngineType *engine_type = RE_engines_find(sce->r.engine);
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
(use_eevee_next ? EEVEE_NEXT_lightcache_info_update : EEVEE_lightcache_info_update)(&sce->eevee);
EEVEE_lightcache_info_update(&sce->eevee);
BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading);

View File

@ -24,7 +24,6 @@
#include "PIL_time.h"
#include "../eevee_next/eevee_lightcache.h"
#include "eevee_lightcache.h"
#include "eevee_private.h"
@ -502,11 +501,6 @@ static void eevee_lightbake_readback_reflections(LightCache *lcache)
void EEVEE_lightcache_free(LightCache *lcache)
{
if (lcache->version == LIGHTCACHE_NEXT_STATIC_VERSION) {
EEVEE_NEXT_lightcache_free(lcache);
return;
}
DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex);
MEM_SAFE_FREE(lcache->cube_tx.data);
DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex);
@ -545,10 +539,6 @@ static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex
void EEVEE_lightcache_blend_write(BlendWriter *writer, LightCache *cache)
{
if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) {
EEVEE_NEXT_lightcache_blend_write(writer, cache);
}
write_lightcache_texture(writer, &cache->grid_tx);
write_lightcache_texture(writer, &cache->cube_tx);
@ -589,10 +579,6 @@ static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTe
void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache)
{
if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) {
EEVEE_NEXT_lightcache_blend_read_data(reader, cache);
}
cache->flag &= ~LIGHTCACHE_NOT_USABLE;
direct_link_lightcache_texture(reader, &cache->cube_tx);
direct_link_lightcache_texture(reader, &cache->grid_tx);

View File

@ -78,9 +78,7 @@ void Camera::sync()
CameraData &data = data_;
/* WORKAROUND(fclem): This is an abomination that should be remove ASAP. */
bool is_first_baking_sync = inst_.light_probes.grids.size() == 0;
if (inst_.is_baking() && !is_first_baking_sync) {
if (inst_.is_baking()) {
/* Any view so that shadows and light culling works during irradiance bake. */
draw::View &view = inst_.irradiance_cache.bake.view_z_;
data.viewmat = view.viewmat();

View File

@ -21,7 +21,6 @@
#include "eevee_engine.h"
#include "eevee_instance.hh"
#include "eevee_lightcache.h"
namespace blender::eevee {
@ -333,51 +332,6 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Bake
* \{ */
LightCache *Instance::light_cache_create(Vector<IrradianceGrid> grids,
Vector<ReflectionCube> cubes)
{
LightCache *light_cache = EEVEE_NEXT_lightcache_create();
light_cache->flag = LIGHTCACHE_BAKING;
light_cache->grid_len = grids.size();
light_cache->grids = MEM_cnew_array<LightCacheIrradianceGrid>(grids.size(),
"LightCacheIrradianceGrid");
for (const auto i : grids.index_range()) {
const IrradianceGrid &grid = grids[i];
size_t sample_count = grid.resolution.x * grid.resolution.y * grid.resolution.z;
size_t grid_texture_sample_size = sizeof(uint16_t) * 4;
if (sample_count * grid_texture_sample_size > INT_MAX) {
/* The size of the texture doesn't fit on a 32bit system. */
light_cache->flag |= LIGHTCACHE_INVALID;
info = "Scene contains an irradiance grid with too many samples points";
return nullptr;
}
LightCacheIrradianceGrid &irradiance_grid = light_cache->grids[i];
float4x4 world_to_grid = math::invert(grid.transform);
copy_m4_m4(irradiance_grid.world_to_grid, world_to_grid.ptr());
irradiance_grid.resolution[0] = grid.resolution.x;
irradiance_grid.resolution[1] = grid.resolution.y;
irradiance_grid.resolution[2] = grid.resolution.z;
}
light_cache->cube_len = cubes.size();
// light_cache->cubes = MEM_cnew_array<LightCacheIrradianceGrid>(cubes.size(),
// "LightCacheIrradianceGrid");
// for (const auto i : cubes.index_range()) {
/* TODO */
// }
return light_cache;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Interface
* \{ */
@ -498,12 +452,12 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view
EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
}
void Instance::light_bake_irradiance(LightCache *&r_light_cache,
void Instance::light_bake_irradiance(Object &probe,
std::function<void()> context_enable,
std::function<void()> context_disable)
std::function<void()> context_disable,
std::function<void(LightProbeGridCacheFrame *)> result_update)
{
BLI_assert(is_baking());
BLI_assert(r_light_cache == nullptr);
auto custom_pipeline_wrapper = [&](std::function<void()> callback) {
context_enable();
@ -519,67 +473,47 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache,
context_disable();
};
/* Count probes. */
/* TODO(fclem): Ideally, this should only iterate the despgraph and not do a full sync. */
custom_pipeline_wrapper([&]() {
GPU_debug_capture_begin();
irradiance_cache.bake.surfel_raster_views_sync(probe);
/* TODO: lightprobe visibility group option. */
manager->begin_sync();
render_sync();
manager->end_sync();
irradiance_cache.bake.surfels_create(probe);
irradiance_cache.bake.surfels_lights_eval();
GPU_debug_capture_end();
});
/* Allocate CPU storage. */
r_light_cache = this->light_cache_create(light_probes.grids, light_probes.cubes);
if (r_light_cache->flag & LIGHTCACHE_INVALID) {
/* Something happened and the light cache couldn't be created. */
// stop = true;
// do_update = true;
r_light_cache->flag &= ~LIGHTCACHE_BAKING;
return;
}
int bounce_len = scene->eevee.gi_diffuse_bounces;
/* Start at bounce 1 as 0 bounce is no indirect lighting. */
for (int bounce = 1; bounce <= bounce_len; bounce++) {
sampling.init(scene);
while (!sampling.finished()) {
context_wrapper([&]() {
/* Batch ray cast by pack of 16 to avoid too much overhead of
* the update function & context switch. */
for (int i = 0; i < 16 && !sampling.finished(); i++) {
sampling.step();
irradiance_cache.bake.propagate_light_sample();
}
if (sampling.finished()) {
irradiance_cache.bake.accumulate_bounce();
}
for (auto i : light_probes.grids.index_range()) {
custom_pipeline_wrapper([&]() {
GPU_debug_capture_begin();
irradiance_cache.bake.surfel_raster_views_sync(light_probes.grids[i]);
/* TODO: lightprobe visibility group option. */
manager->begin_sync();
render_sync();
manager->end_sync();
irradiance_cache.bake.surfels_create(light_probes.grids[i]);
irradiance_cache.bake.surfels_lights_eval();
GPU_debug_capture_end();
});
int bounce_len = scene->eevee.gi_diffuse_bounces;
/* Start at bounce 1 as 0 bounce is no indirect lighting. */
for (int bounce = 1; bounce <= bounce_len; bounce++) {
sampling.init(scene);
while (!sampling.finished()) {
context_wrapper([&]() {
/* Batch ray cast by pack of 16 to avoid too much overhead of
* the update function & context switch. */
for (int i = 0; i < 16 && !sampling.finished(); i++) {
sampling.step();
irradiance_cache.bake.propagate_light_sample();
}
if (sampling.finished()) {
irradiance_cache.bake.accumulate_bounce();
}
irradiance_cache.bake.read_result(r_light_cache->grids[i]);
});
// do_update = true;
}
}
if (bounce_len == 0) {
/* Still read result for debugging surfel direct lighting. */
context_wrapper([&]() { irradiance_cache.bake.read_result(r_light_cache->grids[i]); });
LightProbeGridCacheFrame *cache_frame;
if (bounce != bounce_len) {
/* TODO(fclem): Only do this read-back if needed. But it might be tricky to know when. */
cache_frame = irradiance_cache.bake.read_result_unpacked();
}
else {
cache_frame = irradiance_cache.bake.read_result_packed();
}
result_update(cache_frame);
});
}
}
r_light_cache->flag |= LIGHTCACHE_BAKED;
r_light_cache->flag &= ~LIGHTCACHE_BAKING;
}
/** \} */

View File

@ -146,9 +146,10 @@ class Instance {
/* Light bake. */
void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager);
void light_bake_irradiance(LightCache *&r_light_cache,
void light_bake_irradiance(Object &probe,
std::function<void()> context_enable,
std::function<void()> context_disable);
std::function<void()> context_disable,
std::function<void(LightProbeGridCacheFrame *)> result_update);
static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer);
@ -196,13 +197,6 @@ class Instance {
void scene_sync();
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
/**
* Create a light cache big enough to fit all light-probes inside.
* IMPORTANT: Can return nullptr on failure, in which case, the `Instance::info` will be set
* to an error message.
*/
LightCache *light_cache_create(Vector<IrradianceGrid> grids, Vector<ReflectionCube> cubes);
void update_eval_members();
void set_time(float time);

View File

@ -2,6 +2,8 @@
#include "DNA_lightprobe_types.h"
#include "BKE_lightprobe.h"
#include "GPU_capabilities.h"
#include "GPU_debug.h"
@ -17,15 +19,15 @@ namespace blender::eevee {
/** \name Interface
* \{ */
void IrradianceCache::init() {}
void IrradianceCache::init()
{
display_grids_enabled_ = DRW_state_draw_support() &&
inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE;
}
void IrradianceCache::sync()
{
if (!inst_.is_baking()) {
debug_pass_sync();
display_pass_sync();
}
else {
if (inst_.is_baking()) {
bake.sync();
}
}
@ -38,42 +40,6 @@ void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb)
}
}
void IrradianceCache::debug_pass_sync()
{
if (!ELEM(inst_.debug_mode,
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL,
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) {
return;
}
LightCache *light_cache = inst_.scene->eevee.light_cache_data;
if (light_cache == nullptr || light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION ||
light_cache->grids == nullptr || light_cache->grid_len == 0) {
return;
}
debug_surfels_ps_.init();
debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL);
debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS));
debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f);
debug_surfels_ps_.push_constant("debug_mode", static_cast<int>(inst_.debug_mode));
surfels_buf_.clear();
for (auto i : IndexRange(light_cache->grid_len)) {
LightCacheIrradianceGrid &grid = light_cache->grids[i];
if (grid.surfels_len > 0 && grid.surfels != nullptr) {
Span<Surfel> grid_surfels(static_cast<Surfel *>(grid.surfels), grid.surfels_len);
for (const Surfel &surfel : grid_surfels) {
surfels_buf_.append(surfel);
}
}
}
surfels_buf_.push_update();
debug_surfels_ps_.bind_ssbo("surfels_buf", surfels_buf_);
debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, surfels_buf_.size(), 4);
}
void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
{
switch (inst_.debug_mode) {
@ -88,66 +54,105 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
return;
}
GPU_framebuffer_bind(view_fb);
inst_.manager->submit(debug_surfels_ps_, view);
}
void IrradianceCache::display_pass_sync()
{
LightCache *light_cache = inst_.scene->eevee.light_cache_data;
display_grids_enabled_ = light_cache && light_cache->grid_len > 0 && light_cache->grids &&
DRW_state_draw_support() &&
inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE;
if (!display_grids_enabled_) {
return;
}
display_grids_ps_.init();
display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK);
display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID));
for (auto i : IndexRange(light_cache->grid_len)) {
LightCacheIrradianceGrid &grid = light_cache->grids[i];
if (grid.irradiance_L0_L1_a.data == nullptr) {
for (const IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
if (grid.cache == nullptr) {
continue;
}
auto load_texture = [&](const char *name, LightCacheTexture &cache_texture) {
if ((light_cache->flag & LIGHTCACHE_BAKED) && cache_texture.tex != nullptr) {
return;
}
if (light_cache->flag & LIGHTCACHE_BAKING) {
GPU_TEXTURE_FREE_SAFE(cache_texture.tex);
}
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
cache_texture.tex = GPU_texture_create_3d(
name, UNPACK3(cache_texture.tex_size), 1, GPU_RGBA16F, usage, cache_texture.data);
};
load_texture("grid.irradiance_L0_L1_a", grid.irradiance_L0_L1_a);
load_texture("grid.irradiance_L0_L1_b", grid.irradiance_L0_L1_b);
load_texture("grid.irradiance_L0_L1_c", grid.irradiance_L0_L1_c);
LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
display_grids_ps_.push_constant("sphere_radius", 0.3f);
display_grids_ps_.push_constant("grid_resolution", int3(grid.resolution));
float4x4 grid_to_world = math::invert(float4x4(grid.world_to_grid));
display_grids_ps_.push_constant("grid_to_world", grid_to_world);
display_grids_ps_.bind_texture("irradiance_a_tx", grid.irradiance_L0_L1_a.tex);
display_grids_ps_.bind_texture("irradiance_b_tx", grid.irradiance_L0_L1_b.tex);
display_grids_ps_.bind_texture("irradiance_c_tx", grid.irradiance_L0_L1_c.tex);
if (cache->surfels == nullptr || cache->surfels_len == 0) {
continue;
}
int cell_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, cell_count * 3 * 2);
debug_surfels_ps_.init();
debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL);
display_grids_ps_.framebuffer_set(&view_fb);
debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS));
debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f);
debug_surfels_ps_.push_constant("debug_mode", static_cast<int>(inst_.debug_mode));
debug_surfels_buf_.resize(cache->surfels_len);
/* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an input
* data. */
Span<Surfel> grid_surfels(static_cast<Surfel *>(cache->surfels), cache->surfels_len);
MutableSpan<Surfel>(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels);
debug_surfels_buf_.push_update();
debug_surfels_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_);
debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4);
inst_.manager->submit(debug_surfels_ps_, view);
}
}
void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
{
if (display_grids_enabled_) {
GPU_framebuffer_bind(view_fb);
if (!display_grids_enabled_) {
return;
}
for (const IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
if (grid.cache == nullptr) {
continue;
}
LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
if (cache == nullptr) {
continue;
}
/* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */
draw::Texture irradiance_a_tx_ = {"irradiance_a_tx"};
draw::Texture irradiance_b_tx_ = {"irradiance_b_tx"};
draw::Texture irradiance_c_tx_ = {"irradiance_c_tx"};
draw::Texture irradiance_d_tx_ = {"irradiance_d_tx"};
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
int3 grid_size = int3(cache->size);
if (cache->baking.L0) {
irradiance_a_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0);
irradiance_b_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a);
irradiance_c_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b);
irradiance_d_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c);
}
else if (cache->irradiance.L0) {
irradiance_a_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0);
irradiance_b_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a);
irradiance_c_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b);
irradiance_d_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c);
}
else {
continue;
}
display_grids_ps_.init();
display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK);
display_grids_ps_.framebuffer_set(&view_fb);
display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID));
display_grids_ps_.push_constant("sphere_radius", 0.3f); /* TODO property */
display_grids_ps_.push_constant("grid_resolution", grid_size);
display_grids_ps_.push_constant("grid_to_world", grid.object_to_world);
display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx_);
display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx_);
display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx_);
display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx_);
int sample_count = static_cast<int>(BKE_lightprobe_grid_cache_frame_sample_count(cache));
int triangle_count = sample_count * 2;
display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, triangle_count * 3);
inst_.manager->submit(display_grids_ps_, view);
irradiance_a_tx_.free();
irradiance_b_tx_.free();
irradiance_c_tx_.free();
irradiance_d_tx_.free();
}
}
@ -215,9 +220,10 @@ void IrradianceBake::sync()
pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
pass.bind_ssbo("list_start_buf", &list_start_buf_);
pass.bind_ssbo("list_info_buf", &list_info_buf_);
pass.bind_image("irradiance_L0_L1_a_img", &irradiance_L0_L1_a_tx_);
pass.bind_image("irradiance_L0_L1_b_img", &irradiance_L0_L1_b_tx_);
pass.bind_image("irradiance_L0_L1_c_img", &irradiance_L0_L1_c_tx_);
pass.bind_image("irradiance_L0_img", &irradiance_L0_tx_);
pass.bind_image("irradiance_L1_a_img", &irradiance_L1_a_tx_);
pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_);
pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_);
pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS);
pass.dispatch(&dispatch_per_grid_sample_);
}
@ -232,10 +238,10 @@ void IrradianceBake::sync()
}
}
void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid)
void IrradianceBake::surfel_raster_views_sync(const Object &probe_object)
{
using namespace blender::math;
const float4x4 transform(grid.transform);
const float4x4 transform(probe_object.object_to_world);
float3 scale;
math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale);
@ -258,7 +264,7 @@ void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid)
sync_view(view_z_, basis_z_);
}
void IrradianceBake::surfels_create(const IrradianceGrid &grid)
void IrradianceBake::surfels_create(const Object &probe_object)
{
/**
* We rasterize the scene along the 3 axes. Each generated fragment will write a surface element
@ -267,9 +273,14 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid)
*/
using namespace blender::math;
dispatch_per_grid_sample_ = math::divide_ceil(grid.resolution, int3(IRRADIANCE_GRID_GROUP_SIZE));
capture_info_buf_.irradiance_grid_size = grid.resolution;
capture_info_buf_.irradiance_grid_local_to_world = grid.transform;
const ::LightProbe *lightprobe = static_cast<::LightProbe *>(probe_object.data);
int3 grid_resolution = int3(&lightprobe->grid_resolution_x);
float4x4 grid_local_to_world = invert(float4x4(probe_object.world_to_object));
dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE));
capture_info_buf_.irradiance_grid_size = grid_resolution;
capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world;
capture_info_buf_.irradiance_accum_solid_angle = 0.0f;
/* Divide by twice the sample count because each ray is evaluated in both directions. */
capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) /
@ -280,14 +291,14 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid)
/* 32bit float is needed here otherwise we loose too much energy from rounding error during the
* accumulation when the sample count is above 500. */
irradiance_L0_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage);
irradiance_L0_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage);
irradiance_L0_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage);
irradiance_L0_L1_a_tx_.clear(float4(0.0f));
irradiance_L0_L1_b_tx_.clear(float4(0.0f));
irradiance_L0_L1_c_tx_.clear(float4(0.0f));
const float4x4 transform(grid.transform);
irradiance_L0_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
irradiance_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
irradiance_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
irradiance_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
irradiance_L0_tx_.clear(float4(0.0f));
irradiance_L1_a_tx_.clear(float4(0.0f));
irradiance_L1_b_tx_.clear(float4(0.0f));
irradiance_L1_c_tx_.clear(float4(0.0f));
/* Extract bounding box. Order is arbitrary as it is not important for our usage. */
const std::array<float3, 8> bbox_corners({float3{+1, +1, +1},
@ -300,7 +311,7 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid)
float3{-1, -1, -1}});
grid_bbox_vertices.clear();
for (const float3 &point : bbox_corners) {
grid_bbox_vertices.append(transform_point(transform, point));
grid_bbox_vertices.append(transform_point(grid_local_to_world, point));
}
DRW_stats_group_start("IrradianceBake.SurfelsCount");
@ -428,35 +439,85 @@ void IrradianceBake::accumulate_bounce()
inst_.manager->submit(surfel_light_bounce_ps_);
}
void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid)
void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame)
{
auto read_texture = [&](LightCacheTexture &cache_texture, draw::Texture &texture) {
MEM_SAFE_FREE(cache_texture.data);
cache_texture.data = (char *)texture.read<float4>(GPU_DATA_FLOAT);
copy_v3_v3_int(cache_texture.tex_size, light_cache_grid.resolution);
cache_texture.data_type = LIGHTCACHETEX_FLOAT;
cache_texture.components = 4;
};
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
read_texture(light_cache_grid.irradiance_L0_L1_a, irradiance_L0_L1_a_tx_);
read_texture(light_cache_grid.irradiance_L0_L1_b, irradiance_L0_L1_b_tx_);
read_texture(light_cache_grid.irradiance_L0_L1_c, irradiance_L0_L1_c_tx_);
switch (inst_.debug_mode) {
case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL:
case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE:
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
capture_info_buf_.read();
surfels_buf_.read();
light_cache_grid.surfels_len = capture_info_buf_.surfel_len;
/* TODO(fclem): This isn't threadsafe. */
MEM_SAFE_FREE(light_cache_grid.surfels);
light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data());
break;
default:
/* Nothing to display. */
return;
if (!ELEM(inst_.debug_mode,
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL,
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) {
return;
}
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
capture_info_buf_.read();
surfels_buf_.read();
cache_frame->surfels_len = capture_info_buf_.surfel_len;
cache_frame->surfels = MEM_malloc_arrayN(cache_frame->surfels_len, sizeof(Surfel), __func__);
MutableSpan<Surfel> surfels_dst((Surfel *)cache_frame->surfels, cache_frame->surfels_len);
Span<Surfel> surfels_src(surfels_buf_.data(), cache_frame->surfels_len);
surfels_dst.copy_from(surfels_src);
}
LightProbeGridCacheFrame *IrradianceBake::read_result_unpacked()
{
LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create();
read_surfels(cache_frame);
cache_frame->size[0] = irradiance_L0_tx_.width();
cache_frame->size[1] = irradiance_L0_tx_.height();
cache_frame->size[2] = irradiance_L0_tx_.depth();
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
/* TODO(fclem): Connectivity. */
// cache_frame->connectivity.bitmask = connectivity_tx_.read<uint8_t>(GPU_DATA_FLOAT);
return cache_frame;
}
LightProbeGridCacheFrame *IrradianceBake::read_result_packed()
{
LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create();
read_surfels(cache_frame);
cache_frame->size[0] = irradiance_L0_tx_.width();
cache_frame->size[1] = irradiance_L0_tx_.height();
cache_frame->size[2] = irradiance_L0_tx_.depth();
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
/* TODO(fclem): Temp. */
cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
/* TODO(fclem): Create packed format texture and swizzle on gpu. */
// cache_frame->irradiance.L0 = (float(*)[4])irradiance_only_L0_tx_.read<float4>(GPU_DATA_FLOAT);
// cache_frame->irradiance.L1_a =
// (float(*)[4])irradiance_only_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
// cache_frame->irradiance.L1_b =
// (float(*)[4])irradiance_only_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
// cache_frame->irradiance.L1_c =
// (float(*)[4])irradiance_only_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
// cache_frame->visibility.L0 = irradiance_only_L0_tx_.read<uint8_t>(GPU_DATA_UBYTE);
// cache_frame->visibility.L1_a = irradiance_only_L1_a_tx_.read<uint8_t>(GPU_DATA_UBYTE);
// cache_frame->visibility.L1_b = irradiance_only_L1_b_tx_.read<uint8_t>(GPU_DATA_UBYTE);
// cache_frame->visibility.L1_c = irradiance_only_L1_c_tx_.read<uint8_t>(GPU_DATA_UBYTE);
/* TODO(fclem): Connectivity. */
// cache_frame->connectivity.bitmask = connectivity_tx_.read<uint8_t>(GPU_DATA_FLOAT);
return cache_frame;
}
/** \} */

View File

@ -77,9 +77,10 @@ class IrradianceBake {
int3 dispatch_per_grid_sample_ = int3(1);
/** Irradiance textures for baking. Only represents one grid in there. */
Texture irradiance_L0_L1_a_tx_ = {"irradiance_L0_L1_a_tx_"};
Texture irradiance_L0_L1_b_tx_ = {"irradiance_L0_L1_b_tx_"};
Texture irradiance_L0_L1_c_tx_ = {"irradiance_L0_L1_c_tx_"};
Texture irradiance_L0_tx_ = {"irradiance_L0_tx_"};
Texture irradiance_L1_a_tx_ = {"irradiance_L1_a_tx_"};
Texture irradiance_L1_b_tx_ = {"irradiance_L1_b_tx_"};
Texture irradiance_L1_c_tx_ = {"irradiance_L1_c_tx_"};
/* Orientation of the irradiance grid being baked. */
math::Quaternion grid_orientation_;
@ -96,9 +97,9 @@ class IrradianceBake {
void sync();
/** Create the views used to rasterize the scene into surfel representation. */
void surfel_raster_views_sync(const IrradianceGrid &grid);
/** Create a surfel representation of the scene from the \a grid using the capture pipeline. */
void surfels_create(const IrradianceGrid &grid);
void surfel_raster_views_sync(const Object &probe_object);
/** Create a surfel representation of the scene from the probe using the capture pipeline. */
void surfels_create(const Object &probe_object);
/** Evaluate direct lighting (and also clear the surfels radiance). */
void surfels_lights_eval();
/** Propagate light from surfel to surfel in a random direction over the sphere. */
@ -106,8 +107,14 @@ class IrradianceBake {
/** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */
void accumulate_bounce();
/** Read grid final irradiance back to CPU into \a light_cache_grid . */
void read_result(LightCacheIrradianceGrid &light_cache_grid);
/** Read grid unpacked irradiance back to CPU and returns as a #LightProbeGridCacheFrame. */
LightProbeGridCacheFrame *read_result_unpacked();
/** Read grid packed irradiance back to CPU and returns as a #LightProbeGridCacheFrame. */
LightProbeGridCacheFrame *read_result_packed();
private:
/** Read surfel data back to CPU into \a cache_frame . */
void read_surfels(LightProbeGridCacheFrame *cache_frame);
};
/**
@ -121,10 +128,12 @@ class IrradianceCache {
private:
Instance &inst_;
/** Display surfel debug data. */
PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"};
/** Debug surfel elements copied from the light cache. */
draw::StorageVectorBuffer<Surfel> surfels_buf_;
draw::StorageArrayBuffer<Surfel> debug_surfels_buf_;
/** Display grid cache data. */
bool display_grids_enabled_ = false;
PassSimple display_grids_ps_ = {"IrradianceCache.Display Grids"};
@ -137,9 +146,7 @@ class IrradianceCache {
void viewport_draw(View &view, GPUFrameBuffer *view_fb);
private:
void debug_pass_sync();
void debug_pass_draw(View &view, GPUFrameBuffer *view_fb);
void display_pass_sync();
void display_pass_draw(View &view, GPUFrameBuffer *view_fb);
};

View File

@ -9,8 +9,10 @@
#include "DRW_render.h"
#include "BKE_global.h"
#include "BKE_lightprobe.h"
#include "DNA_lightprobe_types.h"
#include "BLI_endian_switch.h"
#include "BLI_threads.h"
#include "DEG_depsgraph_build.h"
@ -24,14 +26,12 @@
#include "WM_api.h"
#include "WM_types.h"
#include "BLO_read_write.h"
#include "wm_window.h"
#include "eevee_engine.h"
#include "eevee_instance.hh"
#include "eevee_lightcache.h"
#include "eevee_lightcache.hh"
/* -------------------------------------------------------------------- */
/** \name Light Probe Baking
@ -64,18 +64,29 @@ class LightBake {
/** Manager used for command submission. Created and freed in the worker thread. */
draw::Manager *manager_ = nullptr;
/** Lightprobe original objects to bake. */
Vector<Object *> original_probes_;
/** Frame to copy to original objects during update. This is needed to avoid race conditions. */
Vector<LightProbeGridCacheFrame *> bake_result_;
std::mutex result_mutex_;
public:
LightBake(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
Span<Object *> probes,
bool run_as_job,
int frame,
int delay_ms = 0)
: depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)),
frame_(frame),
delay_ms_(delay_ms)
delay_ms_(delay_ms),
original_probes_(probes)
{
BLI_assert(BLI_thread_is_main());
bake_result_.resize(probes.size());
bake_result_.fill(nullptr);
if (run_as_job && !GPU_use_main_context_workaround()) {
/* This needs to happen in main thread. */
gl_context_ = WM_opengl_context_create();
@ -98,20 +109,28 @@ class LightBake {
void update()
{
BLI_assert(BLI_thread_is_main());
Scene *original_scene = DEG_get_input_scene(depsgraph_);
LightCache *&scene_light_cache = original_scene->eevee.light_cache_data;
if (scene_light_cache != light_cache_) {
if (scene_light_cache != nullptr) {
/* Delete old data if existing. */
EEVEE_NEXT_lightcache_free(scene_light_cache);
for (auto i : bake_result_.index_range()) {
if (bake_result_[i] == nullptr) {
continue;
}
scene_light_cache = light_cache_;
Object *orig_ob = original_probes_[i];
{
std::scoped_lock lock(result_mutex_);
LightProbeObjectCache *cache = orig_ob->lightprobe_cache;
/* Delete any existing cache. */
if (cache->grid_static_cache != nullptr) {
BKE_lightprobe_grid_cache_frame_free(cache->grid_static_cache);
}
/* Pass ownership to original object. */
cache->grid_static_cache = bake_result_[i];
bake_result_[i] = nullptr;
}
/* Propagate the cache to evaluated object. */
DEG_id_tag_update(&orig_ob->id, ID_RECALC_COPY_ON_WRITE);
}
EEVEE_NEXT_lightcache_info_update(&original_scene->eevee);
DEG_id_tag_update(&original_scene->id, ID_RECALC_COPY_ON_WRITE);
}
/**
@ -124,18 +143,36 @@ class LightBake {
DEG_graph_relations_update(depsgraph_);
DEG_evaluate_on_framechange(depsgraph_, frame_);
if (delay_ms_ > 0) {
PIL_sleep_ms(delay_ms_);
}
context_enable();
manager_ = new draw::Manager();
instance_ = new eevee::Instance();
instance_->init_light_bake(depsgraph_, manager_);
context_disable();
if (delay_ms_ > 0) {
PIL_sleep_ms(delay_ms_);
}
for (auto i : original_probes_.index_range()) {
Object *eval_ob = DEG_get_evaluated_object(depsgraph_, original_probes_[i]);
instance_->light_bake_irradiance(
light_cache_, [this]() { context_enable(); }, [this]() { context_disable(); });
instance_->light_bake_irradiance(
*eval_ob,
[this]() { context_enable(); },
[this]() { context_disable(); },
[&](LightProbeGridCacheFrame *cache_frame) {
{
std::scoped_lock lock(result_mutex_);
/* Delete any existing cache that wasn't transferred to the original object. */
if (bake_result_[i] != nullptr) {
BKE_lightprobe_grid_cache_frame_free(bake_result_[i]);
}
bake_result_[i] = cache_frame;
}
*do_update = true;
/* TODO: Update progress. */
});
}
delete_resources();
}
@ -226,8 +263,6 @@ class LightBake {
} // namespace blender::eevee
extern "C" {
using namespace blender::eevee;
/* -------------------------------------------------------------------- */
@ -239,6 +274,7 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
blender::Vector<struct Object *> original_probes,
int delay_ms,
int frame)
{
@ -257,7 +293,8 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS,
WM_JOB_TYPE_LIGHT_BAKE);
LightBake *bake = new LightBake(bmain, view_layer, scene, true, frame, delay_ms);
LightBake *bake = new LightBake(
bmain, view_layer, scene, original_probes, true, frame, delay_ms);
WM_jobs_customdata_set(wm_job, bake, EEVEE_NEXT_lightbake_job_data_free);
WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0);
@ -275,12 +312,10 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
bool run_as_job,
blender::Vector<struct Object *> original_probes,
int frame)
{
/* This should only be used for exec job. Eventually, remove `run_as_job` parameter later. */
BLI_assert(run_as_job == false);
LightBake *bake = new LightBake(bmain, view_layer, scene, run_as_job, frame);
LightBake *bake = new LightBake(bmain, view_layer, scene, original_probes, false, frame);
/* TODO(fclem): Can remove this cast once we remove the previous EEVEE light cache. */
return reinterpret_cast<void *>(bake);
}
@ -301,184 +336,3 @@ void EEVEE_NEXT_lightbake_job(void *job_data, bool *stop, bool *do_update, float
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Cache Create / Delete
* \{ */
LightCache *EEVEE_NEXT_lightcache_create()
{
LightCache *light_cache = (LightCache *)MEM_callocN(sizeof(LightCache), "LightCache");
light_cache->version = LIGHTCACHE_NEXT_STATIC_VERSION;
light_cache->type = LIGHTCACHE_TYPE_STATIC;
return light_cache;
}
void EEVEE_NEXT_lightcache_free(LightCache *light_cache)
{
if (light_cache->version < LIGHTCACHE_NEXT_STATIC_VERSION) {
/* Allow deleting old EEVEE cache. */
DRW_TEXTURE_FREE_SAFE(light_cache->cube_tx.tex);
MEM_SAFE_FREE(light_cache->cube_tx.data);
DRW_TEXTURE_FREE_SAFE(light_cache->grid_tx.tex);
MEM_SAFE_FREE(light_cache->grid_tx.data);
if (light_cache->cube_mips) {
for (int i = 0; i < light_cache->mips_len; i++) {
MEM_SAFE_FREE(light_cache->cube_mips[i].data);
}
MEM_SAFE_FREE(light_cache->cube_mips);
}
MEM_SAFE_FREE(light_cache->cube_data);
MEM_SAFE_FREE(light_cache->grid_data);
}
else {
for (int i = 0; i < light_cache->grid_len; i++) {
MEM_SAFE_FREE(light_cache->grids[i].surfels);
MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_a.data);
MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_b.data);
MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_c.data);
DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_a.tex);
DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_b.tex);
DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_c.tex);
}
MEM_SAFE_FREE(light_cache->grids);
}
MEM_freeN(light_cache);
}
void EEVEE_NEXT_lightcache_info_update(SceneEEVEE *eevee)
{
LightCache *light_cache = eevee->light_cache_data;
if (light_cache == nullptr) {
BLI_strncpy(eevee->light_cache_info,
TIP_("No light cache in this scene"),
sizeof(eevee->light_cache_info));
return;
}
if (light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION) {
BLI_strncpy(eevee->light_cache_info,
TIP_("Incompatible Light cache version, please bake again"),
sizeof(eevee->light_cache_info));
return;
}
if (light_cache->flag & LIGHTCACHE_INVALID) {
BLI_strncpy(eevee->light_cache_info,
TIP_("Error: Light cache dimensions not supported by the GPU"),
sizeof(eevee->light_cache_info));
return;
}
if (light_cache->flag & LIGHTCACHE_BAKING) {
BLI_strncpy(
eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info));
return;
}
int irradiance_sample_len = 0;
for (const LightCacheIrradianceGrid &grid :
blender::Span<LightCacheIrradianceGrid>(light_cache->grids, light_cache->grid_len)) {
irradiance_sample_len += grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
}
size_t size_in_bytes = irradiance_sample_len * sizeof(uint16_t) * 4 * 3;
char formatted_mem[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE];
BLI_str_format_byte_unit(formatted_mem, size_in_bytes, false);
BLI_snprintf(eevee->light_cache_info,
sizeof(eevee->light_cache_info),
TIP_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"),
light_cache->cube_len,
irradiance_sample_len,
formatted_mem);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Cache Read/Write to file
* \{ */
void EEVEE_NEXT_lightcache_blend_write(BlendWriter *writer, LightCache *light_cache)
{
auto write_lightcache_texture = [&](LightCacheTexture &tex) {
if (tex.data) {
size_t data_size = tex.components * tex.tex_size[0] * tex.tex_size[1] * tex.tex_size[2];
if (tex.data_type == LIGHTCACHETEX_FLOAT) {
data_size *= sizeof(float);
}
else if (tex.data_type == LIGHTCACHETEX_UINT) {
data_size *= sizeof(uint);
}
/* FIXME: We can't save more than what 32bit systems can handle.
* The solution would be to split the texture but it is too late for 2.90. (see #78529) */
if (data_size < INT_MAX) {
BLO_write_raw(writer, data_size, tex.data);
}
}
};
BLO_write_struct_array(
writer, LightCacheIrradianceGrid, light_cache->grid_len, light_cache->grids);
for (int i = 0; i < light_cache->grid_len; i++) {
LightCacheIrradianceGrid &grid = light_cache->grids[i];
/* Surfels are runtime data. Not stored in the blend file. */
write_lightcache_texture(grid.irradiance_L0_L1_a);
write_lightcache_texture(grid.irradiance_L0_L1_b);
write_lightcache_texture(grid.irradiance_L0_L1_c);
}
}
void EEVEE_NEXT_lightcache_blend_read_data(BlendDataReader *reader, LightCache *light_cache)
{
if (light_cache->grids) {
BLO_read_data_address(reader, &light_cache->grids);
auto direct_link_lightcache_texture = [&](LightCacheTexture &lctex) {
/* Runtime data. Not stored in the blend file. */
lctex.tex = nullptr;
if (lctex.data) {
BLO_read_data_address(reader, &lctex.data);
if (lctex.data && BLO_read_requires_endian_switch(reader)) {
int data_size = lctex.components * lctex.tex_size[0] * lctex.tex_size[1] *
lctex.tex_size[2];
if (lctex.data_type == LIGHTCACHETEX_FLOAT) {
BLI_endian_switch_float_array((float *)lctex.data, data_size * sizeof(float));
}
else if (lctex.data_type == LIGHTCACHETEX_UINT) {
BLI_endian_switch_uint32_array((uint *)lctex.data, data_size * sizeof(uint));
}
}
}
if (lctex.data == nullptr) {
zero_v3_int(lctex.tex_size);
}
};
for (int i = 0; i < light_cache->grid_len; i++) {
LightCacheIrradianceGrid &grid = light_cache->grids[i];
/* Runtime data. Not stored in the blend file. */
grid.surfels_len = 0;
grid.surfels = nullptr;
direct_link_lightcache_texture(grid.irradiance_L0_L1_a);
direct_link_lightcache_texture(grid.irradiance_L0_L1_b);
direct_link_lightcache_texture(grid.irradiance_L0_L1_c);
}
}
}
/** \} */
}
/** \} */

View File

@ -6,9 +6,15 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "BLI_vector.hh"
struct wmWindowManager;
struct wmWindow;
struct Main;
struct ViewLayer;
struct Scene;
struct Object;
struct wmJob;
/** Opaque type hiding eevee::LightBake. */
typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake;
@ -21,7 +27,6 @@ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake;
* Create the job description.
* This is called for async (modal) bake operator.
* The actual work will be done by `EEVEE_NEXT_lightbake_job()`.
* Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()`.
* IMPORTANT: Must run on the main thread because of potential GPUContext creation.
*/
struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
@ -29,6 +34,7 @@ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
blender::Vector<struct Object *> original_probes,
int delay_ms,
int frame);
@ -43,7 +49,7 @@ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
bool run_as_job,
blender::Vector<struct Object *> original_probes,
int frame);
/**
@ -69,38 +75,3 @@ void EEVEE_NEXT_lightbake_job(void *job_data /* EEVEE_NEXT_LightBake */,
float *progress);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Cache Create / Delete
* \{ */
/**
* Create an empty light-cache.
*/
struct LightCache *EEVEE_NEXT_lightcache_create(void);
/**
* Free a light-cache and its associated data.
*/
void EEVEE_NEXT_lightcache_free(struct LightCache *lcache);
/**
* Update the UI message in the render panel about the state of the cache.
*/
void EEVEE_NEXT_lightcache_info_update(struct SceneEEVEE *eevee);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Cache Read/Write to file
* \{ */
void EEVEE_NEXT_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *light_cache);
void EEVEE_NEXT_lightcache_blend_read_data(struct BlendDataReader *reader,
struct LightCache *light_cache);
/** \} */
#ifdef __cplusplus
}
#endif

View File

@ -19,40 +19,28 @@ void LightProbeModule::begin_sync()
{
auto_bake_enabled_ = inst_.is_viewport() &&
(inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0;
grid_update_ = false;
cube_update_ = false;
grids.clear();
cubes.clear();
}
void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
{
LightProbe &grid = grid_map_.lookup_or_add_default(handle.object_key);
IrradianceGrid &grid = grid_map_.lookup_or_add_default(handle.object_key);
grid.used = true;
if (handle.recalc != 0 || grid.initialized == false) {
grid.initialized = true;
grid_update_ = true;
}
if (inst_.is_baking()) {
const ::LightProbe *light_probe = (const ::LightProbe *)ob->data;
grids.append({float4x4(ob->object_to_world), &light_probe->grid_resolution_x});
grid.updated = true;
grid.object_to_world = float4x4(ob->object_to_world);
grid.cache = ob->lightprobe_cache;
}
}
void LightProbeModule::sync_cube(ObjectHandle &handle)
{
LightProbe &cube = cube_map_.lookup_or_add_default(handle.object_key);
ReflectionCube &cube = cube_map_.lookup_or_add_default(handle.object_key);
cube.used = true;
if (handle.recalc != 0 || cube.initialized == false) {
cube.initialized = true;
cube_update_ = true;
}
if (inst_.is_baking()) {
cubes.append({});
}
}
void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle)
@ -75,10 +63,14 @@ void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle)
void LightProbeModule::end_sync()
{
{
/* Check for deleted grid. */
/* Check for deleted or updated grid. */
grid_update_ = false;
auto it_end = grid_map_.items().end();
for (auto it = grid_map_.items().begin(); it != it_end; ++it) {
LightProbe &grid = (*it).value;
IrradianceGrid &grid = (*it).value;
if (grid.updated) {
grid_update_ = true;
}
if (!grid.used) {
grid_map_.remove(it);
grid_update_ = true;
@ -89,10 +81,14 @@ void LightProbeModule::end_sync()
}
}
{
/* Check for deleted cube. */
/* Check for deleted or updated cube. */
cube_update_ = false;
auto it_end = cube_map_.items().end();
for (auto it = cube_map_.items().begin(); it != it_end; ++it) {
LightProbe &cube = (*it).value;
ReflectionCube &cube = (*it).value;
if (cube.updated) {
cube_update_ = true;
}
if (!cube.used) {
cube_map_.remove(it);
cube_update_ = true;

View File

@ -16,30 +16,37 @@
namespace blender::eevee {
class Instance;
struct IrradianceGrid {
float4x4 transform;
int3 resolution;
};
struct ReflectionCube {};
class IrradianceCache;
struct LightProbe {
bool used = false;
bool initialized = false;
bool updated = false;
};
struct IrradianceGrid : public LightProbe {
/** Reference to the light-cache data. Should be refreshed every sync. */
float4x4 object_to_world;
/**
* Reference to the light-cache data.
* Do not try to dereference it before LightProbeModule::end_sync() as the grid could
* already have been freed (along with its cache). It is only safe to dereference after the
* pruning have been done.
*/
const struct LightProbeObjectCache *cache = nullptr;
};
struct ReflectionCube : public LightProbe {};
class LightProbeModule {
public:
/** Synced probe data. Only valid if the `eevee::Instance` is a baking instance. */
Vector<IrradianceGrid> grids;
Vector<ReflectionCube> cubes;
friend class IrradianceCache;
private:
Instance &inst_;
/** Light Probe map to detect deletion. */
Map<ObjectKey, LightProbe> grid_map_, cube_map_;
/** Light Probe map to detect deletion and store associated data. */
Map<ObjectKey, IrradianceGrid> grid_map_;
Map<ObjectKey, ReflectionCube> cube_map_;
/** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */
bool grid_update_;
/** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */

View File

@ -11,13 +11,15 @@ void main()
return;
}
SphericalHarmonicL1 sh = spherical_harmonics_unpack(texelFetch(irradiance_a_tx, cell, 0),
texelFetch(irradiance_b_tx, cell, 0),
texelFetch(irradiance_c_tx, cell, 0));
SphericalHarmonicL1 sh;
sh.L0.M0 = texelFetch(irradiance_a_tx, cell, 0);
sh.L1.Mn1 = texelFetch(irradiance_b_tx, cell, 0);
sh.L1.M0 = texelFetch(irradiance_c_tx, cell, 0);
sh.L1.Mp1 = texelFetch(irradiance_d_tx, cell, 0);
vec3 vN = vec3(lP, sqrt(max(0.0, 1.0 - dist_sqr)));
vec3 N = normal_view_to_world(vN);
vec3 irradiance = spherical_harmonics_evaluate_lambert(N, sh);
out_color = vec4(irradiance, 0.0);
vec4 irradiance = spherical_harmonics_evaluate_lambert(N, sh);
out_color = vec4(irradiance.rgb, 0.0);
}

View File

@ -17,7 +17,7 @@
void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh)
{
spherical_harmonics_encode_signal_sample(
L, irradiance * capture_info_buf.irradiance_sample_solid_angle, sh);
L, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh);
}
void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh)
@ -59,10 +59,11 @@ void main()
vec3 sky_L = cameraVec(P);
SphericalHarmonicL1 sh = spherical_harmonics_unpack(
imageLoad(irradiance_L0_L1_a_img, grid_coord),
imageLoad(irradiance_L0_L1_b_img, grid_coord),
imageLoad(irradiance_L0_L1_c_img, grid_coord));
SphericalHarmonicL1 sh;
sh.L0.M0 = imageLoad(irradiance_L0_img, grid_coord);
sh.L1.Mn1 = imageLoad(irradiance_L1_a_img, grid_coord);
sh.L1.M0 = imageLoad(irradiance_L1_b_img, grid_coord);
sh.L1.Mp1 = imageLoad(irradiance_L1_c_img, grid_coord);
if (surfel_next > -1) {
irradiance_capture(surfel_buf[surfel_next], P, sh);
@ -80,9 +81,8 @@ void main()
irradiance_capture(-sky_L, vec3(0.0), sh);
}
vec4 sh_a, sh_b, sh_c;
spherical_harmonics_pack(sh, sh_a, sh_b, sh_c);
imageStore(irradiance_L0_L1_a_img, grid_coord, sh_a);
imageStore(irradiance_L0_L1_b_img, grid_coord, sh_b);
imageStore(irradiance_L0_L1_c_img, grid_coord, sh_c);
imageStore(irradiance_L0_img, grid_coord, sh.L0.M0);
imageStore(irradiance_L1_a_img, grid_coord, sh.L1.Mn1);
imageStore(irradiance_L1_b_img, grid_coord, sh.L1.M0);
imageStore(irradiance_L1_c_img, grid_coord, sh.L1.Mp1);
}

View File

@ -64,21 +64,21 @@ float spherical_harmonics_L2_Mp2(vec3 v)
* \{ */
struct SphericalHarmonicBandL0 {
vec3 M0;
vec4 M0;
};
struct SphericalHarmonicBandL1 {
vec3 Mn1;
vec3 M0;
vec3 Mp1;
vec4 Mn1;
vec4 M0;
vec4 Mp1;
};
struct SphericalHarmonicBandL2 {
vec3 Mn2;
vec3 Mn1;
vec3 M0;
vec3 Mp1;
vec3 Mp2;
vec4 Mn2;
vec4 Mn1;
vec4 M0;
vec4 Mp1;
vec4 Mp2;
};
struct SphericalHarmonicL0 {
@ -106,14 +106,14 @@ struct SphericalHarmonicL2 {
* \{ */
void spherical_harmonics_L0_encode_signal_sample(vec3 direction,
vec3 amplitude,
vec4 amplitude,
inout SphericalHarmonicBandL0 r_L0)
{
r_L0.M0 += spherical_harmonics_L0_M0(direction) * amplitude;
}
void spherical_harmonics_L1_encode_signal_sample(vec3 direction,
vec3 amplitude,
vec4 amplitude,
inout SphericalHarmonicBandL1 r_L1)
{
r_L1.Mn1 += spherical_harmonics_L1_Mn1(direction) * amplitude;
@ -122,7 +122,7 @@ void spherical_harmonics_L1_encode_signal_sample(vec3 direction,
}
void spherical_harmonics_L2_encode_signal_sample(vec3 direction,
vec3 amplitude,
vec4 amplitude,
inout SphericalHarmonicBandL2 r_L2)
{
r_L2.Mn2 += spherical_harmonics_L2_Mn2(direction) * amplitude;
@ -133,14 +133,14 @@ void spherical_harmonics_L2_encode_signal_sample(vec3 direction,
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
vec4 amplitude,
inout SphericalHarmonicL0 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
vec4 amplitude,
inout SphericalHarmonicL1 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
@ -148,7 +148,7 @@ void spherical_harmonics_encode_signal_sample(vec3 direction,
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
vec4 amplitude,
inout SphericalHarmonicL2 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
@ -164,19 +164,19 @@ void spherical_harmonics_encode_signal_sample(vec3 direction,
* Evaluate an encoded signal in a given unit vector direction.
* \{ */
vec3 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0)
vec4 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0)
{
return spherical_harmonics_L0_M0(direction) * L0.M0;
}
vec3 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1)
vec4 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1)
{
return spherical_harmonics_L1_Mn1(direction) * L1.Mn1 +
spherical_harmonics_L1_M0(direction) * L1.M0 +
spherical_harmonics_L1_Mp1(direction) * L1.Mp1;
}
vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
{
return spherical_harmonics_L2_Mn2(direction) * L2.Mn2 +
spherical_harmonics_L2_Mn1(direction) * L2.Mn1 +
@ -197,16 +197,16 @@ vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
* 2/3 and 1/4. See this reference for more explanation:
* https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
*/
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0);
}
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0);
}
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) +
@ -221,27 +221,36 @@ vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
* This section define the compression scheme of spherical harmonic data.
* \{ */
SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a, vec4 L0_L1_b, vec4 L0_L1_c)
SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a,
vec4 L0_L1_b,
vec4 L0_L1_c,
vec4 L0_L1_vis)
{
SphericalHarmonicL1 sh;
sh.L0.M0 = L0_L1_a.xyz;
sh.L1.Mn1 = L0_L1_b.xyz;
sh.L1.M0 = L0_L1_c.xyz;
sh.L1.Mp1 = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w);
sh.L0.M0.xyz = L0_L1_a.xyz;
sh.L1.Mn1.xyz = L0_L1_b.xyz;
sh.L1.M0.xyz = L0_L1_c.xyz;
sh.L1.Mp1.xyz = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w);
sh.L0.M0.w = L0_L1_vis.x;
sh.L1.Mn1.w = L0_L1_vis.y;
sh.L1.M0.w = L0_L1_vis.z;
sh.L1.Mp1.w = L0_L1_vis.w;
return sh;
}
void spherical_harmonics_pack(SphericalHarmonicL1 sh,
out vec4 L0_L1_a,
out vec4 L0_L1_b,
out vec4 L0_L1_c)
out vec4 L0_L1_c,
out vec4 L0_L1_vis)
{
L0_L1_a.xyz = sh.L0.M0;
L0_L1_b.xyz = sh.L1.Mn1;
L0_L1_c.xyz = sh.L1.M0;
L0_L1_a.xyz = sh.L0.M0.xyz;
L0_L1_b.xyz = sh.L1.Mn1.xyz;
L0_L1_c.xyz = sh.L1.M0.xyz;
L0_L1_a.w = sh.L1.Mp1.x;
L0_L1_b.w = sh.L1.Mp1.y;
L0_L1_c.w = sh.L1.Mp1.z;
L0_L1_vis = vec4(sh.L0.M0.w, sh.L1.Mn1.w, sh.L1.M0.w, sh.L1.Mp1.w);
}
/** \} */

View File

@ -77,6 +77,7 @@ GPU_SHADER_CREATE_INFO(eevee_display_probe_grid)
.sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx")
.sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx")
.sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx")
.sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
@ -86,8 +87,9 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
.additional_info("eevee_shared", "eevee_surfel_common", "draw_view")
.storage_buf(0, Qualifier::READ, "int", "list_start_buf[]")
.storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_a_img")
.image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_b_img")
.image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_c_img")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_img")
.image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_a_img")
.image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_b_img")
.image(3, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_c_img")
.compute_source("eevee_lightprobe_irradiance_ray_comp.glsl")
.do_static_compilation(true);

View File

@ -85,7 +85,7 @@
#include "RE_pipeline.h"
#include "engines/eevee/eevee_lightcache.h"
#include "engines/eevee_next/eevee_lightcache.h"
#include "engines/eevee_next/eevee_lightcache.hh"
#include "render_intern.hh" /* own include */
@ -1401,23 +1401,17 @@ static int light_cache_bake_exec(bContext *C, wmOperator *op)
G.is_break = false;
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
/* TODO: abort if selected engine is not eevee. */
void *rj = ((use_eevee_next) ?
EEVEE_NEXT_lightbake_job_data_alloc :
EEVEE_lightbake_job_data_alloc)(bmain, view_layer, scene, false, scene->r.cfra);
void *rj = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false, scene->r.cfra);
light_cache_bake_tag_cache(scene, op);
bool stop = false, do_update;
float progress; /* Not actually used. */
/* Do the job. */
((use_eevee_next) ? EEVEE_NEXT_lightbake_job :
EEVEE_lightbake_job)(rj, &stop, &do_update, &progress);
EEVEE_lightbake_job(rj, &stop, &do_update, &progress);
/* Free baking data. Result is already stored in the scene data. */
((use_eevee_next) ? EEVEE_NEXT_lightbake_job_data_free : EEVEE_lightbake_job_data_free)(rj);
EEVEE_lightbake_job_data_free(rj);
/* No redraw needed, we leave state as we entered it. */
ED_update_for_newframe(bmain, CTX_data_depsgraph_pointer(C));
@ -1436,11 +1430,7 @@ static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent *
Scene *scene = CTX_data_scene(C);
int delay = RNA_int_get(op->ptr, "delay");
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
wmJob *wm_job = ((use_eevee_next) ? EEVEE_NEXT_lightbake_job_create :
EEVEE_lightbake_job_create)(
wmJob *wm_job = EEVEE_lightbake_job_create(
wm, win, bmain, view_layer, scene, delay, scene->r.cfra);
if (!wm_job) {
@ -1514,7 +1504,7 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot)
/* NOTE: New version destined to replace the old lightcache bake operator. */
static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
static blender::Vector<Object *> lightprobe_cache_bake_start(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Scene *scene = CTX_data_scene(C);
@ -1524,10 +1514,13 @@ static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
static_cast<LightProbe *>(ob->data)->type == LIGHTPROBE_TYPE_GRID;
};
auto irradiance_volume_setup = [](Object *ob) {
blender::Vector<Object *> probes;
auto irradiance_volume_setup = [&](Object *ob) {
BKE_lightprobe_cache_free(ob);
BKE_lightprobe_cache_create(ob);
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
probes.append(ob);
};
int subset = RNA_enum_get(op->ptr, "subset");
@ -1576,21 +1569,37 @@ static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
BLI_assert_unreachable();
break;
}
return probes;
}
static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
int delay = RNA_int_get(op->ptr, "delay");
lightprobe_cache_bake_start(C, op);
blender::Vector<Object *> probes = lightprobe_cache_bake_start(C, op);
if (probes.is_empty()) {
return OPERATOR_CANCELLED;
}
wmJob *wm_job = EEVEE_NEXT_lightbake_job_create(
wm, win, bmain, view_layer, scene, probes, scene->r.cfra, delay);
WM_event_add_modal_handler(C, op);
/* store actual owner of job, so modal operator could check for it,
/* Store actual owner of job, so modal operator could check for it,
* the reason of this is that active scene could change when rendering
* several layers from compositor #31800. */
op->customdata = scene;
WM_jobs_start(wm, wm_job);
WM_cursor_wait(false);
return OPERATOR_RUNNING_MODAL;
@ -1625,7 +1634,18 @@ static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op)
/* Executes blocking bake. */
static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op)
{
lightprobe_cache_bake_start(C, op);
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
blender::Vector<Object *> probes = lightprobe_cache_bake_start(C, op);
/* TODO: abort if selected engine is not eevee. */
void *rj = EEVEE_NEXT_lightbake_job_data_alloc(bmain, view_layer, scene, probes, scene->r.cfra);
/* Do the job. */
EEVEE_NEXT_lightbake_job(rj, nullptr, nullptr, nullptr);
/* Free baking data. Result is already stored in the scene data. */
EEVEE_NEXT_lightbake_job_data_free(rj);
return OPERATOR_FINISHED;
}
@ -1703,11 +1723,7 @@ static int light_cache_free_exec(bContext *C, wmOperator * /*op*/)
EEVEE_lightcache_free(scene->eevee.light_cache_data);
scene->eevee.light_cache_data = nullptr;
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
((use_eevee_next) ? EEVEE_NEXT_lightcache_info_update :
EEVEE_lightcache_info_update)(&scene->eevee);
EEVEE_lightcache_info_update(&scene->eevee);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);

View File

@ -139,21 +139,6 @@ typedef struct LightCacheTexture {
char _pad[2];
} LightCacheTexture;
typedef struct LightCacheIrradianceGrid {
/** Transform to local space [0..resolution-1] range. */
float world_to_grid[4][4];
/** Number of samples in each dimension. */
int resolution[3];
/** Number of surfels in the cache. */
int surfels_len;
/** Currently only used at runtime for debugging the baking process. Not written to file. */
void *surfels;
/** Irradiance Data. Stored as spherical harmonic. */
LightCacheTexture irradiance_L0_L1_a;
LightCacheTexture irradiance_L0_L1_b;
LightCacheTexture irradiance_L0_L1_c;
} LightCacheIrradianceGrid;
typedef struct LightCache {
int flag;
/** Version number to know if the cache data is compatible with this version of blender. */
@ -179,17 +164,10 @@ typedef struct LightCache {
/* All lightprobes data contained in the cache. */
LightProbeCache *cube_data;
LightGridCache *grid_data;
/* ---- EEVEE-Next ---- */
LightCacheIrradianceGrid *grids;
// LightCacheReflectionCube *cubes;
} LightCache;
/* Bump the version number for lightcache data structure changes. */
#define LIGHTCACHE_STATIC_VERSION 2
/* Cache generated by EEVEE-Next. Should be removed and be replaced by bumped
* LIGHTCACHE_STATIC_VERSION once EEVEE-Next is made default. */
#define LIGHTCACHE_NEXT_STATIC_VERSION 3
/* LightCache->type */
enum {
@ -306,6 +284,13 @@ typedef struct LightProbeGridCacheFrame {
LightProbeIrradianceData irradiance;
LightProbeVisibilityData visibility;
LightProbeConnectivityData connectivity;
char _pad[4];
/** Number of debug surfels. */
int surfels_len;
/** Debug surfels used to visualize the baking process. */
void *surfels;
} LightProbeGridCacheFrame;
/** #LightProbeGridCacheFrame.data_layout (int) */