EEVEE-Next: LightProbe Bake: Add better feedback when allocation fail
- Add a report to explain why allocation fails and display the current limit. - Add heuristic to avoid out of memory issue. - Remove the delay property that did not do anything right now. - Reduce default clip end by half. Fix #121916 Pull Request: https://projects.blender.org/blender/blender/pulls/122572
This commit is contained in:
committed by
Clément Foucault
parent
358ccb8146
commit
0d7d6b00c9
@@ -683,11 +683,6 @@ void Instance::light_bake_irradiance(
|
||||
volume_probes.bake.irradiance_capture();
|
||||
}
|
||||
|
||||
if (sampling.finished()) {
|
||||
/* TODO(fclem): Dilation, filter etc... */
|
||||
// irradiance_cache.bake.irradiance_finalize();
|
||||
}
|
||||
|
||||
LightProbeGridCacheFrame *cache_frame;
|
||||
if (sampling.finished()) {
|
||||
cache_frame = volume_probes.bake.read_result_packed();
|
||||
|
||||
@@ -50,6 +50,12 @@ class LightBake {
|
||||
int frame_;
|
||||
/** Milliseconds. Delay the start of the baking to not slowdown interactions (TODO: remove). */
|
||||
int delay_ms_;
|
||||
/**
|
||||
* Reference to the operator report string to print messages to the UI.
|
||||
* Should be threadsafe to write to as it gets read by the operator code only if the job is
|
||||
* finished.
|
||||
*/
|
||||
std::string &report_;
|
||||
|
||||
/**
|
||||
* If running in parallel (in a separate thread), use this context.
|
||||
@@ -76,11 +82,13 @@ class LightBake {
|
||||
Scene *scene,
|
||||
Span<Object *> probes,
|
||||
bool run_as_job,
|
||||
std::string &report,
|
||||
int frame,
|
||||
int delay_ms = 0)
|
||||
: depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)),
|
||||
frame_(frame),
|
||||
delay_ms_(delay_ms),
|
||||
report_(report),
|
||||
original_probes_(probes)
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
@@ -179,8 +187,8 @@ class LightBake {
|
||||
});
|
||||
|
||||
if (instance_->info != "") {
|
||||
/** TODO: Print to the Status Bar UI. */
|
||||
printf("%s\n", instance_->info.c_str());
|
||||
/* Pipe report to operator. */
|
||||
report_ = instance_->info;
|
||||
}
|
||||
|
||||
if ((G.is_break == true) || (stop != nullptr && *stop == true)) {
|
||||
@@ -292,6 +300,7 @@ wmJob *EEVEE_NEXT_lightbake_job_create(wmWindowManager *wm,
|
||||
ViewLayer *view_layer,
|
||||
Scene *scene,
|
||||
blender::Vector<Object *> original_probes,
|
||||
std::string &report,
|
||||
int delay_ms,
|
||||
int frame)
|
||||
{
|
||||
@@ -311,7 +320,7 @@ wmJob *EEVEE_NEXT_lightbake_job_create(wmWindowManager *wm,
|
||||
WM_JOB_TYPE_LIGHT_BAKE);
|
||||
|
||||
LightBake *bake = new LightBake(
|
||||
bmain, view_layer, scene, std::move(original_probes), true, frame, delay_ms);
|
||||
bmain, view_layer, scene, std::move(original_probes), true, report, 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);
|
||||
@@ -330,10 +339,11 @@ void *EEVEE_NEXT_lightbake_job_data_alloc(Main *bmain,
|
||||
ViewLayer *view_layer,
|
||||
Scene *scene,
|
||||
blender::Vector<Object *> original_probes,
|
||||
std::string &report,
|
||||
int frame)
|
||||
{
|
||||
LightBake *bake = new LightBake(
|
||||
bmain, view_layer, scene, std::move(original_probes), false, frame);
|
||||
bmain, view_layer, scene, std::move(original_probes), false, report, frame);
|
||||
/* TODO(fclem): Can remove this cast once we remove the previous EEVEE light cache. */
|
||||
return reinterpret_cast<void *>(bake);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
#include <string>
|
||||
|
||||
struct wmWindowManager;
|
||||
struct wmWindow;
|
||||
@@ -38,6 +39,7 @@ wmJob *EEVEE_NEXT_lightbake_job_create(wmWindowManager *wm,
|
||||
ViewLayer *view_layer,
|
||||
Scene *scene,
|
||||
blender::Vector<Object *> original_probes,
|
||||
std::string &report,
|
||||
int delay_ms,
|
||||
int frame);
|
||||
|
||||
@@ -53,6 +55,7 @@ void *EEVEE_NEXT_lightbake_job_data_alloc(Main *bmain,
|
||||
ViewLayer *view_layer,
|
||||
Scene *scene,
|
||||
blender::Vector<Object *> original_probes,
|
||||
std::string &report,
|
||||
int frame);
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_lightprobe.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
@@ -15,6 +16,8 @@
|
||||
|
||||
#include "eevee_lightprobe_volume.hh"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -984,25 +987,44 @@ void IrradianceBake::surfels_create(const Object &probe_object)
|
||||
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
||||
capture_info_buf_.read();
|
||||
if (capture_info_buf_.surfel_len == 0) {
|
||||
/* No surfel to allocated. */
|
||||
do_break_ = true;
|
||||
/* No surfel to allocate. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (capture_info_buf_.surfel_len > surfels_buf_.size()) {
|
||||
printf("IrradianceBake: Allocating %u surfels.\n", capture_info_buf_.surfel_len);
|
||||
|
||||
size_t max_size = GPU_max_storage_buffer_size();
|
||||
if (GPU_mem_stats_supported()) {
|
||||
int total_mem_kb, free_mem_kb;
|
||||
GPU_mem_stats_get(&total_mem_kb, &free_mem_kb);
|
||||
max_size = min(max_size, size_t(free_mem_kb) * 1024);
|
||||
/* Leave at least 128MByte for OS and stuffs.
|
||||
* Try to avoid crashes because of OUT_OF_MEMORY errors. */
|
||||
size_t max_alloc = (size_t(total_mem_kb) - 128 * 1024) * 1024;
|
||||
/* Cap to 95% of available memory. */
|
||||
size_t max_free = size_t((size_t(free_mem_kb) * 1024) * 0.95f);
|
||||
|
||||
max_size = min(max_size, min(max_alloc, max_free));
|
||||
}
|
||||
|
||||
size_t required_mem = sizeof(Surfel) * (capture_info_buf_.surfel_len - surfels_buf_.size());
|
||||
if (required_mem > max_size) {
|
||||
capture_info_buf_.surfel_len = 0u;
|
||||
capture_info_buf_.push_update();
|
||||
inst_.info += "Error: Not enough memory to bake " + std::string(probe_object.id.name) +
|
||||
".\n";
|
||||
const bool is_ssbo_bound = (max_size == GPU_max_storage_buffer_size());
|
||||
const uint req_mb = required_mem / (1024 * 1024);
|
||||
const uint max_mb = max_size / (1024 * 1024);
|
||||
|
||||
inst_.info = std::string(is_ssbo_bound ? "Cannot allocate enough" : "Not enough available") +
|
||||
" video memory to bake \"" + std::string(probe_object.id.name + 2) + "\" (" +
|
||||
std::to_string(req_mb) + " / " + std::to_string(max_mb) +
|
||||
" MBytes). "
|
||||
"Try reducing surfel resolution or capture distance to lower the size of the "
|
||||
"allocation.\n";
|
||||
|
||||
if (G.background) {
|
||||
/* Print something in background mode instead of failing silently. */
|
||||
fprintf(stderr, "%s\n", inst_.info.c_str());
|
||||
}
|
||||
|
||||
do_break_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1590,6 +1590,15 @@ static blender::Vector<Object *> lightprobe_cache_irradiance_volume_subset_get(b
|
||||
return probes;
|
||||
}
|
||||
|
||||
struct BakeOperatorData {
|
||||
/* 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. */
|
||||
Scene *scene;
|
||||
|
||||
std::string report;
|
||||
};
|
||||
|
||||
static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
@@ -1597,7 +1606,6 @@ static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEve
|
||||
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");
|
||||
|
||||
blender::Vector<Object *> probes = lightprobe_cache_irradiance_volume_subset_get(C, op);
|
||||
|
||||
@@ -1605,15 +1613,16 @@ static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEve
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BakeOperatorData *data = MEM_new<BakeOperatorData>(__func__);
|
||||
data->scene = scene;
|
||||
data->report = "";
|
||||
|
||||
wmJob *wm_job = EEVEE_NEXT_lightbake_job_create(
|
||||
wm, win, bmain, view_layer, scene, probes, scene->r.cfra, delay);
|
||||
wm, win, bmain, view_layer, scene, probes, data->report, scene->r.cfra, 0);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
/* 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;
|
||||
op->customdata = static_cast<void *>(data);
|
||||
|
||||
WM_jobs_start(wm, wm_job);
|
||||
|
||||
@@ -1624,10 +1633,20 @@ static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEve
|
||||
|
||||
static int lightprobe_cache_bake_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Scene *scene = (Scene *)op->customdata;
|
||||
BakeOperatorData *data = static_cast<BakeOperatorData *>(op->customdata);
|
||||
Scene *scene = data->scene;
|
||||
|
||||
/* No running bake, remove handler and pass through. */
|
||||
if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LIGHT_BAKE)) {
|
||||
std::string report = data->report;
|
||||
|
||||
MEM_delete(data);
|
||||
op->customdata = nullptr;
|
||||
|
||||
if (!report.empty()) {
|
||||
BKE_report(op->reports, RPT_ERROR, report.c_str());
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
@@ -1642,7 +1661,7 @@ static int lightprobe_cache_bake_modal(bContext *C, wmOperator *op, const wmEven
|
||||
static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
Scene *scene = (Scene *)op->customdata;
|
||||
Scene *scene = static_cast<BakeOperatorData *>(op->customdata)->scene;
|
||||
|
||||
/* Kill on cancel, because job is using op->reports. */
|
||||
WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE);
|
||||
@@ -1659,8 +1678,9 @@ static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op)
|
||||
|
||||
blender::Vector<Object *> probes = lightprobe_cache_irradiance_volume_subset_get(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);
|
||||
std::string report;
|
||||
void *rj = EEVEE_NEXT_lightbake_job_data_alloc(
|
||||
bmain, view_layer, scene, probes, report, scene->r.cfra);
|
||||
/* Do the job. */
|
||||
wmJobWorkerStatus worker_status = {};
|
||||
EEVEE_NEXT_lightbake_job(rj, &worker_status);
|
||||
@@ -1668,6 +1688,11 @@ static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op)
|
||||
EEVEE_NEXT_lightbake_update(rj);
|
||||
EEVEE_NEXT_lightbake_job_data_free(rj);
|
||||
|
||||
if (!report.empty()) {
|
||||
BKE_report(op->reports, RPT_ERROR, report.c_str());
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -1699,17 +1724,6 @@ void OBJECT_OT_lightprobe_cache_bake(wmOperatorType *ot)
|
||||
ot->cancel = lightprobe_cache_bake_cancel;
|
||||
ot->exec = lightprobe_cache_bake_exec;
|
||||
|
||||
ot->prop = RNA_def_int(ot->srna,
|
||||
"delay",
|
||||
0,
|
||||
0,
|
||||
2000,
|
||||
"Delay",
|
||||
"Delay in millisecond before baking starts",
|
||||
0,
|
||||
2000);
|
||||
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
|
||||
|
||||
ot->prop = RNA_def_enum(
|
||||
ot->srna, "subset", light_cache_subset_items, 0, "Subset", "Subset of probes to update");
|
||||
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
.distpar = 2.5f, \
|
||||
.falloff = 0.2f, \
|
||||
.clipsta = 0.8f, \
|
||||
.clipend = 40.0f, \
|
||||
.clipend = 20.0f, \
|
||||
.vis_bias = 1.0f, \
|
||||
.vis_blur = 0.2f, \
|
||||
.intensity = 1.0f, \
|
||||
|
||||
Reference in New Issue
Block a user