EEVEE-Next: Scene sync fixes and improvements

Rely on the depsgraph to detect scene updates,
using the `view_update` callback.
Remove `SceneHandle` and `scene_sync`.
Remove `reset_recalc_flag`.
Remove most `sampling.reset()` calls and their related logic,
and move the remaining ones to `Instance`.
Re-sync lights on `light_threshold` changes.

Pull Request: https://projects.blender.org/blender/blender/pulls/115758
This commit is contained in:
Miguel Pozo
2023-12-04 15:57:51 +01:00
parent 8bfc412490
commit 56be404b0f
15 changed files with 89 additions and 183 deletions

View File

@@ -53,17 +53,13 @@ void DepthOfField::init()
return;
}
/* Reminder: These are parameters not interpolated by motion blur. */
int update = 0;
int sce_flag = sce_eevee.flag;
update += assign_if_different(do_jitter_, (sce_flag & SCE_EEVEE_DOF_JITTER) != 0);
update += assign_if_different(user_overblur_, sce_eevee.bokeh_overblur / 100.0f);
update += assign_if_different(fx_max_coc_, sce_eevee.bokeh_max_size);
update += assign_if_different(data_.scatter_color_threshold, sce_eevee.bokeh_threshold);
update += assign_if_different(data_.scatter_neighbor_max_color, sce_eevee.bokeh_neighbor_max);
update += assign_if_different(data_.bokeh_blades, float(camera->dof.aperture_blades));
if (update > 0) {
inst_.sampling.reset();
}
do_jitter_ = (sce_flag & SCE_EEVEE_DOF_JITTER) != 0;
user_overblur_ = sce_eevee.bokeh_overblur / 100.0f;
fx_max_coc_ = sce_eevee.bokeh_max_size;
data_.scatter_color_threshold = sce_eevee.bokeh_threshold;
data_.scatter_neighbor_max_color = sce_eevee.bokeh_neighbor_max;
data_.bokeh_blades = float(camera->dof.aperture_blades);
}
void DepthOfField::sync()
@@ -74,31 +70,21 @@ void DepthOfField::sync()
reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
nullptr;
int update = 0;
if (camera_data == nullptr || (camera_data->dof.flag & CAM_DOF_ENABLED) == 0) {
update += assign_if_different(jitter_radius_, 0.0f);
update += assign_if_different(fx_radius_, 0.0f);
if (update > 0) {
inst_.sampling.reset();
}
jitter_radius_ = 0.0f;
fx_radius_ = 0.0f;
return;
}
float2 anisotropic_scale = {clamp_f(1.0f / camera_data->dof.aperture_ratio, 1e-5f, 1.0f),
clamp_f(camera_data->dof.aperture_ratio, 1e-5f, 1.0f)};
update += assign_if_different(data_.bokeh_anisotropic_scale, anisotropic_scale);
update += assign_if_different(data_.bokeh_rotation, camera_data->dof.aperture_rotation);
update += assign_if_different(focus_distance_,
BKE_camera_object_dof_distance(camera_object_eval));
data_.bokeh_anisotropic_scale = anisotropic_scale;
data_.bokeh_rotation = camera_data->dof.aperture_rotation;
focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval);
data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
float fstop = max_ff(camera_data->dof.aperture_fstop, 1e-5f);
if (update) {
inst_.sampling.reset();
}
float aperture = 1.0f / (2.0f * fstop);
if (camera.is_perspective()) {
aperture *= camera_data->lens * 1e-3f;
@@ -147,11 +133,8 @@ void DepthOfField::sync()
fx_radius = 0.0f;
}
update += assign_if_different(jitter_radius_, jitter_radius);
update += assign_if_different(fx_radius_, fx_radius);
if (update > 0) {
inst_.sampling.reset();
}
jitter_radius_ = jitter_radius;
fx_radius_ = fx_radius;
if (fx_radius_ == 0.0f) {
return;

View File

@@ -112,6 +112,13 @@ static void eevee_cache_finish(void *vedata)
reinterpret_cast<EEVEE_Data *>(vedata)->instance->end_sync();
}
static void eevee_view_update(void *vedata)
{
if (eevee::Instance *instance = reinterpret_cast<EEVEE_Data *>(vedata)->instance) {
instance->view_update();
}
}
static void eevee_engine_free()
{
eevee::ShaderModule::module_free();
@@ -177,7 +184,7 @@ DrawEngineType draw_engine_eevee_next_type = {
/*cache_populate*/ &eevee_cache_populate,
/*cache_finish*/ &eevee_cache_finish,
/*draw_scene*/ &eevee_draw_scene,
/*view_update*/ nullptr,
/*view_update*/ &eevee_view_update,
/*id_update*/ nullptr,
/*render_to_image*/ &eevee_render_to_image,
/*store_metadata*/ &eevee_store_metadata,

View File

@@ -213,34 +213,27 @@ void Film::init(const int2 &extent, const rcti *output_rect)
{
/* Enable passes that need to be rendered. */
eViewLayerEEVEEPassType render_passes = eViewLayerEEVEEPassType(0);
if (inst_.is_viewport()) {
/* Viewport Case. */
render_passes = eViewLayerEEVEEPassType(inst_.v3d->shading.render_pass);
enabled_passes_ = eViewLayerEEVEEPassType(inst_.v3d->shading.render_pass);
if (inst_.overlays_enabled() || inst_.gpencil_engine_enabled) {
/* Overlays and Grease Pencil needs the depth for correct compositing.
* Using the render pass ensure we store the center depth. */
render_passes |= EEVEE_RENDER_PASS_Z;
enabled_passes_ |= EEVEE_RENDER_PASS_Z;
}
}
else {
/* Render Case. */
render_passes = enabled_passes(inst_.view_layer);
enabled_passes_ = enabled_passes(inst_.view_layer);
}
/* Filter obsolete passes. */
render_passes &= ~(EEVEE_RENDER_PASS_UNUSED_8 | EEVEE_RENDER_PASS_BLOOM);
enabled_passes_ &= ~(EEVEE_RENDER_PASS_UNUSED_8 | EEVEE_RENDER_PASS_BLOOM);
if (scene_eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
/* Disable motion vector pass if motion blur is enabled. */
render_passes &= ~EEVEE_RENDER_PASS_VECTOR;
}
/* TODO(@fclem): Can't we rely on depsgraph update notification? */
if (assign_if_different(enabled_passes_, render_passes)) {
sampling.reset();
enabled_passes_ &= ~EEVEE_RENDER_PASS_VECTOR;
}
}
{
@@ -252,35 +245,29 @@ void Film::init(const int2 &extent, const rcti *output_rect)
display_extent = extent;
FilmData data = data_;
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data.offset = int2(output_rect->xmin, output_rect->ymin);
data.extent_inv = 1.0f / float2(data.extent);
data_.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data_.offset = int2(output_rect->xmin, output_rect->ymin);
data_.extent_inv = 1.0f / float2(data_.extent);
/* TODO(fclem): parameter hidden in experimental.
* We need to figure out LOD bias first in order to preserve texture crispiness. */
data.scaling_factor = 1;
data.render_extent = math::divide_ceil(extent, int2(data.scaling_factor));
data.render_offset = data.offset;
data_.scaling_factor = 1;
data_.render_extent = math::divide_ceil(extent, int2(data_.scaling_factor));
data_.render_offset = data_.offset;
if (inst_.camera.overscan() != 0.0f) {
int2 overscan = int2(inst_.camera.overscan() * math::max(UNPACK2(data.render_extent)));
data.render_extent += overscan * 2;
data.render_offset += overscan;
int2 overscan = int2(inst_.camera.overscan() * math::max(UNPACK2(data_.render_extent)));
data_.render_extent += overscan * 2;
data_.render_offset += overscan;
}
/* Disable filtering if sample count is 1. */
data.filter_radius = (sampling.sample_count() == 1) ? 0.0f :
clamp_f(scene.r.gauss, 0.0f, 100.0f);
data.cryptomatte_samples_len = inst_.view_layer->cryptomatte_levels;
data_.filter_radius = (sampling.sample_count() == 1) ? 0.0f :
clamp_f(scene.r.gauss, 0.0f, 100.0f);
data_.cryptomatte_samples_len = inst_.view_layer->cryptomatte_levels;
data.background_opacity = (scene.r.alphamode == R_ALPHAPREMUL) ? 0.0f : 1.0f;
data_.background_opacity = (scene.r.alphamode == R_ALPHAPREMUL) ? 0.0f : 1.0f;
if (inst_.is_viewport() && false /* TODO(fclem): StudioLight */) {
data.background_opacity = inst_.v3d->shading.studiolight_background;
}
FilmData &data_prev_ = data_;
if (assign_if_different(data_prev_, data)) {
sampling.reset();
data_.background_opacity = inst_.v3d->shading.studiolight_background;
}
const eViewLayerEEVEEPassType data_passes = EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL |
@@ -402,7 +389,6 @@ void Film::init(const int2 &extent, const rcti *output_rect)
1);
if (reset > 0) {
sampling.reset();
data_.use_history = 0;
data_.use_reprojection = 0;

View File

@@ -58,11 +58,17 @@ void Instance::init(const int2 &output_res,
rv3d = rv3d_;
manager = DRW_manager_get();
info = "";
if (assign_if_different(debug_mode, (eDebugMode)G.debug_value)) {
sampling.reset();
}
info = "";
if (output_res != film.display_extent_get()) {
sampling.reset();
}
if (assign_if_different(overlays_enabled_, v3d && !(v3d->flag2 & V3D_HIDE_OVERLAYS))) {
sampling.reset();
}
update_eval_members();
@@ -132,6 +138,12 @@ void Instance::update_eval_members()
nullptr;
}
void Instance::view_update()
{
sampling.reset();
sync.view_update();
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -157,8 +169,6 @@ void Instance::begin_sync()
gpencil_engine_enabled = false;
scene_sync();
depth_of_field.sync();
raytracing.sync();
motion_blur.sync();
@@ -169,20 +179,9 @@ void Instance::begin_sync()
render_buffers.sync();
ambient_occlusion.sync();
irradiance_cache.sync();
}
void Instance::scene_sync()
{
SceneHandle &sc_handle = sync.sync_scene(scene);
sc_handle.reset_recalc_flag();
/* This refers specifically to the Scene camera that can be accessed
* via View Layer Attribute nodes, rather than the actual render camera. */
if (scene->camera != nullptr) {
ObjectHandle &ob_handle = sync.sync_object({scene->camera, nullptr, nullptr});
ob_handle.reset_recalc_flag();
if (is_viewport() && velocity.camera_has_motion()) {
sampling.reset();
}
}
@@ -253,8 +252,6 @@ void Instance::object_sync(Object *ob)
break;
}
}
ob_handle.reset_recalc_flag();
}
/* Wrapper to use with DRW_render_object_iter. */

View File

@@ -55,6 +55,7 @@ class Instance {
UniformDataBuf global_ubo_;
uint64_t depsgraph_last_update_ = 0;
bool overlays_enabled_;
public:
ShaderModule &shaders;
@@ -156,6 +157,8 @@ class Instance {
const View3D *v3d = nullptr,
const RegionView3D *rv3d = nullptr);
void view_update();
void begin_sync();
void object_sync(Object *ob);
void end_sync();
@@ -200,7 +203,7 @@ class Instance {
bool overlays_enabled() const
{
return v3d && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0);
return overlays_enabled_;
}
bool use_scene_lights() const
@@ -260,7 +263,6 @@ class Instance {
void render_sample();
void render_read_result(RenderLayer *render_layer, const char *view_name);
void scene_sync();
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
void update_eval_members();

View File

@@ -265,7 +265,12 @@ void LightModule::begin_sync()
/* In begin_sync so it can be animated. */
if (assign_if_different(light_threshold_, max_ff(1e-16f, inst_.scene->eevee.light_threshold))) {
inst_.sampling.reset();
/* All local lights need to be re-sync. */
for (Light &light : light_map_.values()) {
if (!ELEM(light.type, LIGHT_SUN, LIGHT_SUN_ORTHO)) {
light.initialized = false;
}
}
}
sun_lights_len_ = 0;
@@ -325,11 +330,6 @@ void LightModule::end_sync()
/* This scene data buffer is then immutable after this point. */
light_buf_.push_update();
/* Update sampling on deletion or un-hiding (use_scene_lights). */
if (assign_if_different(light_map_size_, light_map_.size())) {
inst_.sampling.reset();
}
/* If exceeding the limit, just trim off the excess to avoid glitchy rendering. */
if (sun_lights_len_ + local_lights_len_ > CULLING_MAX_ITEM) {
sun_lights_len_ = min_ii(sun_lights_len_, CULLING_MAX_ITEM);

View File

@@ -114,8 +114,6 @@ class LightModule {
Map<ObjectKey, Light> light_map_;
/** Flat array sent to GPU, populated from light_map_. Source buffer for light culling. */
LightDataBuf light_buf_ = {"Lights_no_cull"};
/** Recorded size of light_map_ (after pruning) to detect deletion. */
int64_t light_map_size_ = 0;
/** Luminous intensity to consider the light boundary at. Used for culling. */
float light_threshold_ = 0.01f;
/** If false, will prevent all scene lights from being synced. */

View File

@@ -200,6 +200,8 @@ MaterialPass MaterialModule::material_pass_get(Object *ob,
inst_.manager->register_layer_attributes(matpass.gpumat);
if (GPU_material_recalc_flag_get(matpass.gpumat)) {
/* TODO(Miguel Pozo): This is broken, it consumes the flag,
* but GPUMats can be shared across viewports.*/
inst_.sampling.reset();
}

View File

@@ -58,10 +58,7 @@ float4 PlanarProbe::reflection_clip_plane_get()
void PlanarProbeModule::init()
{
if (assign_if_different(update_probes_, !probes_.is_empty())) {
instance_.sampling.reset();
}
update_probes_ = !probes_.is_empty();
do_display_draw_ = false;
}

View File

@@ -207,7 +207,6 @@ void ReflectionProbeModule::begin_sync()
update_probes_this_sample_ = false;
if (update_probes_next_sample_) {
update_probes_this_sample_ = true;
instance_.sampling.reset();
}
{
@@ -389,10 +388,6 @@ bool ReflectionProbeModule::remove_unused_probes()
{
const int64_t removed_count = probes_.remove_if(
[](const ReflectionProbes::Item &item) { return !item.value.is_probe_used; });
if (removed_count > 0) {
instance_.sampling.reset();
}
return removed_count > 0;
}

View File

@@ -735,7 +735,6 @@ void ShadowModule::init()
::Scene &scene = *inst_.scene;
bool enabled = (scene.eevee.flag & SCE_EEVEE_SHADOW_ENABLED) != 0;
if (assign_if_different(enabled_, enabled)) {
inst_.sampling.reset();
/* Force light reset. */
for (Light &light : inst_.lights.light_map_.values()) {
light.initialized = false;
@@ -967,9 +966,6 @@ void ShadowModule::end_sync()
shadow_ob.used = false;
}
}
if (!past_casters_updated_.is_empty() || !curr_casters_updated_.is_empty()) {
inst_.sampling.reset();
}
past_casters_updated_.push_update();
curr_casters_updated_.push_update();

View File

@@ -30,14 +30,15 @@
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Draw Data
/** \name Recalc
*
* \{ */
static void draw_data_init_cb(DrawData *dd)
void SyncModule::view_update()
{
/* Object has just been created or was never evaluated by the engine. */
dd->recalc = ID_RECALC_ALL;
if (DEG_id_type_updated(inst_.depsgraph, ID_WO)) {
world_updated_ = true;
}
}
ObjectHandle &SyncModule::sync_object(const ObjectRef &ob_ref)
@@ -52,39 +53,15 @@ ObjectHandle &SyncModule::sync_object(const ObjectRef &ob_ref)
handle.recalc = inst_.get_recalc_flags(ob_ref);
if (handle.recalc != 0) {
inst_.sampling.reset();
}
return handle;
}
WorldHandle &SyncModule::sync_world(::World *world)
WorldHandle SyncModule::sync_world()
{
DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_next_type;
DrawData *dd = DRW_drawdata_ensure(
(ID *)world, owner, sizeof(eevee::WorldHandle), draw_data_init_cb, nullptr);
WorldHandle &eevee_dd = *reinterpret_cast<WorldHandle *>(dd);
const int recalc_flags = ID_RECALC_ALL;
if ((eevee_dd.recalc & recalc_flags) != 0) {
inst_.sampling.reset();
}
return eevee_dd;
}
SceneHandle &SyncModule::sync_scene(::Scene *scene)
{
DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_next_type;
DrawData *dd = DRW_drawdata_ensure(
(ID *)scene, owner, sizeof(eevee::SceneHandle), draw_data_init_cb, nullptr);
SceneHandle &eevee_dd = *reinterpret_cast<SceneHandle *>(dd);
const int recalc_flags = ID_RECALC_ALL;
if ((eevee_dd.recalc & recalc_flags) != 0) {
inst_.sampling.reset();
}
return eevee_dd;
WorldHandle handle;
handle.recalc = world_updated_ ? ID_RECALC_SHADING : 0;
world_updated_ = false;
return handle;
}
/** \} */

View File

@@ -98,36 +98,17 @@ class ObjectKey {
* \{ */
struct BaseHandle {
/* Accumulated recalc flags, which corresponds to ID->recalc flags. */
unsigned int recalc;
void reset_recalc_flag()
{
if (recalc != 0) {
recalc = 0;
}
}
};
struct ObjectHandle : public BaseHandle {
struct ObjectHandle : BaseHandle {
ObjectKey object_key;
};
struct WorldHandle : public DrawData {
void reset_recalc_flag()
{
if (recalc != 0) {
recalc = 0;
}
}
struct WorldHandle : public BaseHandle {
};
struct SceneHandle : public DrawData {
void reset_recalc_flag()
{
if (recalc != 0) {
recalc = 0;
}
}
struct SceneHandle : public BaseHandle {
};
class SyncModule {
@@ -136,13 +117,16 @@ class SyncModule {
Map<ObjectKey, ObjectHandle> ob_handles = {};
bool world_updated_;
public:
SyncModule(Instance &inst) : inst_(inst){};
~SyncModule(){};
void view_update();
ObjectHandle &sync_object(const ObjectRef &ob_ref);
WorldHandle &sync_world(::World *world);
SceneHandle &sync_scene(::Scene *scene);
WorldHandle sync_world();
void sync_mesh(Object *ob,
ObjectHandle &ob_handle,

View File

@@ -93,8 +93,6 @@ static void step_object_sync_render(void *instance,
if (object_is_visible) {
inst.velocity.step_object_sync(ob, ob_handle.object_key, resource_handle, ob_handle.recalc);
}
ob_handle.reset_recalc_flag();
}
void VelocityModule::step_sync(eVelocityStep step, float time)
@@ -239,11 +237,6 @@ bool VelocityModule::step_object_sync(Object *ob,
return false;
}
/* TODO(@fclem): Reset sampling here? Should ultimately be covered by depsgraph update tags. */
/* NOTE(Miguel Pozo): Disable, since is_deform is always true for objects with particle
* modifiers, and this causes the renderer to get stuck at sample 1. */
// inst_.sampling.reset();
return true;
}
@@ -360,14 +353,6 @@ void VelocityModule::end_sync()
}
}
if (deleted_obj.size() > 0) {
inst_.sampling.reset();
}
if (inst_.is_viewport() && camera_has_motion()) {
inst_.sampling.reset();
}
for (auto &key : deleted_obj) {
velocity_map.remove(key);
}

View File

@@ -84,9 +84,8 @@ void World::sync()
if (bl_world) {
/* Detect world update before overriding it. */
WorldHandle &wo_handle = inst_.sync.sync_world(bl_world);
has_update = (wo_handle.recalc != 0);
wo_handle.reset_recalc_flag();
WorldHandle wo_handle = inst_.sync.sync_world();
has_update = wo_handle.recalc != 0;
}
/* Sync volume first since its result can override the surface world. */
@@ -120,14 +119,12 @@ void World::sync()
inst_.reflection_probes.sync_world(bl_world);
if (has_update) {
inst_.reflection_probes.do_world_update_set(true);
inst_.sampling.reset();
}
/* We have to manually test here because we have overrides. */
::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id);
if (assign_if_different(prev_original_world, orig_world)) {
inst_.reflection_probes.do_world_update_set(true);
inst_.sampling.reset();
}
GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree, MAT_PIPE_DEFERRED);