EEVEE-Next: HiZBuffer: Add support for 2nd layer

This allows for parallel processing of refraction.
Also fix a limitation of using AO node in
refraction materials.

This is needed for the grouping of raytracing
passes.

This increases VRAM consumption a bit (8MB for fullHD
frame) but has no impact on performance.

This include a needed fix to the `draw::Texture::swap`.

### Later work
- Limit the memory overhead to the cases where it is needed.

Pull Request: https://projects.blender.org/blender/blender/pulls/115912
This commit is contained in:
Clément Foucault
2023-12-07 22:04:38 +01:00
committed by Clément Foucault
parent a355488993
commit 71f6228876
5 changed files with 82 additions and 54 deletions
@@ -23,9 +23,12 @@ void HiZBuffer::sync()
int2 dispatch_size = math::divide_ceil(hiz_extent, int2(HIZ_GROUP_SIZE));
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
hiz_tx_.ensure_2d(GPU_R32F, hiz_extent, usage, nullptr, HIZ_MIP_COUNT);
hiz_tx_.ensure_mip_views();
GPU_texture_mipmap_mode(hiz_tx_, true, false);
for ([[maybe_unused]] const int i : IndexRange(hiz_tx_.size())) {
hiz_tx_.current().ensure_2d(GPU_R32F, hiz_extent, usage, nullptr, HIZ_MIP_COUNT);
hiz_tx_.current().ensure_mip_views();
GPU_texture_mipmap_mode(hiz_tx_.current(), true, false);
hiz_tx_.swap();
}
data_.uv_scale = float2(render_extent) / float2(hiz_extent);
@@ -36,13 +39,13 @@ void HiZBuffer::sync()
pass.bind_ssbo("finished_tile_counter", atomic_tile_counter_);
/* TODO(fclem): Should be a parameter to avoid confusion. */
pass.bind_texture("depth_tx", &src_tx_, with_filter);
pass.bind_image("out_mip_0", hiz_tx_.mip_view(0));
pass.bind_image("out_mip_1", hiz_tx_.mip_view(1));
pass.bind_image("out_mip_2", hiz_tx_.mip_view(2));
pass.bind_image("out_mip_3", hiz_tx_.mip_view(3));
pass.bind_image("out_mip_4", hiz_tx_.mip_view(4));
pass.bind_image("out_mip_5", hiz_tx_.mip_view(5));
pass.bind_image("out_mip_6", hiz_tx_.mip_view(6));
pass.bind_image("out_mip_0", &hiz_mip_ref_[0]);
pass.bind_image("out_mip_1", &hiz_mip_ref_[1]);
pass.bind_image("out_mip_2", &hiz_mip_ref_[2]);
pass.bind_image("out_mip_3", &hiz_mip_ref_[3]);
pass.bind_image("out_mip_4", &hiz_mip_ref_[4]);
pass.bind_image("out_mip_5", &hiz_mip_ref_[5]);
pass.bind_image("out_mip_6", &hiz_mip_ref_[6]);
/* TODO(@fclem): There might be occasions where we might not want to
* copy mip 0 for performance reasons if there is no need for it. */
pass.push_constant("update_mip_0", true);
@@ -56,13 +59,13 @@ void HiZBuffer::sync()
pass.bind_ssbo("finished_tile_counter", atomic_tile_counter_);
/* TODO(fclem): Should be a parameter to avoid confusion. */
pass.bind_texture("depth_layered_tx", &src_tx_, with_filter);
pass.bind_image("out_mip_0", hiz_tx_.mip_view(0));
pass.bind_image("out_mip_1", hiz_tx_.mip_view(1));
pass.bind_image("out_mip_2", hiz_tx_.mip_view(2));
pass.bind_image("out_mip_3", hiz_tx_.mip_view(3));
pass.bind_image("out_mip_4", hiz_tx_.mip_view(4));
pass.bind_image("out_mip_5", hiz_tx_.mip_view(5));
pass.bind_image("out_mip_6", hiz_tx_.mip_view(6));
pass.bind_image("out_mip_0", &hiz_mip_ref_[0]);
pass.bind_image("out_mip_1", &hiz_mip_ref_[1]);
pass.bind_image("out_mip_2", &hiz_mip_ref_[2]);
pass.bind_image("out_mip_3", &hiz_mip_ref_[3]);
pass.bind_image("out_mip_4", &hiz_mip_ref_[4]);
pass.bind_image("out_mip_5", &hiz_mip_ref_[5]);
pass.bind_image("out_mip_6", &hiz_mip_ref_[6]);
/* TODO(@fclem): There might be occasions where we might not want to
* copy mip 0 for performance reasons if there is no need for it. */
pass.push_constant("update_mip_0", true);
@@ -87,6 +90,9 @@ void HiZBuffer::update()
}
src_tx_ = *src_tx_ptr_;
for (const int i : IndexRange(HIZ_MIP_COUNT)) {
hiz_mip_ref_[i] = hiz_tx_.current().mip_view(i);
}
if (layer_id_ == -1) {
inst_.manager->submit(hiz_update_ps_);
@@ -28,8 +28,16 @@ class HiZBuffer {
private:
Instance &inst_;
/** The texture containing the hiz mip chain. */
Texture hiz_tx_ = {"hiz_tx_"};
/** Contains depth pyramid of the current pass and the previous pass. */
SwapChain<Texture, 2> hiz_tx_;
/** References to the textures in the swap-chain. */
/* Closest surface depth of the current layer. */
GPUTexture *hiz_front_ref_tx_ = nullptr;
/* Closest surface depth of the layer below. */
GPUTexture *hiz_back_ref_tx_ = nullptr;
/** References to the mip views of the current (front) HiZ texture. */
std::array<GPUTexture *, HIZ_MIP_COUNT> hiz_mip_ref_;
/**
* Atomic counter counting the number of tile that have finished down-sampling.
* The last one will process the last few mip level.
@@ -45,8 +53,8 @@ class HiZBuffer {
/** Dirty flag to check if the update is necessary. */
bool is_dirty_ = true;
/** Reference to the depth texture to downsample. */
GPUTexture *src_tx_;
GPUTexture **src_tx_ptr_;
GPUTexture *src_tx_ = nullptr;
GPUTexture **src_tx_ptr_ = nullptr;
HiZData &data_;
@@ -59,7 +67,8 @@ class HiZBuffer {
void sync();
/**
* Set source texture for the hiz downsampling.
* Set source texture for the hiz down-sampling.
* Need to be called once at the start of a pipeline or view.
*/
void set_source(GPUTexture **texture, int layer = -1)
{
@@ -68,7 +77,21 @@ class HiZBuffer {
}
/**
* Tag the buffer for update if needed.
* Swap front and back layer.
* Internally set front layer to be dirty.
* IMPORTANT: Before the second swap (and the second update)
* the content of the back hi-z buffer is undefined.
*/
void swap_layer()
{
hiz_tx_.swap();
hiz_back_ref_tx_ = hiz_tx_.previous();
hiz_front_ref_tx_ = hiz_tx_.current();
set_dirty();
}
/**
* Tag the front buffer for update if needed.
*/
void set_dirty()
{
@@ -76,7 +99,7 @@ class HiZBuffer {
}
/**
* Update the content of the HiZ buffer with the depth render target.
* Update the content of the HiZ buffer with the source depth set by `set_source()`.
* Noop if the buffer has not been tagged as dirty.
* Should be called before each passes that needs to read the hiz buffer.
*/
@@ -84,14 +107,17 @@ class HiZBuffer {
void debug_draw(View &view, GPUFrameBuffer *view_fb);
void bind_resources(DRWShadingGroup *grp)
{
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &hiz_tx_);
}
enum class Type {
/* Previous layer depth (ex: For refraction). */
BACK,
/* Previous layer depth. */
FRONT,
};
template<typename PassType> void bind_resources(PassType &pass)
template<typename PassType> void bind_resources(PassType &pass, Type type = Type::FRONT)
{
pass.bind_texture(HIZ_TEX_SLOT, &hiz_tx_);
pass.bind_texture(HIZ_TEX_SLOT,
(type == Type::FRONT) ? &hiz_front_ref_tx_ : &hiz_back_ref_tx_);
}
};
@@ -655,18 +655,8 @@ void DeferredLayer::render(View &main_view,
GPU_framebuffer_bind(prepass_fb);
inst_.manager->submit(prepass_ps_, render_view);
if (closure_bits_ & CLOSURE_AMBIENT_OCCLUSION) {
/* If the shader needs Ambient Occlusion, we need to update the HiZ here. */
if (do_screen_space_refraction) {
/* TODO(fclem): This update conflicts with the refraction screen tracing which need the depth
* behind the refractive surface. In this case, we do not update the Hi-Z and only consider
* surfaces already in the Hi-Z buffer for the ambient occlusion computation. This might be
* solved (if really problematic) by having another copy of the Hi-Z buffer. */
}
else {
inst_.hiz_buffer.update();
}
}
inst_.hiz_buffer.swap_layer();
inst_.hiz_buffer.update();
if (/* FIXME(fclem): Vulkan doesn't implement load / store config yet. */
GPU_backend_get_type() == GPU_BACKEND_VULKAN)
@@ -710,15 +700,6 @@ void DeferredLayer::render(View &main_view,
inst_.irradiance_cache.set_view(render_view);
RayTraceResult refract_result = inst_.raytracing.trace(rt_buffer,
radiance_behind_tx_,
render_view.persmat(),
closure_bits_,
CLOSURE_REFRACTION,
main_view,
render_view,
!do_screen_space_refraction);
/* Only update the HiZ after refraction tracing. */
inst_.hiz_buffer.update();
@@ -733,6 +714,15 @@ void DeferredLayer::render(View &main_view,
GPU_framebuffer_bind(combined_fb);
inst_.manager->submit(eval_light_ps_, render_view);
RayTraceResult refract_result = inst_.raytracing.trace(rt_buffer,
radiance_behind_tx_,
render_view.persmat(),
closure_bits_,
CLOSURE_REFRACTION,
main_view,
render_view,
!do_screen_space_refraction);
RayTraceResult diffuse_result = inst_.raytracing.trace(rt_buffer,
radiance_feedback_tx_,
radiance_feedback_persmat_,
@@ -119,6 +119,8 @@ void RayTraceModule::sync()
sub.dispatch(ray_dispatch_buf_);
sub.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
HiZBuffer::Type hiz_type = (&pass == &trace_refract_ps_) ? HiZBuffer::Type::BACK :
HiZBuffer::Type::FRONT;
pass.shader_set(inst_.shaders.static_shader_get(SHADER_VARIATION(RAY_TRACE_SCREEN_, type)));
pass.bind_ssbo("tiles_coord_buf", &ray_tiles_buf_);
pass.bind_image("ray_data_img", &ray_data_tx_);
@@ -127,7 +129,7 @@ void RayTraceModule::sync()
pass.bind_texture("depth_tx", &depth_tx);
pass.bind_image("ray_radiance_img", &ray_radiance_tx_);
inst_.bind_uniform_data(&pass);
inst_.hiz_buffer.bind_resources(pass);
inst_.hiz_buffer.bind_resources(pass, hiz_type);
inst_.sampling.bind_resources(pass);
inst_.irradiance_cache.bind_resources(pass);
inst_.reflection_probes.bind_resources(pass);
@@ -975,8 +975,12 @@ class Texture : NonCopyable {
*/
static void swap(Texture &a, Texture &b)
{
SWAP(GPUTexture *, a.tx_, b.tx_);
SWAP(const char *, a.name_, b.name_);
std::swap(a.tx_, b.tx_);
std::swap(a.name_, b.name_);
std::swap(a.stencil_view_, b.stencil_view_);
std::swap(a.layer_range_view_, b.layer_range_view_);
std::swap(a.mip_views_, b.mip_views_);
std::swap(a.layer_views_, b.layer_views_);
}
private: