EEVEE Next: Motion Blur fixes

Fix motion blur for viewport re-projection and final image renders.

Pull Request: https://projects.blender.org/blender/blender/pulls/110114
This commit is contained in:
Miguel Pozo
2023-08-03 12:48:12 +02:00
committed by Clément Foucault
parent 0af370a62d
commit 9db289924f
13 changed files with 156 additions and 93 deletions

View File

@@ -478,7 +478,7 @@ void Film::end_sync()
data_.use_reprojection = inst_.sampling.interactive_mode();
/* Just bypass the reprojection and reset the accumulation. */
if (force_disable_reprojection_ && inst_.sampling.is_reset()) {
if (inst_.is_viewport() && force_disable_reprojection_ && inst_.sampling.is_reset()) {
data_.use_reprojection = false;
data_.use_history = false;
}

View File

@@ -197,26 +197,12 @@ void Instance::object_sync(Object *ob)
ObjectHandle &ob_handle = sync.sync_object(ob);
if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) {
int sub_key = 1;
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_ParticleSystem) {
ParticleSystem *particle_sys = reinterpret_cast<ParticleSystemModifierData *>(md)->psys;
ParticleSettings *part_settings = particle_sys->part;
const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
part_settings->draw_as;
if (draw_as != PART_DRAW_PATH ||
!DRW_object_is_visible_psys_in_active_context(ob, particle_sys)) {
continue;
}
ObjectHandle _ob_handle{};
_ob_handle.object_key = ObjectKey(ob_handle.object_key.ob, sub_key++);
_ob_handle.recalc = particle_sys->recalc;
ResourceHandle _res_handle = manager->resource_handle(float4x4(ob->object_to_world));
sync.sync_curves(ob, _ob_handle, _res_handle, md, particle_sys);
}
}
auto sync_hair =
[&](ObjectHandle hair_handle, ModifierData &md, ParticleSystem &particle_sys) {
ResourceHandle _res_handle = manager->resource_handle(float4x4(ob->object_to_world));
sync.sync_curves(ob, hair_handle, _res_handle, &md, &particle_sys);
};
foreach_hair_particle_handle(ob, ob_handle, sync_hair);
}
if (object_is_visible) {
@@ -282,6 +268,7 @@ void Instance::render_sync()
begin_sync();
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
velocity.geometry_steps_fill();
end_sync();
manager->end_sync();

View File

@@ -71,6 +71,7 @@ void MotionBlurModule::init()
* function is only called after rendering a sample. */
inst_.velocity.step_sync(STEP_PREVIOUS, time_steps_[0]);
inst_.velocity.step_sync(STEP_NEXT, time_steps_[2]);
/* Let the main sync loop handle the current step. */
}
inst_.set_time(time_steps_[1]);
}
@@ -141,8 +142,9 @@ void MotionBlurModule::sync()
{
/* Create max velocity tiles. */
PassSimple::Sub &sub = motion_blur_ps_.sub("TilesFlatten");
eShaderType shader = inst_.is_viewport() ? MOTION_BLUR_TILE_FLATTEN_VIEWPORT :
MOTION_BLUR_TILE_FLATTEN_RENDER;
eGPUTextureFormat vector_tx_format = inst_.render_buffers.vector_tx_format();
eShaderType shader = vector_tx_format == GPU_RG16F ? MOTION_BLUR_TILE_FLATTEN_RG :
MOTION_BLUR_TILE_FLATTEN_RGBA;
sub.shader_set(inst_.shaders.static_shader_get(shader));
sub.bind_ubo("motion_blur_buf", data_);
sub.bind_texture("depth_tx", &render_buffers.depth_tx);
@@ -210,9 +212,6 @@ void MotionBlurModule::render(View &view, GPUTexture **input_tx, GPUTexture **ou
}
}
was_navigating_ = DRW_state_is_navigating();
/* Change texture swizzling to avoid complexity in gather pass shader. */
GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgrg");
}
else {
data_.motion_scale = float2(1.0f);
@@ -235,17 +234,23 @@ void MotionBlurModule::render(View &view, GPUTexture **input_tx, GPUTexture **ou
tile_indirection_buf_.clear_to_zero();
const bool do_motion_vectors_swizzle = inst_.render_buffers.vector_tx_format() == GPU_RG16F;
if (do_motion_vectors_swizzle) {
/* Change texture swizzling to avoid complexity in gather pass shader. */
GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgrg");
}
inst_.manager->submit(motion_blur_ps_, view);
if (do_motion_vectors_swizzle) {
/* Reset swizzle since this texture might be reused in other places. */
GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgba");
}
tiles_tx_.release();
DRW_stats_group_end();
if (inst_.is_viewport()) {
/* Reset swizzle since this texture might be reused in other places. */
GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgba");
}
/* Swap buffers so that next effect has the right input. */
*input_tx = output_color_tx_;
*output_tx = input_color_tx_;

View File

@@ -70,17 +70,14 @@ void RenderBuffers::acquire(int2 extent)
depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8);
combined_tx.acquire(extent, color_format);
bool do_vector_render_pass = (enabled_passes & EEVEE_RENDER_PASS_VECTOR) ||
(inst_.motion_blur.postfx_enabled() && !inst_.is_viewport());
/* Only RG16F when only doing only reprojection or motion blur. */
eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F;
eGPUTextureUsage usage_attachment_read_write = GPU_TEXTURE_USAGE_ATTACHMENT |
GPU_TEXTURE_USAGE_SHADER_READ |
GPU_TEXTURE_USAGE_SHADER_WRITE;
/* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */
vector_tx.acquire(
extent, vector_format, usage_attachment_read_write | GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW);
vector_tx.acquire(extent,
vector_tx_format(),
usage_attachment_read_write | GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW);
int color_len = data.color_len + data.aovs.color_len;
int value_len = data.value_len + data.aovs.value_len;
@@ -119,4 +116,14 @@ void RenderBuffers::release()
cryptomatte_tx.release();
}
eGPUTextureFormat RenderBuffers::vector_tx_format()
{
const eViewLayerEEVEEPassType enabled_passes = inst_.film.enabled_passes_get();
bool do_vector_render_pass = (enabled_passes & EEVEE_RENDER_PASS_VECTOR) ||
(inst_.motion_blur.postfx_enabled() && !inst_.is_viewport());
/* Only RG16F when only doing only reprojection or motion blur. */
return do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F;
}
} // namespace blender::eevee

View File

@@ -63,6 +63,8 @@ class RenderBuffers {
/* Acquires (also ensures) the render buffer before rendering to them. */
void acquire(int2 extent);
void release();
eGPUTextureFormat vector_tx_format();
};
} // namespace blender::eevee

View File

@@ -100,10 +100,10 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_motion_blur_gather";
case MOTION_BLUR_TILE_DILATE:
return "eevee_motion_blur_tiles_dilate";
case MOTION_BLUR_TILE_FLATTEN_RENDER:
return "eevee_motion_blur_tiles_flatten_render";
case MOTION_BLUR_TILE_FLATTEN_VIEWPORT:
return "eevee_motion_blur_tiles_flatten_viewport";
case MOTION_BLUR_TILE_FLATTEN_RGBA:
return "eevee_motion_blur_tiles_flatten_rgba";
case MOTION_BLUR_TILE_FLATTEN_RG:
return "eevee_motion_blur_tiles_flatten_rg";
case DEBUG_SURFELS:
return "eevee_debug_surfels";
case DISPLAY_PROBE_GRID:

View File

@@ -72,8 +72,8 @@ enum eShaderType {
MOTION_BLUR_GATHER,
MOTION_BLUR_TILE_DILATE,
MOTION_BLUR_TILE_FLATTEN_RENDER,
MOTION_BLUR_TILE_FLATTEN_VIEWPORT,
MOTION_BLUR_TILE_FLATTEN_RGBA,
MOTION_BLUR_TILE_FLATTEN_RG,
REFLECTION_PROBE_REMAP,
REFLECTION_PROBE_UPDATE_IRRADIANCE,

View File

@@ -404,4 +404,28 @@ void SyncModule::sync_light_probe(Object *ob, ObjectHandle &ob_handle)
/** \} */
void foreach_hair_particle_handle(Object *ob, ObjectHandle ob_handle, HairHandleCallback callback)
{
int sub_key = 1;
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_ParticleSystem) {
ParticleSystem *particle_sys = reinterpret_cast<ParticleSystemModifierData *>(md)->psys;
ParticleSettings *part_settings = particle_sys->part;
const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
part_settings->draw_as;
if (draw_as != PART_DRAW_PATH ||
!DRW_object_is_visible_psys_in_active_context(ob, particle_sys)) {
continue;
}
ObjectHandle particle_sys_handle = {0};
particle_sys_handle.object_key = ObjectKey(ob_handle.object_key.ob, sub_key++);
particle_sys_handle.recalc = particle_sys->recalc;
callback(particle_sys_handle, *md, *particle_sys);
}
}
}
} // namespace blender::eevee

View File

@@ -180,6 +180,9 @@ class SyncModule {
void sync_light_probe(Object *ob, ObjectHandle &ob_handle);
};
using HairHandleCallback = FunctionRef<void(ObjectHandle, ModifierData &, ParticleSystem &)>;
void foreach_hair_particle_handle(Object *ob, ObjectHandle ob_handle, HairHandleCallback callback);
/** \} */
} // namespace blender::eevee

View File

@@ -15,6 +15,7 @@
#include "BKE_object.h"
#include "BLI_map.hh"
#include "DEG_depsgraph_query.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "DNA_rigidbody_types.h"
@@ -35,16 +36,17 @@ namespace blender::eevee {
void VelocityModule::init()
{
if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) != 0) {
if (!inst_.is_viewport() && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) &&
!inst_.motion_blur.postfx_enabled())
{
/* No motion blur and the vector pass was requested. Do the steps sync here. */
const Scene *scene = inst_.scene;
float initial_time = scene->r.cfra + scene->r.subframe;
step_sync(STEP_PREVIOUS, initial_time - 1.0f);
step_sync(STEP_NEXT, initial_time + 1.0f);
/* Let the main sync loop handle the current step. */
inst_.set_time(initial_time);
step_ = STEP_CURRENT;
/* Let the main sync loop handle the current step. */
}
/* For viewport, only previous motion is supported.
@@ -52,15 +54,44 @@ void VelocityModule::init()
next_step_ = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT;
}
static void step_object_sync_render(void *velocity,
/* Similar to Instance::object_sync, but only syncs velocity. */
static void step_object_sync_render(void *instance,
Object *ob,
RenderEngine * /*engine*/,
Depsgraph * /*depsgraph*/)
{
ObjectKey object_key(ob);
/* NOTE: Dummy resource handle since this will not be used for drawing. */
Instance &inst = *reinterpret_cast<Instance *>(instance);
const bool is_velocity_type = ELEM(
ob->type, OB_CURVES, OB_GPENCIL_LEGACY, OB_MESH, OB_POINTCLOUD);
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_velocity_type || (!partsys_is_visible && !object_is_visible)) {
return;
}
/* NOTE: Dummy resource handle since this won't be used for drawing. */
ResourceHandle resource_handle(0);
reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key, resource_handle);
ObjectHandle &ob_handle = inst.sync.sync_object(ob);
if (partsys_is_visible) {
auto sync_hair =
[&](ObjectHandle hair_handle, ModifierData &md, ParticleSystem &particle_sys) {
inst.velocity.step_object_sync(
ob, hair_handle.object_key, resource_handle, hair_handle.recalc, &md, &particle_sys);
};
foreach_hair_particle_handle(ob, ob_handle, sync_hair);
};
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)
@@ -69,7 +100,8 @@ void VelocityModule::step_sync(eVelocityStep step, float time)
step_ = step;
object_steps_usage[step_] = 0;
step_camera_sync();
DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render);
DRW_render_object_iter(&inst_, inst_.render, inst_.depsgraph, step_object_sync_render);
geometry_steps_fill();
}
void VelocityModule::step_camera_sync()
@@ -192,47 +224,45 @@ bool VelocityModule::step_object_sync(Object *ob,
return true;
}
void VelocityModule::geometry_steps_fill()
{
uint dst_ofs = 0;
for (VelocityGeometryData &geom : geometry_map.values()) {
uint src_len = GPU_vertbuf_get_vertex_len(geom.pos_buf);
geom.len = src_len;
geom.ofs = dst_ofs;
dst_ofs += src_len;
}
/* TODO(@fclem): Fail gracefully (disable motion blur + warning print) if
* `tot_len * sizeof(float4)` is greater than max SSBO size. */
geometry_steps[step_]->resize(max_ii(16, dst_ofs));
for (VelocityGeometryData &geom : geometry_map.values()) {
GPU_storagebuf_copy_sub_from_vertbuf(*geometry_steps[step_],
geom.pos_buf,
geom.ofs * sizeof(float4),
0,
geom.len * sizeof(float4));
}
/* Copy back the #VelocityGeometryIndex into #VelocityObjectData which are
* indexed using persistent keys (unlike geometries which are indexed by volatile ID). */
for (VelocityObjectData &vel : velocity_map.values()) {
const VelocityGeometryData &geom = geometry_map.lookup_default(vel.id, VelocityGeometryData());
vel.geo.len[step_] = geom.len;
vel.geo.ofs[step_] = geom.ofs;
/* Avoid reuse. */
vel.id = nullptr;
}
geometry_map.clear();
}
/**
* Moves next frame data to previous frame data. Nullify next frame data.
* IMPORTANT: This runs AFTER drawing in the viewport (so after `begin_sync()`) but BEFORE drawing
* in render mode (so before `begin_sync()`). In viewport the data will be used the next frame.
* In Render, moves the next frame data to previous frame data. Nullify next frame data.
* In Viewport, the current frame data will be used as previous frame data in the next frame.
*/
void VelocityModule::step_swap()
{
{
/* Now that vertex buffers are guaranteed to be updated, proceed with
* offset computation and copy into the geometry step buffer. */
uint dst_ofs = 0;
for (VelocityGeometryData &geom : geometry_map.values()) {
uint src_len = GPU_vertbuf_get_vertex_len(geom.pos_buf);
geom.len = src_len;
geom.ofs = dst_ofs;
dst_ofs += src_len;
}
/* TODO(@fclem): Fail gracefully (disable motion blur + warning print) if
* `tot_len * sizeof(float4)` is greater than max SSBO size. */
geometry_steps[step_]->resize(max_ii(16, dst_ofs));
for (VelocityGeometryData &geom : geometry_map.values()) {
GPU_storagebuf_copy_sub_from_vertbuf(*geometry_steps[step_],
geom.pos_buf,
geom.ofs * sizeof(float4),
0,
geom.len * sizeof(float4));
}
/* Copy back the #VelocityGeometryIndex into #VelocityObjectData which are
* indexed using persistent keys (unlike geometries which are indexed by volatile ID). */
for (VelocityObjectData &vel : velocity_map.values()) {
const VelocityGeometryData &geom = geometry_map.lookup_default(vel.id,
VelocityGeometryData());
vel.geo.len[step_] = geom.len;
vel.geo.ofs[step_] = geom.ofs;
/* Avoid reuse. */
vel.id = nullptr;
}
geometry_map.clear();
}
auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) {
std::swap(object_steps[step_a], object_steps[step_b]);
@@ -251,6 +281,7 @@ void VelocityModule::step_swap()
};
if (inst_.is_viewport()) {
geometry_steps_fill();
/* For viewport we only use the last rendered redraw as previous frame.
* We swap current with previous step at the end of a redraw.
* We do not support motion blur as it is rendered to avoid conflicting motions

View File

@@ -140,6 +140,10 @@ class VelocityModule {
/* Returns frame time difference between two steps. */
float step_time_delta_get(eVelocityStep start, eVelocityStep end) const;
/* Perform VelocityGeometryData offset computation and copy into the geometry step buffer.
* Should be called after all the vertex buffers have been updated by batch cache extraction. */
void geometry_steps_fill();
private:
bool object_has_velocity(const Object *ob);
bool object_is_deform(const Object *ob);

View File

@@ -51,7 +51,7 @@ void main()
vec2 uv = (vec2(texel) + 0.5) / render_size;
float depth = texelFetch(depth_tx, texel, 0).r;
vec4 motion = velocity_resolve(imageLoad(velocity_img, texel), uv, depth);
#ifdef FLATTEN_VIEWPORT
#ifdef FLATTEN_RG
/* imageLoad does not perform the swizzling like sampler does. Do it manually. */
motion = motion.xyxy;
#endif

View File

@@ -13,13 +13,13 @@ GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten)
.image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_img")
.compute_source("eevee_motion_blur_flatten_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_viewport)
GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_rg)
.do_static_compilation(true)
.define("FLATTEN_VIEWPORT")
.define("FLATTEN_RG")
.image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img")
.additional_info("eevee_motion_blur_tiles_flatten");
GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_render)
GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_rgba)
.do_static_compilation(true)
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img")
.additional_info("eevee_motion_blur_tiles_flatten");