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.
5 changed files with 335 additions and 4 deletions
Showing only changes of commit 52a7125ce3 - Show all commits

View File

@ -570,6 +570,34 @@ class RENDER_PT_eevee_indirect_lighting(RenderButtonsPanel, Panel):
col.prop(props, "gi_filter_quality")
class RENDER_PT_eevee_next_indirect_lighting(RenderButtonsPanel, Panel):
bl_label = "Indirect Lighting"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
@classmethod
def poll(cls, context):
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
scene = context.scene
props = scene.eevee
col = layout.column()
col.operator("scene.light_cache_bake", text="Bake Indirect Lighting", icon='RENDER_STILL')
col.operator("scene.light_cache_free", text="Delete Lighting Cache")
cache_info = scene.eevee.gi_cache_info
if cache_info:
col.label(text=cache_info)
col.prop(props, "gi_auto_bake")
class RENDER_PT_eevee_indirect_lighting_display(RenderButtonsPanel, Panel):
bl_label = "Display"
bl_parent_id = "RENDER_PT_eevee_indirect_lighting"
@ -905,6 +933,7 @@ classes = (
RENDER_PT_eevee_next_shadows,
RENDER_PT_eevee_indirect_lighting,
RENDER_PT_eevee_indirect_lighting_display,
RENDER_PT_eevee_next_indirect_lighting,
RENDER_PT_eevee_film,
RENDER_PT_eevee_next_film,

View File

@ -146,6 +146,7 @@ set(SRC
engines/eevee_next/eevee_instance.cc
engines/eevee_next/eevee_irradiance_cache.cc
engines/eevee_next/eevee_light.cc
engines/eevee_next/eevee_lightcache.cc
engines/eevee_next/eevee_material.cc
engines/eevee_next/eevee_motion_blur.cc
engines/eevee_next/eevee_pipeline.cc

View File

@ -0,0 +1,218 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup eevee
*
* Contains everything about light baking.
*/
#include "DRW_render.h"
#include "BKE_global.h"
#include "BLI_endian_switch.h"
#include "BLI_threads.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "BKE_object.h"
#include "DNA_collection_types.h"
#include "DNA_lightprobe_types.h"
#include "PIL_time.h"
#include "eevee_lightcache.h"
#include "GPU_capabilities.h"
#include "GPU_context.h"
#include "WM_api.h"
#include "WM_types.h"
#include "BLO_read_write.h"
#include "wm_window.h"
/* -------------------------------------------------------------------- */
/** \name Light Probe Baking
* \{ */
/* TODO: should be replace by a more elegant alternative. */
extern void DRW_opengl_context_enable(void);
extern void DRW_opengl_context_disable(void);
extern void DRW_opengl_render_context_enable(void *re_gl_context);
extern void DRW_opengl_render_context_disable(void *re_gl_context);
extern void DRW_gpu_render_context_enable(void *re_gpu_context);
extern void DRW_gpu_render_context_disable(void *re_gpu_context);
namespace blender::eevee {
class LightBake {
private:
Depsgraph *depsgraph_;
/** Scene frame to evaluate the depsgraph at. */
int frame_;
/** Milliseconds. Delay the start of the baking to not slowdown interactions (TODO: remove). */
int delay_ms_;
/**
* If running in parallel (in a separate thread), use this context.
* Created on main thread but first bound in worker thread.
*/
void *gl_context_ = nullptr;
/** GPUContext associated to `gl_context_`. Created in the worker thread. */
void *gpu_context_ = nullptr;
public:
LightBake(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
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)
{
BLI_assert(BLI_thread_is_main());
if (run_as_job && !GPU_use_main_context_workaround()) {
/* This needs to happen in main thread. */
gl_context_ = WM_opengl_context_create();
wm_window_reset_drawable();
}
std::cout << "Create" << std::endl;
}
~LightBake()
{
BLI_assert(BLI_thread_is_main());
std::cout << "Delete" << std::endl;
DEG_graph_free(depsgraph_);
}
/**
* Called from main thread.
* Copy result to original scene data.
* Note that since this is in the main thread, the viewport cannot be using the light cache.
* So there is no race condition here.
*/
void update()
{
BLI_assert(BLI_thread_is_main());
std::cout << "update" << std::endl;
}
/**
* Called from worker thread.
*/
void run(bool *stop = nullptr, bool *do_update = nullptr, float *progress = nullptr)
{
UNUSED_VARS(stop, do_update, progress);
DEG_graph_relations_update(depsgraph_);
DEG_evaluate_on_framechange(depsgraph_, frame_);
PIL_sleep_ms(1000);
std::cout << "run" << std::endl;
delete_resources();
}
private:
void context_enable()
{
}
void context_disable()
{
}
/**
* Delete the engine instance and the optional contexts.
* This needs to run on the worker thread because the OpenGL context can only be ever bound to a
* single thread (because of some driver implementation), and the resources (textures,
* buffers,...) need to be freed with the right context bound.
*/
void delete_resources()
{
}
};
} // namespace blender::eevee
extern "C" {
using namespace blender::eevee;
wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
struct wmWindow *win,
struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
int delay_ms,
int frame)
{
/* Do not bake if there is a render going on. */
if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) {
return nullptr;
}
/* Stop existing baking job. */
WM_jobs_stop(wm, nullptr, (void *)EEVEE_NEXT_lightbake_job);
wmJob *wm_job = WM_jobs_get(wm,
win,
scene,
"Bake Lighting",
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);
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);
WM_jobs_callbacks(wm_job,
EEVEE_NEXT_lightbake_job,
nullptr,
EEVEE_NEXT_lightbake_update,
EEVEE_NEXT_lightbake_update);
G.is_break = false;
return wm_job;
}
void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
bool run_as_job,
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);
/* TODO(fclem): Can remove this cast once we remove the previous EEVEE light cache. */
return reinterpret_cast<void *>(bake);
}
void EEVEE_NEXT_lightbake_job_data_free(void *job_data)
{
delete reinterpret_cast<LightBake *>(job_data);
}
void EEVEE_NEXT_lightbake_update(void *job_data)
{
reinterpret_cast<LightBake *>(job_data)->update();
}
void EEVEE_NEXT_lightbake_job(void *job_data, bool *stop, bool *do_update, float *progress)
{
reinterpret_cast<LightBake *>(job_data)->run(stop, do_update, progress);
}
}
/** \} */

View File

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup eevee
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/** Opaque type hiding eevee::LightBake. */
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()` or reuse data from an already
* existing baking job.
* IMPORTANT: Must run on the main thread because of potential GPUContext creation.
*/
struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
struct wmWindow *win,
struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
int delay_ms,
int frame);
/**
* Allocate dependency graph and job description (EEVEE_NEXT_LightBake).
* Dependency graph evaluation does *not* happen here. It is delayed until
* `EEVEE_NEXT_lightbake_job` runs.
* IMPORTANT: Must run on the main thread because of potential GPUContext creation.
* Return `EEVEE_NEXT_LightBake *` but cast to `void *` because of compatibility with existing
* EEVEE function.
*/
void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
bool run_as_job,
int frame);
/**
* Free the job data.
* NOTE: Does not free the GPUContext. This is the responsibility of `EEVEE_NEXT_lightbake_job()`
*/
void EEVEE_NEXT_lightbake_job_data_free(void *job_data /* EEVEE_NEXT_LightBake */);
/**
* Callback for updating original scene light cache with bake result.
* Run by the job system for each update step and the finish step.
* This is called manually by `EEVEE_NEXT_lightbake_job()` if not run from a job.
*/
void EEVEE_NEXT_lightbake_update(void *job_data /* EEVEE_NEXT_LightBake */);
/**
* Do the full light baking for all samples.
* Will call `EEVEE_NEXT_lightbake_update()` on finish.
*/
void EEVEE_NEXT_lightbake_job(void *job_data /* EEVEE_NEXT_LightBake */,
bool *stop,
bool *do_update,
float *progress);
#ifdef __cplusplus
}
#endif

View File

@ -83,6 +83,7 @@
#include "RE_pipeline.h"
#include "engines/eevee/eevee_lightcache.h"
#include "engines/eevee_next/eevee_lightcache.h"
#include "render_intern.hh" /* own include */
@ -1393,15 +1394,23 @@ 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 = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false, scene->r.cfra);
void *rj = ((use_eevee_next) ?
EEVEE_NEXT_lightbake_job_data_alloc :
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. */
EEVEE_lightbake_job(rj, &stop, &do_update, &progress);
EEVEE_lightbake_job_data_free(rj);
/* Do the job. */
((use_eevee_next) ? EEVEE_NEXT_lightbake_job :
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);
/* No redraw needed, we leave state as we entered it. */
ED_update_for_newframe(bmain, CTX_data_depsgraph_pointer(C));
@ -1420,7 +1429,11 @@ 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");
wmJob *wm_job = EEVEE_lightbake_job_create(
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)(
wm, win, bmain, view_layer, scene, delay, scene->r.cfra);
if (!wm_job) {