EEVEE-Next: Add compute shader to downsample sphere probe

This has no functional change except that it might speed
up probe updates since only updated pixels are processed.

Ref #118256
This commit is contained in:
Clément Foucault
2024-02-14 18:45:02 +01:00
parent 1c3c1e7776
commit da72cdee5e
9 changed files with 86 additions and 17 deletions

View File

@@ -564,6 +564,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl
engines/eevee_next/shaders/eevee_ray_trace_screen_lib.glsl
engines/eevee_next/shaders/eevee_ray_types_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_convolve_comp.glsl
engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl

View File

@@ -97,7 +97,7 @@ struct SphereProbeAtlasCoord {
{
SphereProbePixelArea coord;
coord.extent = atlas_extent >> (subdivision_lvl + mip_lvl);
coord.offset = (area_location() * coord.extent) >> mip_lvl;
coord.offset = area_location() * coord.extent;
coord.layer = atlas_layer;
return coord;
}

View File

@@ -50,6 +50,17 @@ void SphereProbeModule::begin_sync()
pass.push_constant("probe_brightness_clamp", probe_brightness_clamp);
pass.dispatch(&dispatch_probe_pack_);
}
{
PassSimple &pass = convolve_ps_;
pass.init();
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_CONVOLVE));
pass.bind_image("in_atlas_mip_img", &convolve_input_);
pass.bind_image("out_atlas_mip_img", &convolve_output_);
pass.push_constant("write_coord_packed", reinterpret_cast<int4 *>(&probe_write_coord_));
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
pass.dispatch(&dispatch_probe_convolve_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
PassSimple &pass = update_irradiance_ps_;
pass.init();
@@ -85,6 +96,7 @@ bool SphereProbeModule::ensure_atlas()
nullptr,
SPHERE_PROBE_MIPMAP_LEVELS))
{
probes_tx_.ensure_mip_views();
/* TODO(fclem): Clearing means that we need to render all probes again.
* If existing data exists, copy it using `CopyImageSubData`. */
probes_tx_.clear(float4(0.0f));
@@ -187,8 +199,19 @@ void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoo
probe_write_coord_ = atlas_coord.as_write_coord(max_resolution_, 0);
probe_mip_level_ = atlas_coord.subdivision_lvl;
dispatch_probe_pack_ = int3(int2(ceil_division(resolution, SPHERE_PROBE_GROUP_SIZE)), 1);
instance_.manager->submit(remap_ps_);
/* Populate the mip levels */
for (auto i : IndexRange(SPHERE_PROBE_MIPMAP_LEVELS - 1)) {
convolve_input_ = probes_tx_.mip_view(i);
convolve_output_ = probes_tx_.mip_view(i + 1);
probe_write_coord_ = atlas_coord.as_write_coord(max_resolution_, i + 1);
int out_mip_res = resolution >> (i + 1);
dispatch_probe_convolve_ = int3(int2(ceil_division(out_mip_res, SPHERE_PROBE_GROUP_SIZE)), 1);
instance_.manager->submit(convolve_ps_);
}
/* Sync with atlas usage for shading. */
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
}
void SphereProbeModule::update_world_irradiance()
@@ -198,11 +221,6 @@ void SphereProbeModule::update_world_irradiance()
instance_.volume_probes.do_update_world_ = true;
}
void SphereProbeModule::update_probes_texture_mipmaps()
{
GPU_texture_update_mipmap_chain(probes_tx_);
}
void SphereProbeModule::set_view(View & /*view*/)
{
Vector<SphereProbe *> probe_active;

View File

@@ -53,9 +53,17 @@ class SphereProbeModule {
PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"};
/** Extract irradiance information from the world. */
PassSimple update_irradiance_ps_ = {"Probe.UpdateIrradiance"};
/** Copy volume probe irradiance for the center of sphere probes. */
PassSimple select_ps_ = {"Probe.Select"};
/** Convolve the octahedral map to fill the Mip-map levels. */
PassSimple convolve_ps_ = {"Probe.Convolve"};
/** Input mip level for the convolution. */
GPUTexture *convolve_input_ = nullptr;
/** Output mip level for the convolution. */
GPUTexture *convolve_output_ = nullptr;
int3 dispatch_probe_pack_ = int3(1);
int3 dispatch_probe_convolve_ = int3(1);
int3 dispatch_probe_select_ = int3(1);
/**
@@ -167,7 +175,6 @@ class SphereProbeModule {
* Internal processing passes.
*/
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord);
void update_probes_texture_mipmaps();
void update_world_irradiance();
void sync_display(Vector<SphereProbe *> &probe_active);

View File

@@ -214,6 +214,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_lightprobe_irradiance_ray";
case LIGHTPROBE_IRRADIANCE_LOAD:
return "eevee_lightprobe_irradiance_load";
case SPHERE_PROBE_CONVOLVE:
return "eevee_reflection_probe_convolve";
case SPHERE_PROBE_REMAP:
return "eevee_reflection_probe_remap";
case SPHERE_PROBE_UPDATE_IRRADIANCE:

View File

@@ -104,9 +104,10 @@ enum eShaderType {
RAY_TRACE_PLANAR,
RAY_TRACE_SCREEN,
SPHERE_PROBE_CONVOLVE,
SPHERE_PROBE_REMAP,
SPHERE_PROBE_UPDATE_IRRADIANCE,
SPHERE_PROBE_SELECT,
SPHERE_PROBE_UPDATE_IRRADIANCE,
SHADOW_CLIPMAP_CLEAR,
SHADOW_DEBUG,

View File

@@ -263,7 +263,6 @@ void CaptureView::render_world()
}
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
inst_.sphere_probes.update_probes_texture_mipmaps();
}
if (update_info->do_world_irradiance_update) {
@@ -277,10 +276,8 @@ void CaptureView::render_probes()
{
Framebuffer prepass_fb;
View view = {"Capture.View"};
bool do_update_mipmap_chain = false;
while (const auto update_info = inst_.sphere_probes.probe_update_info_pop()) {
GPU_debug_group_begin("Probe.Capture");
do_update_mipmap_chain = true;
if (inst_.pipelines.data.is_probe_reflection != true) {
inst_.pipelines.data.is_probe_reflection = true;
@@ -334,11 +331,6 @@ void CaptureView::render_probes()
inst_.pipelines.data.is_probe_reflection = false;
inst_.uniform_data.push_update();
}
if (do_update_mipmap_chain) {
/* TODO: only update the regions that have been updated. */
inst_.sphere_probes.update_probes_texture_mipmaps();
}
}
/** \} */

View File

@@ -0,0 +1,39 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Shader to convert cube-map to octahedral projection. */
#pragma BLENDER_REQUIRE(eevee_octahedron_lib.glsl)
SphereProbePixelArea reinterpret_as_write_coord(ivec4 packed_coord)
{
SphereProbePixelArea unpacked;
unpacked.offset = packed_coord.xy;
unpacked.extent = packed_coord.z;
unpacked.layer = packed_coord.w;
return unpacked;
}
void main()
{
SphereProbePixelArea write_coord = reinterpret_as_write_coord(write_coord_packed);
/* Texel in probe. */
ivec2 local_texel = ivec2(gl_GlobalInvocationID.xy);
/* Exit when pixel being written doesn't fit in the area reserved for the probe. */
if (any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
return;
}
ivec2 texel_out = write_coord.offset + local_texel;
vec4 color = vec4(0.0);
color += imageLoad(in_atlas_mip_img, ivec3(texel_out * 2 + ivec2(0, 0), write_coord.layer));
color += imageLoad(in_atlas_mip_img, ivec3(texel_out * 2 + ivec2(1, 0), write_coord.layer));
color += imageLoad(in_atlas_mip_img, ivec3(texel_out * 2 + ivec2(0, 1), write_coord.layer));
color += imageLoad(in_atlas_mip_img, ivec3(texel_out * 2 + ivec2(1, 1), write_coord.layer));
color *= 0.25;
imageStore(out_atlas_mip_img, ivec3(texel_out, write_coord.layer), color);
}

View File

@@ -54,6 +54,15 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select)
.compute_source("eevee_reflection_probe_select_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_convolve)
.local_group_size(SPHERE_PROBE_GROUP_SIZE, SPHERE_PROBE_GROUP_SIZE)
.additional_info("eevee_shared")
.push_constant(Type::IVEC4, "write_coord_packed")
.image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_atlas_mip_img")
.image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_atlas_mip_img")
.compute_source("eevee_reflection_probe_convolve_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_INTERFACE_INFO(eevee_display_probe_reflection_iface, "")
.smooth(Type::VEC2, "lP")
.flat(Type::INT, "probe_index");