EEVEE crashes when it is not able to allocate buffers. Previously we had a message showing to the user that it tries to allocate a texture larger than supported by the GPU. But was not implemented for EEVEE-next. This fix will add back this error message.  Pull Request: https://projects.blender.org/blender/blender/pulls/134725
797 lines
24 KiB
C++
797 lines
24 KiB
C++
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup eevee
|
|
*
|
|
* An instance contains all structures needed to do a complete render.
|
|
*/
|
|
|
|
#include "BKE_global.hh"
|
|
#include "BKE_object.hh"
|
|
|
|
#include "BLI_rect.h"
|
|
#include "BLI_time.h"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
#include "DNA_ID.h"
|
|
#include "DNA_lightprobe_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
|
|
#include "IMB_imbuf_types.hh"
|
|
|
|
#include "RE_pipeline.h"
|
|
|
|
#include "eevee_engine.h"
|
|
#include "eevee_instance.hh"
|
|
|
|
#include "DNA_particle_types.h"
|
|
|
|
#include "draw_common.hh"
|
|
#include "draw_view_data.hh"
|
|
|
|
namespace blender::eevee {
|
|
|
|
void *Instance::debug_scope_render_sample = nullptr;
|
|
void *Instance::debug_scope_irradiance_setup = nullptr;
|
|
void *Instance::debug_scope_irradiance_sample = nullptr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Initialization
|
|
*
|
|
* Initialization functions need to be called once at the start of a frame.
|
|
* Active camera, render extent and enabled render passes are immutable until next init.
|
|
* This takes care of resizing output buffers and view in case a parameter changed.
|
|
* IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources.
|
|
* Any attempt to do so will likely produce use after free situations.
|
|
* \{ */
|
|
|
|
void Instance::init(const int2 &output_res,
|
|
const rcti *output_rect,
|
|
const rcti *visible_rect,
|
|
RenderEngine *render_,
|
|
Depsgraph *depsgraph_,
|
|
Object *camera_object_,
|
|
const RenderLayer *render_layer_,
|
|
View *drw_view_,
|
|
const View3D *v3d_,
|
|
const RegionView3D *rv3d_)
|
|
{
|
|
render = render_;
|
|
depsgraph = depsgraph_;
|
|
camera_orig_object = camera_object_;
|
|
render_layer = render_layer_;
|
|
drw_view = drw_view_;
|
|
v3d = v3d_;
|
|
rv3d = rv3d_;
|
|
manager = DRW_manager_get();
|
|
update_eval_members();
|
|
|
|
info_ = "";
|
|
|
|
shaders_are_ready_ = shaders.is_ready(is_image_render());
|
|
if (!shaders_are_ready_) {
|
|
skip_render_ = true;
|
|
return;
|
|
}
|
|
|
|
if (is_viewport()) {
|
|
if (assign_if_different(debug_mode, (eDebugMode)G.debug_value)) {
|
|
sampling.reset();
|
|
}
|
|
if (output_res != film.display_extent_get()) {
|
|
sampling.reset();
|
|
}
|
|
if (output_rect) {
|
|
int2 offset = int2(output_rect->xmin, output_rect->ymin);
|
|
int2 extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
|
|
if (offset != film.get_data().offset || extent != film.get_data().extent) {
|
|
sampling.reset();
|
|
}
|
|
}
|
|
if (assign_if_different(overlays_enabled_, v3d && !(v3d->flag2 & V3D_HIDE_OVERLAYS))) {
|
|
sampling.reset();
|
|
}
|
|
if (is_painting()) {
|
|
sampling.reset();
|
|
}
|
|
if (is_navigating() && scene->eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT) {
|
|
sampling.reset();
|
|
}
|
|
}
|
|
|
|
sampling.init(scene);
|
|
camera.init();
|
|
film.init(output_res, output_rect);
|
|
render_buffers.init();
|
|
ambient_occlusion.init();
|
|
velocity.init();
|
|
raytracing.init();
|
|
depth_of_field.init();
|
|
shadows.init();
|
|
motion_blur.init();
|
|
main_view.init();
|
|
light_probes.init();
|
|
planar_probes.init();
|
|
/* Irradiance Cache needs reflection probes to be initialized. */
|
|
sphere_probes.init();
|
|
volume_probes.init();
|
|
volume.init();
|
|
lookdev.init(visible_rect);
|
|
|
|
/* Pre-compile specialization constants in parallel (if supported). */
|
|
shaders.precompile_specializations(
|
|
render_buffers.data.shadow_id, shadows.get_data().ray_count, shadows.get_data().step_count);
|
|
shaders_are_ready_ = shaders.is_ready(is_image_render()) || !film.is_valid_render_extent();
|
|
skip_render_ = !shaders_are_ready_ || !film.is_valid_render_extent();
|
|
}
|
|
|
|
void Instance::init_light_bake(Depsgraph *depsgraph, draw::Manager *manager)
|
|
{
|
|
this->depsgraph = depsgraph;
|
|
this->manager = manager;
|
|
camera_orig_object = nullptr;
|
|
render = nullptr;
|
|
render_layer = nullptr;
|
|
drw_view = nullptr;
|
|
v3d = nullptr;
|
|
rv3d = nullptr;
|
|
update_eval_members();
|
|
|
|
is_light_bake = true;
|
|
debug_mode = (eDebugMode)G.debug_value;
|
|
info_ = "";
|
|
|
|
shaders.is_ready(true);
|
|
|
|
sampling.init(scene);
|
|
camera.init();
|
|
/* Film isn't used but init to avoid side effects in other module. */
|
|
rcti empty_rect{0, 0, 0, 0};
|
|
film.init(int2(1), &empty_rect);
|
|
render_buffers.init();
|
|
velocity.init();
|
|
depth_of_field.init();
|
|
shadows.init();
|
|
main_view.init();
|
|
light_probes.init();
|
|
planar_probes.init();
|
|
/* Irradiance Cache needs reflection probes to be initialized. */
|
|
sphere_probes.init();
|
|
volume_probes.init();
|
|
volume.init();
|
|
lookdev.init(&empty_rect);
|
|
}
|
|
|
|
void Instance::set_time(float time)
|
|
{
|
|
BLI_assert(render);
|
|
DRW_render_set_time(render, depsgraph, floorf(time), fractf(time));
|
|
update_eval_members();
|
|
}
|
|
|
|
void Instance::update_eval_members()
|
|
{
|
|
scene = DEG_get_evaluated_scene(depsgraph);
|
|
view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
camera_eval_object = (camera_orig_object) ?
|
|
DEG_get_evaluated_object(depsgraph, camera_orig_object) :
|
|
nullptr;
|
|
}
|
|
|
|
void Instance::view_update()
|
|
{
|
|
if (is_viewport()) {
|
|
sampling.reset();
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Sync
|
|
*
|
|
* Sync will gather data from the scene that can change over a time step (i.e: motion steps).
|
|
* IMPORTANT: xxx.sync() functions area responsible for creating DRW resources as
|
|
* well as querying temp texture pool. All DRWPasses should be ready by the end end_sync().
|
|
* \{ */
|
|
|
|
void Instance::begin_sync()
|
|
{
|
|
if (skip_render_) {
|
|
return;
|
|
}
|
|
|
|
/* Needs to be first for sun light parameters. */
|
|
world.sync();
|
|
|
|
materials.begin_sync();
|
|
velocity.begin_sync(); /* NOTE: Also syncs camera. */
|
|
lights.begin_sync();
|
|
shadows.begin_sync();
|
|
volume.begin_sync();
|
|
pipelines.begin_sync();
|
|
cryptomatte.begin_sync();
|
|
sphere_probes.begin_sync();
|
|
light_probes.begin_sync();
|
|
|
|
depth_of_field.sync();
|
|
raytracing.sync();
|
|
motion_blur.sync();
|
|
hiz_buffer.sync();
|
|
main_view.sync();
|
|
film.sync();
|
|
ambient_occlusion.sync();
|
|
volume_probes.sync();
|
|
lookdev.sync();
|
|
|
|
use_surfaces = (view_layer->layflag & SCE_LAY_SOLID) != 0;
|
|
use_curves = (view_layer->layflag & SCE_LAY_STRAND) != 0;
|
|
use_volumes = (view_layer->layflag & SCE_LAY_VOLUMES) != 0;
|
|
|
|
if (is_light_bake) {
|
|
/* Do not use render layer visibility during bake.
|
|
* NOTE: This is arbitrary and could be changed if needed. */
|
|
use_surfaces = use_curves = use_volumes = true;
|
|
}
|
|
|
|
if (is_viewport() && velocity.camera_has_motion()) {
|
|
sampling.reset();
|
|
}
|
|
}
|
|
|
|
void Instance::object_sync(ObjectRef &ob_ref)
|
|
{
|
|
if (skip_render_) {
|
|
return;
|
|
}
|
|
|
|
Object *ob = ob_ref.object;
|
|
const bool is_renderable_type = ELEM(ob->type,
|
|
OB_CURVES,
|
|
OB_GREASE_PENCIL,
|
|
OB_MESH,
|
|
OB_POINTCLOUD,
|
|
OB_VOLUME,
|
|
OB_LAMP,
|
|
OB_LIGHTPROBE);
|
|
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
|
|
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
|
|
(ob->type == OB_MESH);
|
|
const bool object_is_visible = DRW_object_is_renderable(ob) &&
|
|
(ob_visibility & OB_VISIBLE_SELF) != 0;
|
|
|
|
if (!is_renderable_type || (!partsys_is_visible && !object_is_visible)) {
|
|
return;
|
|
}
|
|
|
|
ObjectHandle &ob_handle = sync.sync_object(ob_ref);
|
|
|
|
if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) {
|
|
auto sync_hair =
|
|
[&](ObjectHandle hair_handle, ModifierData &md, ParticleSystem &particle_sys) {
|
|
ResourceHandle _res_handle = manager->resource_handle_for_psys(ob_ref,
|
|
ob->object_to_world());
|
|
sync.sync_curves(ob, hair_handle, ob_ref, _res_handle, &md, &particle_sys);
|
|
};
|
|
foreach_hair_particle_handle(ob, ob_handle, sync_hair);
|
|
}
|
|
|
|
if (object_is_visible) {
|
|
switch (ob->type) {
|
|
case OB_LAMP:
|
|
lights.sync_light(ob, ob_handle);
|
|
break;
|
|
case OB_MESH:
|
|
if (!sync.sync_sculpt(ob, ob_handle, ob_ref)) {
|
|
sync.sync_mesh(ob, ob_handle, ob_ref);
|
|
}
|
|
break;
|
|
case OB_POINTCLOUD:
|
|
sync.sync_point_cloud(ob, ob_handle, ob_ref);
|
|
break;
|
|
case OB_VOLUME:
|
|
sync.sync_volume(ob, ob_handle, ob_ref);
|
|
break;
|
|
case OB_CURVES:
|
|
sync.sync_curves(ob, ob_handle, ob_ref);
|
|
break;
|
|
case OB_LIGHTPROBE:
|
|
light_probes.sync_probe(ob, ob_handle);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Instance::object_sync_render(void *instance_,
|
|
Object *ob,
|
|
RenderEngine *engine,
|
|
Depsgraph *depsgraph)
|
|
{
|
|
UNUSED_VARS(engine, depsgraph);
|
|
Instance &inst = *reinterpret_cast<Instance *>(instance_);
|
|
ObjectRef ob_ref = DRW_object_ref_get(ob);
|
|
inst.object_sync(ob_ref);
|
|
}
|
|
|
|
void Instance::end_sync()
|
|
{
|
|
if (skip_render_) {
|
|
return;
|
|
}
|
|
|
|
velocity.end_sync();
|
|
volume.end_sync(); /* Needs to be before shadows. */
|
|
shadows.end_sync(); /* Needs to be before lights. */
|
|
lights.end_sync();
|
|
sampling.end_sync();
|
|
subsurface.end_sync();
|
|
film.end_sync();
|
|
cryptomatte.end_sync();
|
|
pipelines.end_sync();
|
|
light_probes.end_sync();
|
|
sphere_probes.end_sync();
|
|
planar_probes.end_sync();
|
|
|
|
uniform_data.push_update();
|
|
|
|
depsgraph_last_update_ = DEG_get_update_count(depsgraph);
|
|
}
|
|
|
|
void Instance::render_sync()
|
|
{
|
|
/* TODO: Remove old draw manager calls. */
|
|
DRW_cache_restart();
|
|
|
|
manager->begin_sync();
|
|
|
|
draw::hair_init();
|
|
draw::curves_init();
|
|
|
|
begin_sync();
|
|
|
|
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
|
|
|
|
draw::hair_update(*manager);
|
|
draw::curves_update(*manager);
|
|
draw::hair_free();
|
|
draw::curves_free();
|
|
|
|
velocity.geometry_steps_fill();
|
|
|
|
end_sync();
|
|
|
|
manager->end_sync();
|
|
|
|
/* TODO: Remove old draw manager calls. */
|
|
DRW_curves_update(*manager);
|
|
}
|
|
|
|
bool Instance::needs_lightprobe_sphere_passes() const
|
|
{
|
|
return sphere_probes.update_probes_this_sample_;
|
|
}
|
|
|
|
bool Instance::do_lightprobe_sphere_sync() const
|
|
{
|
|
return (materials.queued_shaders_count == 0) && needs_lightprobe_sphere_passes();
|
|
}
|
|
|
|
bool Instance::needs_planar_probe_passes() const
|
|
{
|
|
return planar_probes.update_probes_;
|
|
}
|
|
|
|
bool Instance::do_planar_probe_sync() const
|
|
{
|
|
return (materials.queued_shaders_count == 0) && needs_planar_probe_passes();
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Rendering
|
|
* \{ */
|
|
|
|
void Instance::render_sample()
|
|
{
|
|
if (sampling.finished_viewport()) {
|
|
film.display();
|
|
lookdev.display();
|
|
return;
|
|
}
|
|
|
|
/* Motion blur may need to do re-sync after a certain number of sample. */
|
|
if (!is_viewport() && sampling.do_render_sync()) {
|
|
render_sync();
|
|
while (materials.queued_shaders_count > 0) {
|
|
/* Leave some time for shaders to compile. */
|
|
BLI_time_sleep_ms(50);
|
|
/** WORKAROUND: Re-sync to check if all shaders are already compiled. */
|
|
render_sync();
|
|
}
|
|
}
|
|
|
|
DebugScope debug_scope(debug_scope_render_sample, "EEVEE.render_sample");
|
|
|
|
sampling.step();
|
|
|
|
capture_view.render_world();
|
|
capture_view.render_probes();
|
|
|
|
main_view.render();
|
|
|
|
lookdev_view.render();
|
|
|
|
motion_blur.step();
|
|
}
|
|
|
|
void Instance::render_read_result(RenderLayer *render_layer, const char *view_name)
|
|
{
|
|
eViewLayerEEVEEPassType pass_bits = film.enabled_passes_get();
|
|
|
|
for (auto i : IndexRange(EEVEE_RENDER_PASS_MAX_BIT + 1)) {
|
|
eViewLayerEEVEEPassType pass_type = eViewLayerEEVEEPassType(pass_bits & (1 << i));
|
|
if (pass_type == 0) {
|
|
continue;
|
|
}
|
|
|
|
Vector<std::string> pass_names = Film::pass_to_render_pass_names(pass_type, view_layer);
|
|
for (int64_t pass_offset : IndexRange(pass_names.size())) {
|
|
RenderPass *rp = RE_pass_find_by_name(
|
|
render_layer, pass_names[pass_offset].c_str(), view_name);
|
|
if (!rp) {
|
|
continue;
|
|
}
|
|
float *result = film.read_pass(pass_type, pass_offset);
|
|
|
|
if (result) {
|
|
BLI_mutex_lock(&render->update_render_passes_mutex);
|
|
/* WORKAROUND: We use texture read to avoid using a frame-buffer to get the render result.
|
|
* However, on some implementation, we need a buffer with a few extra bytes for the read to
|
|
* happen correctly (see #GLTexture::read()). So we need a custom memory allocation. */
|
|
/* Avoid `memcpy()`, replace the pointer directly. */
|
|
RE_pass_set_buffer_data(rp, result);
|
|
BLI_mutex_unlock(&render->update_render_passes_mutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* AOVs. */
|
|
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
|
if ((aov->flag & AOV_CONFLICT) != 0) {
|
|
continue;
|
|
}
|
|
RenderPass *rp = RE_pass_find_by_name(render_layer, aov->name, view_name);
|
|
if (!rp) {
|
|
continue;
|
|
}
|
|
float *result = film.read_aov(aov);
|
|
|
|
if (result) {
|
|
BLI_mutex_lock(&render->update_render_passes_mutex);
|
|
/* WORKAROUND: We use texture read to avoid using a frame-buffer to get the render result.
|
|
* However, on some implementation, we need a buffer with a few extra bytes for the read to
|
|
* happen correctly (see #GLTexture::read()). So we need a custom memory allocation. */
|
|
/* Avoid #memcpy(), replace the pointer directly. */
|
|
RE_pass_set_buffer_data(rp, result);
|
|
BLI_mutex_unlock(&render->update_render_passes_mutex);
|
|
}
|
|
}
|
|
|
|
/* The vector pass is initialized to weird values. Set it to neutral value if not rendered. */
|
|
if ((pass_bits & EEVEE_RENDER_PASS_VECTOR) == 0) {
|
|
for (const std::string &vector_pass_name :
|
|
Film::pass_to_render_pass_names(EEVEE_RENDER_PASS_VECTOR, view_layer))
|
|
{
|
|
RenderPass *vector_rp = RE_pass_find_by_name(
|
|
render_layer, vector_pass_name.c_str(), view_name);
|
|
if (vector_rp) {
|
|
memset(vector_rp->ibuf->float_buffer.data,
|
|
0,
|
|
sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Interface
|
|
* \{ */
|
|
|
|
void Instance::render_frame(RenderEngine *engine, RenderLayer *render_layer, const char *view_name)
|
|
{
|
|
if (skip_render_) {
|
|
if (!info_.empty()) {
|
|
RE_engine_set_error_message(engine, info_.c_str());
|
|
info_ = "";
|
|
}
|
|
return;
|
|
}
|
|
/* TODO: Break on RE_engine_test_break(engine) */
|
|
while (!sampling.finished()) {
|
|
this->render_sample();
|
|
|
|
if ((sampling.sample_index() == 1) || ((sampling.sample_index() % 25) == 0) ||
|
|
sampling.finished())
|
|
{
|
|
/* TODO: Use `fmt`. */
|
|
std::string re_info = "Rendering " + std::to_string(sampling.sample_index()) + " / " +
|
|
std::to_string(sampling.sample_count()) + " samples";
|
|
RE_engine_update_stats(engine, nullptr, re_info.c_str());
|
|
}
|
|
|
|
/* Perform render step between samples to allow
|
|
* flushing of freed GPUBackend resources. */
|
|
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
|
|
GPU_flush();
|
|
}
|
|
GPU_render_step();
|
|
|
|
#if 0
|
|
/* TODO(fclem) print progression. */
|
|
RE_engine_update_progress(engine, float(sampling.sample_index()) / float(sampling.sample_count()));
|
|
/* TODO(fclem): Does not currently work. But would be better to just display to 2D view like
|
|
* cycles does. */
|
|
if (G.background == false && first_read) {
|
|
/* Allow to preview the first sample. */
|
|
/* TODO(fclem): Might want to not do this during animation render to avoid too much stall. */
|
|
this->render_read_result(render_layer, view_name);
|
|
first_read = false;
|
|
DRW_render_context_disable(render->re);
|
|
/* Allow the 2D viewport to grab the ticket mutex to display the render. */
|
|
DRW_render_context_enable(render->re);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
this->film.cryptomatte_sort();
|
|
|
|
this->render_read_result(render_layer, view_name);
|
|
|
|
if (!info_.empty()) {
|
|
RE_engine_set_error_message(
|
|
engine, RPT_("Errors during render. See the System Console for more info."));
|
|
printf("%s", info_.c_str());
|
|
info_ = "";
|
|
}
|
|
}
|
|
|
|
void Instance::draw_viewport()
|
|
{
|
|
if (skip_render_) {
|
|
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
|
GPU_framebuffer_clear_color_depth(dfbl->default_fb, float4(0.0f), 1.0f);
|
|
if (!shaders_are_ready_) {
|
|
info_append_i18n("Compiling EEVEE engine shaders");
|
|
DRW_viewport_request_redraw();
|
|
}
|
|
return;
|
|
}
|
|
|
|
render_sample();
|
|
velocity.step_swap();
|
|
|
|
if (this->film.is_viewport_compositor_enabled()) {
|
|
this->film.write_viewport_compositor_passes();
|
|
}
|
|
|
|
/* Do not request redraw during viewport animation to lock the frame-rate to the animation
|
|
* playback rate. This is in order to preserve motion blur aspect and also to avoid TAA reset
|
|
* that can show flickering. */
|
|
if (!sampling.finished_viewport() && !DRW_state_is_playback()) {
|
|
DRW_viewport_request_redraw();
|
|
}
|
|
|
|
if (materials.queued_shaders_count > 0) {
|
|
info_append_i18n("Compiling shaders ({} remaining)", materials.queued_shaders_count);
|
|
|
|
if (!GPU_use_parallel_compilation() &&
|
|
GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL))
|
|
{
|
|
info_append_i18n(
|
|
"Increasing Preferences > System > Max Shader Compilation Subprocesses may improve "
|
|
"compilation time.");
|
|
}
|
|
DRW_viewport_request_redraw();
|
|
}
|
|
else if (materials.queued_optimize_shaders_count > 0) {
|
|
info_append_i18n("Optimizing shaders ({} remaining)", materials.queued_optimize_shaders_count);
|
|
}
|
|
}
|
|
|
|
void Instance::draw_viewport_image_render()
|
|
{
|
|
if (skip_render_) {
|
|
return;
|
|
}
|
|
while (!sampling.finished_viewport()) {
|
|
this->render_sample();
|
|
}
|
|
velocity.step_swap();
|
|
|
|
if (this->film.is_viewport_compositor_enabled()) {
|
|
this->film.write_viewport_compositor_passes();
|
|
}
|
|
}
|
|
|
|
void Instance::store_metadata(RenderResult *render_result)
|
|
{
|
|
if (skip_render_) {
|
|
return;
|
|
}
|
|
cryptomatte.store_metadata(render_result);
|
|
}
|
|
|
|
void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
|
|
{
|
|
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
|
|
|
|
#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
|
|
if (view_layer->passflag & (SCE_PASS_##name)) { \
|
|
RE_engine_register_pass( \
|
|
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
|
} \
|
|
((void)0)
|
|
#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
|
|
if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
|
|
RE_engine_register_pass( \
|
|
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
|
} \
|
|
((void)0)
|
|
|
|
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
|
|
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
|
|
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
|
|
CHECK_PASS_LEGACY(POSITION, SOCK_VECTOR, 3, "XYZ");
|
|
CHECK_PASS_LEGACY(VECTOR, SOCK_VECTOR, 4, "XYZW");
|
|
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
|
|
CHECK_PASS_EEVEE(TRANSPARENT, SOCK_RGBA, 4, "RGBA");
|
|
|
|
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
|
if ((aov->flag & AOV_CONFLICT) != 0) {
|
|
continue;
|
|
}
|
|
switch (aov->type) {
|
|
case AOV_TYPE_COLOR:
|
|
RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
|
|
break;
|
|
case AOV_TYPE_VALUE:
|
|
RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* NOTE: Name channels lowercase `rgba` so that compression rules check in OpenEXR DWA code uses
|
|
* lossless compression. Reportedly this naming is the only one which works good from the
|
|
* interoperability point of view. Using `xyzw` naming is not portable. */
|
|
auto register_cryptomatte_passes = [&](eViewLayerCryptomatteFlags cryptomatte_layer,
|
|
eViewLayerEEVEEPassType eevee_pass) {
|
|
if (view_layer->cryptomatte_flag & cryptomatte_layer) {
|
|
for (const std::string &pass_name : Film::pass_to_render_pass_names(eevee_pass, view_layer))
|
|
{
|
|
RE_engine_register_pass(
|
|
engine, scene, view_layer, pass_name.c_str(), 4, "rgba", SOCK_RGBA);
|
|
}
|
|
}
|
|
};
|
|
register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_OBJECT, EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
|
|
register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_ASSET, EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
|
|
register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_MATERIAL,
|
|
EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
|
|
}
|
|
|
|
void Instance::light_bake_irradiance(
|
|
Object &probe,
|
|
FunctionRef<void()> context_enable,
|
|
FunctionRef<void()> context_disable,
|
|
FunctionRef<bool()> stop,
|
|
FunctionRef<void(LightProbeGridCacheFrame *, float progress)> result_update)
|
|
{
|
|
BLI_assert(is_baking());
|
|
|
|
auto custom_pipeline_wrapper = [&](FunctionRef<void()> callback) {
|
|
context_enable();
|
|
DRW_custom_pipeline_begin(&draw_engine_eevee_next_type, depsgraph);
|
|
callback();
|
|
DRW_custom_pipeline_end();
|
|
context_disable();
|
|
};
|
|
|
|
auto context_wrapper = [&](FunctionRef<void()> callback) {
|
|
context_enable();
|
|
callback();
|
|
context_disable();
|
|
};
|
|
|
|
volume_probes.bake.init(probe);
|
|
|
|
custom_pipeline_wrapper([&]() {
|
|
this->render_sync();
|
|
while (materials.queued_shaders_count > 0) {
|
|
/* Leave some time for shaders to compile. */
|
|
BLI_time_sleep_ms(50);
|
|
/** WORKAROUND: Re-sync to check if all shaders are already compiled. */
|
|
this->render_sync();
|
|
}
|
|
/* Sampling module needs to be initialized to computing lighting. */
|
|
sampling.init(probe);
|
|
sampling.step();
|
|
|
|
DebugScope debug_scope(debug_scope_irradiance_setup, "EEVEE.irradiance_setup");
|
|
|
|
capture_view.render_world();
|
|
|
|
volume_probes.bake.surfels_create(probe);
|
|
|
|
if (volume_probes.bake.should_break()) {
|
|
return;
|
|
}
|
|
|
|
volume_probes.bake.surfels_lights_eval();
|
|
|
|
volume_probes.bake.clusters_build();
|
|
volume_probes.bake.irradiance_offset();
|
|
});
|
|
|
|
if (volume_probes.bake.should_break()) {
|
|
return;
|
|
}
|
|
|
|
sampling.init(probe);
|
|
while (!sampling.finished()) {
|
|
context_wrapper([&]() {
|
|
DebugScope debug_scope(debug_scope_irradiance_sample, "EEVEE.irradiance_sample");
|
|
|
|
/* Batch ray cast by pack of 16. Avoids too much overhead of the update function & context
|
|
* switch. */
|
|
/* TODO(fclem): Could make the number of iteration depend on the computation time. */
|
|
for (int i = 0; i < 16 && !sampling.finished(); i++) {
|
|
sampling.step();
|
|
|
|
volume_probes.bake.raylists_build();
|
|
volume_probes.bake.propagate_light();
|
|
volume_probes.bake.irradiance_capture();
|
|
}
|
|
|
|
LightProbeGridCacheFrame *cache_frame;
|
|
if (sampling.finished()) {
|
|
cache_frame = volume_probes.bake.read_result_packed();
|
|
}
|
|
else {
|
|
/* TODO(fclem): Only do this read-back if needed. But it might be tricky to know when. */
|
|
cache_frame = volume_probes.bake.read_result_unpacked();
|
|
}
|
|
|
|
float progress = sampling.sample_index() / float(sampling.sample_count());
|
|
result_update(cache_frame, progress);
|
|
});
|
|
|
|
if (stop()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::eevee
|