From 408c5d72810ef4bb5f5f8a7ce814164f8858a375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 19 Mar 2023 21:33:55 +0100 Subject: [PATCH 01/58] EEVEE-Next: Irradiance-Cache: Add surfel placement Spawning surfels uses a new shader pipeline (called `capture`) to avoid the complexity of modifying the deferred or forward pipeline. The shaders are light to compile and should only be compiled on bake. This is very WIP: the surfel projection box is hardcoded and the 3 projections are done every frame. The surfels are placed in a regular grid because of the raster pipeline used to spawn them. Note: While this is really fast, I am not sure this will scale well for highly detailed lighting conditions. In the case where the surfel density is too low, the aliasing might create really poor result. --- source/blender/draw/CMakeLists.txt | 1 + .../draw/engines/eevee_next/eevee_defines.hh | 4 + .../draw/engines/eevee_next/eevee_instance.cc | 2 + .../eevee_next/eevee_irradiance_cache.cc | 139 +++++++++++++----- .../eevee_next/eevee_irradiance_cache.hh | 20 ++- .../draw/engines/eevee_next/eevee_material.cc | 5 + .../draw/engines/eevee_next/eevee_material.hh | 11 +- .../draw/engines/eevee_next/eevee_pipeline.cc | 33 +++++ .../draw/engines/eevee_next/eevee_pipeline.hh | 29 +++- .../draw/engines/eevee_next/eevee_shader.cc | 6 +- .../engines/eevee_next/eevee_shader_shared.hh | 45 +++++- .../draw/engines/eevee_next/eevee_sync.cc | 1 + .../draw/engines/eevee_next/eevee_view.cc | 1 - .../shaders/eevee_debug_surfels_frag.glsl | 13 +- .../shaders/eevee_debug_surfels_vert.glsl | 3 +- .../shaders/eevee_surf_capture_frag.glsl | 53 +++++++ .../infos/eevee_irradiance_cache_info.hh | 3 +- .../shaders/infos/eevee_material_info.hh | 8 + 18 files changed, 319 insertions(+), 58 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 9706cecb54d..37fd7271dea 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -491,6 +491,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl + engines/eevee_next/shaders/eevee_surf_capture_frag.glsl engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl engines/eevee_next/shaders/eevee_surf_depth_frag.glsl engines/eevee_next/shaders/eevee_surf_forward_frag.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index c54d537db61..9f05794c07d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -122,6 +122,10 @@ #define LIGHT_BUF_SLOT 1 #define LIGHT_ZBIN_BUF_SLOT 2 #define LIGHT_TILE_BUF_SLOT 3 +/* Only during surface capture. */ +#define SURFEL_BUF_SLOT 4 +/* Only during surface capture. */ +#define CAPTURE_BUF_SLOT 5 /* Only during surface shading. */ #define RBUFS_AOV_BUF_SLOT 5 /* Only during shadow rendering. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index b0a40d27982..0dc76355853 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -249,6 +249,8 @@ void Instance::render_sample() main_view.render(); + irradiance_cache.create_surfels(); + motion_blur.step(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 890ef35c3b5..7ea4b08caf7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -1,38 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_rand.hh" #include "eevee_instance.hh" #include "eevee_irradiance_cache.hh" namespace blender::eevee { -void IrradianceCache::generate_random_surfels() -{ - const int surfels_len = 256; - debug_surfels.resize(surfels_len); - - RandomNumberGenerator rng; - rng.seed(0); - - for (DebugSurfel &surfel : debug_surfels) { - float3 random = rng.get_unit_float3(); - surfel.position = random * 3.0f; - surfel.normal = random; - surfel.color = float4(rng.get_float(), rng.get_float(), rng.get_float(), 1.0f); - } - - debug_surfels.push_update(); -} - void IrradianceCache::init() { - if (debug_surfels_sh_ == nullptr) { - debug_surfels_sh_ = inst_.shaders.static_shader_get(DEBUG_SURFELS); - } - - /* TODO: Remove this. */ - generate_random_surfels(); } void IrradianceCache::sync() @@ -42,24 +17,114 @@ void IrradianceCache::sync() void IrradianceCache::debug_pass_sync() { - if (inst_.debug_mode == eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS) { - debug_surfels_ps_.init(); - debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS_EQUAL); - debug_surfels_ps_.shader_set(debug_surfels_sh_); - debug_surfels_ps_.bind_ssbo("surfels_buf", debug_surfels); - debug_surfels_ps_.push_constant("surfel_radius", 0.25f); - debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, debug_surfels.size(), 4); + if (!ELEM(inst_.debug_mode, + eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL, + eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) { + return; } + debug_surfels_ps_.init(); + debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_LESS_EQUAL); + debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); + debug_surfels_ps_.bind_ssbo("surfels_buf", surfels_buf_); + debug_surfels_ps_.push_constant("surfel_radius", 0.5f / 4.0f); + debug_surfels_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); + debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, surfels_buf_.size(), 4); } void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb) { - if (inst_.debug_mode == eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS) { - inst_.info = "Debug Mode: Irradiance Cache Surfels"; - GPU_framebuffer_bind(view_fb); - inst_.manager->submit(debug_surfels_ps_, view); + switch (inst_.debug_mode) { + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: + inst_.info = "Debug Mode: Surfels Normal"; + break; + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: + inst_.info = "Debug Mode: Surfels Irradiance"; + break; + default: + /* Nothing to display. */ + return; } + + GPU_framebuffer_bind(view_fb); + inst_.manager->submit(debug_surfels_ps_, view); +} + +void IrradianceCache::create_surfels() +{ + /** + * We rasterize the scene along the 3 axes. Each generated fragment will write a surface element + * so raster grid density need to match the desired surfel density. We do a first pass to know + * how much surfel to allocate then render again to create the surfels. + */ + using namespace blender::math; + + /* Attachment-less frame-buffer. */ + empty_raster_fb_.ensure(int2(40 * 4)); + + /** We could use multi-view rendering here to avoid multiple submissions but it is unlikely to + * make any difference. The bottleneck is still the light propagation loop. */ + auto render_axis = [&](Axis axis) { + /* TODO(fclem): get scene bounds GPU or CPU side. Or use the irradiance grid extents. */ + float4x4 winmat = math::projection::orthographic(-20.0f, 20.0f, -20.0f, 20.0f, -20.0f, 20.0f); + + CartesianBasis basis = from_orthonormal_axes(AxisSigned(axis).next_after(), axis); + view_.sync(from_rotation(basis), winmat); + view_.visibility_test(false); + + inst_.pipelines.capture.render(view_); + }; + + DRW_stats_group_start("IrradianceBake.SurfelsCount"); + + /* Raster the scene to query the number of surfel needed. */ + capture_info_buf_.do_surfel_count = true; + capture_info_buf_.do_surfel_output = false; + capture_info_buf_.surfel_len = 0u; + capture_info_buf_.push_update(); + render_axis(Axis::X); + render_axis(Axis::Y); + render_axis(Axis::Z); + + DRW_stats_group_end(); + + /* Allocate surfel pool. */ + capture_info_buf_.read(); + if (capture_info_buf_.surfel_len == 0) { + /* Not surfel to allocated. */ + return; + } + /* 1000000 for testing. */ + if (capture_info_buf_.surfel_len * sizeof(Surfel) > 1000000) { + /* TODO(fclem): Display error message. */ + /* Not enough GPU memory to fit all needed surfels. */ + return; + } + surfels_buf_.resize(capture_info_buf_.surfel_len); + + DRW_stats_group_start("IrradianceBake.SurfelsCreate"); + + /* Raster the scene to generate the surfels. */ + capture_info_buf_.do_surfel_count = true; + capture_info_buf_.do_surfel_output = true; + capture_info_buf_.surfel_len = 0u; + capture_info_buf_.push_update(); + render_axis(Axis::X); + render_axis(Axis::Y); + render_axis(Axis::Z); + + DRW_stats_group_end(); + + /* TODO(fclem): Resize at the end of the light propagation. */ +} + +void IrradianceCache::propagate_light() +{ + /* Evaluate direct lighting (and also clear the surfels radiance). */ + /* For every ray direction over the sphere. */ + /* Create the surfels lists. */ + /* Sort the surfels lists. */ + /* Propagate light. */ } } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index bc5be19332f..46e5542c4d1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +/** \file + * \ingroup eevee + */ + #pragma once #include "eevee_shader_shared.hh" @@ -7,14 +11,23 @@ namespace blender::eevee { class Instance; +class CapturePipeline; class IrradianceCache { + friend CapturePipeline; + private: Instance &inst_; - DebugSurfelBuf debug_surfels; + /** Surface elements that represent the scene. */ + SurfelBuf surfels_buf_; + /** Capture state. */ + CaptureInfoBuf capture_info_buf_; + PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"}; - GPUShader *debug_surfels_sh_ = nullptr; + + Framebuffer empty_raster_fb_ = {"empty_raster_fb_"}; + View view_ = {"ortho_raster_view"}; /* TODO: Remove this. */ void generate_random_surfels(); @@ -26,6 +39,9 @@ class IrradianceCache { void init(); void sync(); + void create_surfels(); + void propagate_light(); + void debug_pass_sync(); void debug_draw(View &view, GPUFrameBuffer *view_fb); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index a210681517a..eb13fd1d965 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -242,6 +242,11 @@ Material &MaterialModule::material_sync(Object *ob, /* Order is important for transparent. */ mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type); mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); + if (true) { + /* TODO(fclem): This can be expensive since it can trigger a shader compilation. So better + * avoid this if we can. */ + mat.capture = material_pass_get(ob, blender_mat, MAT_PIPE_CAPTURE, geometry_type); + } if (blender_mat->blend_shadow == MA_BS_NONE) { mat.shadow = MaterialPass(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 0546c2e06bb..ad24a84532a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -34,6 +34,7 @@ enum eMaterialPipeline { MAT_PIPE_FORWARD_PREPASS_VELOCITY, MAT_PIPE_VOLUME, MAT_PIPE_SHADOW, + MAT_PIPE_CAPTURE, }; enum eMaterialGeometry { @@ -48,16 +49,16 @@ static inline void material_type_from_shader_uuid(uint64_t shader_uuid, eMaterialPipeline &pipeline_type, eMaterialGeometry &geometry_type) { - const uint64_t geometry_mask = ((1u << 3u) - 1u); - const uint64_t pipeline_mask = ((1u << 3u) - 1u); + const uint64_t geometry_mask = ((1u << 4u) - 1u); + const uint64_t pipeline_mask = ((1u << 4u) - 1u); geometry_type = static_cast(shader_uuid & geometry_mask); - pipeline_type = static_cast((shader_uuid >> 3u) & pipeline_mask); + pipeline_type = static_cast((shader_uuid >> 4u) & pipeline_mask); } static inline uint64_t shader_uuid_from_material_type(eMaterialPipeline pipeline_type, eMaterialGeometry geometry_type) { - return geometry_type | (pipeline_type << 3); + return geometry_type | (pipeline_type << 4); } ENUM_OPERATORS(eClosureBits, CLOSURE_AMBIENT_OCCLUSION) @@ -209,7 +210,7 @@ struct MaterialPass { struct Material { bool is_alpha_blend_transparent; - MaterialPass shadow, shading, prepass; + MaterialPass shadow, shading, prepass, capture; }; struct MaterialArray { diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 8b6f0ccfdce..23ad5525d36 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -503,4 +503,37 @@ void DeferredPipeline::render(View &view, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Capture Pipeline + * + * \{ */ + +void CapturePipeline::sync() +{ + surface_ps_.init(); + /* Surfel output is done using a SSBO, so no need for a fragment shader output color or depth. */ + /* WORKAROUND: Avoid rasterizer discard, but the shaders actually use no fragment output. */ + surface_ps_.state_set(DRW_STATE_WRITE_STENCIL); + surface_ps_.framebuffer_set(&inst_.irradiance_cache.empty_raster_fb_); + + surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.irradiance_cache.surfels_buf_); + surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.irradiance_cache.capture_info_buf_); + + surface_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + /* TODO(fclem): Remove. There should be no view dependent behavior during capture. */ + surface_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); +} + +PassMain::Sub *CapturePipeline::surface_material_add(GPUMaterial *gpumat) +{ + return &surface_ps_.sub(GPU_material_get_name(gpumat)); +} + +void CapturePipeline::render(View &view) +{ + inst_.manager->submit(surface_ps_, view); +} + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index d390fad4775..5f9cd6231da 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -184,6 +184,28 @@ class DeferredPipeline { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Capture Pipeline + * + * \{ */ + +class CapturePipeline { + private: + Instance &inst_; + + PassMain surface_ps_ = {"Capture.Surface"}; + + public: + CapturePipeline(Instance &inst) : inst_(inst){}; + + PassMain::Sub *surface_material_add(GPUMaterial *gpumat); + + void sync(); + void render(View &view); +}; + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Utility texture * @@ -271,17 +293,20 @@ class PipelineModule { DeferredPipeline deferred; ForwardPipeline forward; ShadowPipeline shadow; + CapturePipeline capture; UtilityTexture utility_tx; public: - PipelineModule(Instance &inst) : world(inst), deferred(inst), forward(inst), shadow(inst){}; + PipelineModule(Instance &inst) + : world(inst), deferred(inst), forward(inst), shadow(inst), capture(inst){}; void begin_sync() { deferred.begin_sync(); forward.sync(); shadow.sync(); + capture.sync(); } void end_sync() @@ -324,6 +349,8 @@ class PipelineModule { return nullptr; case MAT_PIPE_SHADOW: return shadow.surface_material_add(gpumat); + case MAT_PIPE_CAPTURE: + return capture.surface_material_add(gpumat); } return nullptr; } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 116d6004642..586c043eefd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -414,7 +414,6 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu info.additional_info("eevee_geom_curves"); break; case MAT_GEOM_MESH: - default: info.additional_info("eevee_geom_mesh"); break; } @@ -439,6 +438,9 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu case MAT_PIPE_SHADOW: info.additional_info("eevee_surf_shadow"); break; + case MAT_PIPE_CAPTURE: + info.additional_info("eevee_surf_capture"); + break; case MAT_PIPE_DEFERRED: info.additional_info("eevee_surf_deferred"); break; @@ -446,7 +448,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu info.additional_info("eevee_surf_forward"); break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } break; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 3b4345c3a2b..681eeb6eb01 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -51,7 +51,8 @@ enum eDebugMode : uint32_t { /** * Display IrradianceCache surfels. */ - DEBUG_IRRADIANCE_CACHE_SURFELS = 3u, + DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL = 3u, + DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE = 4u, /** * Show tiles depending on their status. */ @@ -826,17 +827,48 @@ static inline ShadowTileDataPacked shadow_tile_pack(ShadowTileData tile) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Debug +/** \name Irradiance Cache * \{ */ -struct DebugSurfel { +struct Surfel { + /** World position of the surfel. */ packed_float3 position; int _pad0; + /** World orientation of the surface. */ packed_float3 normal; int _pad1; - float4 color; + /** Surface albedo to apply to incoming radiance. */ + packed_float3 albedo; + int _pad2; + /** Accumulated reflected radiance at this point. */ + packed_float3 radiance; + int _pad3; }; -BLI_STATIC_ASSERT_ALIGN(DebugSurfel, 16) +BLI_STATIC_ASSERT_ALIGN(Surfel, 16) + +struct CaptureInfoData { + /** True if the surface shader needs to write the surfel data. */ + bool1 do_surfel_output; + /** True if the surface shader needs to increment the surfel_len. */ + bool1 do_surfel_count; + /** Number of surfels inside the surfel buffer or the needed len. */ + uint surfel_len; + int _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16) + +enum SurfelListEntryType : uint32_t { + ENTRY_SURFEL = 0u, + ENTRY_IRRADIANCE_SAMPLE = 1u, +}; + +struct SurfelListEntry { + uint next_entry_index; + SurfelListEntryType type; + uint payload; + uint _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(SurfelListEntry, 16) /** \} */ @@ -947,7 +979,6 @@ using DepthOfFieldDataBuf = draw::UniformBuffer; using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer; using DrawIndirectBuf = draw::StorageBuffer; using FilmDataBuf = draw::UniformBuffer; -using DebugSurfelBuf = draw::StorageArrayBuffer; using HiZDataBuf = draw::UniformBuffer; using LightCullingDataBuf = draw::StorageBuffer; using LightCullingKeyBuf = draw::StorageArrayBuffer; @@ -965,6 +996,8 @@ using ShadowPageCacheBuf = draw::StorageArrayBuffer; using ShadowTileMapClipBuf = draw::StorageArrayBuffer; using ShadowTileDataBuf = draw::StorageArrayBuffer; +using SurfelBuf = draw::StorageArrayBuffer; +using CaptureInfoBuf = draw::StorageBuffer; using VelocityGeometryBuf = draw::StorageArrayBuffer; using VelocityIndexBuf = draw::StorageArrayBuffer; using VelocityObjectBuf = draw::StorageArrayBuffer; diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index b340280cdfc..9c25ffacfcd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -131,6 +131,7 @@ void SyncModule::sync_mesh(Object *ob, geometry_call(material.shading.sub_pass, geom, res_handle); geometry_call(material.prepass.sub_pass, geom, res_handle); geometry_call(material.shadow.sub_pass, geom, res_handle); + geometry_call(material.capture.sub_pass, geom, res_handle); is_shadow_caster = is_shadow_caster || material.shadow.sub_pass != nullptr; is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index d2d8f80feb7..6b2a5beac95 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -134,7 +134,6 @@ void ShadingView::render() inst_.lights.debug_draw(render_view_new_, combined_fb_); inst_.hiz_buffer.debug_draw(render_view_new_, combined_fb_); inst_.shadows.debug_draw(render_view_new_, combined_fb_); - inst_.irradiance_cache.debug_draw(render_view_new_, combined_fb_); GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl index ed6bfc8f87f..156773abb26 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl @@ -1,8 +1,17 @@ void main() { - DebugSurfel surfel = surfels_buf[surfel_index]; - out_color = surfel.color; + Surfel surfel = surfels_buf[surfel_index]; + + switch (eDebugMode(debug_mode)) { + default: + case DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: + out_color = vec4(surfel.normal, 1.0); + break; + case DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: + out_color = vec4(surfel.radiance, 1.0); + break; + } /* Display surfels as circles. */ if (distance(P, surfel.position) > surfel_radius) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl index fc08f418827..e0c82de7299 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl @@ -4,7 +4,7 @@ void main() { surfel_index = gl_InstanceID; - DebugSurfel surfel = surfels_buf[surfel_index]; + Surfel surfel = surfels_buf[surfel_index]; vec3 lP; @@ -35,4 +35,5 @@ void main() P = (model_matrix * vec4(lP, 1)).xyz; gl_Position = point_world_to_ndc(P); + gl_Position.z -= 2.5e-5; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl new file mode 100644 index 00000000000..eedb3280dbe --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl @@ -0,0 +1,53 @@ + +/** + * Surface Capture: Output surface parameters to diverse storage. + * + * This is a separate shader to allow custom closure behavior and avoid putting more complexity + * into other surface shaders. + */ + +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) + +vec4 closure_to_rgba(Closure cl) +{ + return vec4(0.0); +} + +void main() +{ + init_globals(); + + /* TODO(fclem): Remove random sampling for capture and accumulate color. */ + g_closure_rand = 0.5; + + nodetree_surface(); + + g_diffuse_data.color *= g_diffuse_data.weight; + g_reflection_data.color *= g_reflection_data.weight; + g_refraction_data.color *= g_refraction_data.weight; + + vec3 albedo = g_diffuse_data.color + g_reflection_data.color; + + /* ----- Surfel output ----- */ + + if (capture_info_buf.do_surfel_count) { + /* Generate a surfel only once. This check allow cases where no axis is dominant. */ + bool is_surface_view_aligned = dominant_axis(g_data.Ng) == dominant_axis(cameraForward); + if (is_surface_view_aligned) { + uint surfel_id = atomicAdd(capture_info_buf.surfel_len, 1u); + if (capture_info_buf.do_surfel_output) { + surfel_buf[surfel_id].position = g_data.P; + surfel_buf[surfel_id].normal = gl_FrontFacing ? g_data.Ng : -g_data.Ng; + surfel_buf[surfel_id].albedo = albedo; + surfel_buf[surfel_id].radiance = g_emission; + } + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 0bc27f5ae2e..5bb91610af3 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -11,6 +11,7 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .vertex_out(eeve_debug_surfel_iface) .fragment_source("eevee_debug_surfels_frag.glsl") .fragment_out(0, Type::VEC4, "out_color") - .storage_buf(0, Qualifier::READ, "DebugSurfel", "surfels_buf[]") + .storage_buf(0, Qualifier::READ, "Surfel", "surfels_buf[]") .push_constant(Type::FLOAT, "surfel_radius") + .push_constant(Type::INT, "debug_mode") .do_static_compilation(true); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index f16113a5efc..b25caff4e9b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -147,6 +147,13 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) // "eevee_render_pass_out", ); +GPU_SHADER_CREATE_INFO(eevee_surf_capture) + .vertex_out(eevee_surf_iface) + .storage_buf(SURFEL_BUF_SLOT, Qualifier::WRITE, "Surfel", "surfel_buf[]") + .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ_WRITE, "CaptureInfoData", "capture_info_buf") + .fragment_source("eevee_surf_capture_frag.glsl") + .additional_info("eevee_camera", "eevee_utility_texture"); + GPU_SHADER_CREATE_INFO(eevee_surf_depth) .vertex_out(eevee_surf_iface) .fragment_source("eevee_surf_depth_frag.glsl") @@ -233,6 +240,7 @@ GPU_SHADER_CREATE_INFO(eevee_material_stub).define("EEVEE_MATERIAL_STUBS"); EEVEE_MAT_GEOM_VARIATIONS(name##_depth, "eevee_surf_depth", __VA_ARGS__) \ EEVEE_MAT_GEOM_VARIATIONS(name##_deferred, "eevee_surf_deferred", __VA_ARGS__) \ EEVEE_MAT_GEOM_VARIATIONS(name##_forward, "eevee_surf_forward", __VA_ARGS__) \ + EEVEE_MAT_GEOM_VARIATIONS(name##_capture, "eevee_surf_capture", __VA_ARGS__) \ EEVEE_MAT_GEOM_VARIATIONS(name##_shadow, "eevee_surf_shadow", __VA_ARGS__) EEVEE_MAT_PIPE_VARIATIONS(eevee_surface, "eevee_material_stub") -- 2.30.2 From 52a7125ce3cdc6467dc3965ef9a68a0490642b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 20 Mar 2023 19:45:25 +0100 Subject: [PATCH 02/58] EEVEE-Next: LightCache: Port basic lightcache structure to C++ This removes the complexity of resource sharing as it isn't even leveraged yet. So keep the code as simple as possible for now. --- scripts/startup/bl_ui/properties_render.py | 29 +++ source/blender/draw/CMakeLists.txt | 1 + .../engines/eevee_next/eevee_lightcache.cc | 218 ++++++++++++++++++ .../engines/eevee_next/eevee_lightcache.h | 70 ++++++ .../blender/editors/render/render_shading.cc | 21 +- 5 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/eevee_lightcache.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_lightcache.h diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index bf3c322f377..d9e9cba5bb7 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -570,6 +570,34 @@ class RENDER_PT_eevee_indirect_lighting(RenderButtonsPanel, Panel): col.prop(props, "gi_filter_quality") +class RENDER_PT_eevee_next_indirect_lighting(RenderButtonsPanel, Panel): + bl_label = "Indirect Lighting" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + scene = context.scene + props = scene.eevee + + col = layout.column() + col.operator("scene.light_cache_bake", text="Bake Indirect Lighting", icon='RENDER_STILL') + col.operator("scene.light_cache_free", text="Delete Lighting Cache") + + cache_info = scene.eevee.gi_cache_info + if cache_info: + col.label(text=cache_info) + + col.prop(props, "gi_auto_bake") + + class RENDER_PT_eevee_indirect_lighting_display(RenderButtonsPanel, Panel): bl_label = "Display" bl_parent_id = "RENDER_PT_eevee_indirect_lighting" @@ -905,6 +933,7 @@ classes = ( RENDER_PT_eevee_next_shadows, RENDER_PT_eevee_indirect_lighting, RENDER_PT_eevee_indirect_lighting_display, + RENDER_PT_eevee_next_indirect_lighting, RENDER_PT_eevee_film, RENDER_PT_eevee_next_film, diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 37fd7271dea..55bad222bba 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC engines/eevee_next/eevee_instance.cc engines/eevee_next/eevee_irradiance_cache.cc engines/eevee_next/eevee_light.cc + engines/eevee_next/eevee_lightcache.cc engines/eevee_next/eevee_material.cc engines/eevee_next/eevee_motion_blur.cc engines/eevee_next/eevee_pipeline.cc diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc new file mode 100644 index 00000000000..045c1764ee7 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + * + * Contains everything about light baking. + */ + +#include "DRW_render.h" + +#include "BKE_global.h" + +#include "BLI_endian_switch.h" +#include "BLI_threads.h" + +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "BKE_object.h" + +#include "DNA_collection_types.h" +#include "DNA_lightprobe_types.h" + +#include "PIL_time.h" + +#include "eevee_lightcache.h" + +#include "GPU_capabilities.h" +#include "GPU_context.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BLO_read_write.h" + +#include "wm_window.h" + +/* -------------------------------------------------------------------- */ +/** \name Light Probe Baking + * \{ */ + +/* TODO: should be replace by a more elegant alternative. */ +extern void DRW_opengl_context_enable(void); +extern void DRW_opengl_context_disable(void); + +extern void DRW_opengl_render_context_enable(void *re_gl_context); +extern void DRW_opengl_render_context_disable(void *re_gl_context); +extern void DRW_gpu_render_context_enable(void *re_gpu_context); +extern void DRW_gpu_render_context_disable(void *re_gpu_context); + +namespace blender::eevee { + +class LightBake { + private: + Depsgraph *depsgraph_; + + /** Scene frame to evaluate the depsgraph at. */ + int frame_; + /** Milliseconds. Delay the start of the baking to not slowdown interactions (TODO: remove). */ + int delay_ms_; + + /** + * If running in parallel (in a separate thread), use this context. + * Created on main thread but first bound in worker thread. + */ + void *gl_context_ = nullptr; + /** GPUContext associated to `gl_context_`. Created in the worker thread. */ + void *gpu_context_ = nullptr; + + public: + LightBake(struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + bool run_as_job, + int frame, + int delay_ms = 0) + : depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)), + frame_(frame), + delay_ms_(delay_ms) + { + BLI_assert(BLI_thread_is_main()); + if (run_as_job && !GPU_use_main_context_workaround()) { + /* This needs to happen in main thread. */ + gl_context_ = WM_opengl_context_create(); + wm_window_reset_drawable(); + } + std::cout << "Create" << std::endl; + } + + ~LightBake() + { + BLI_assert(BLI_thread_is_main()); + std::cout << "Delete" << std::endl; + DEG_graph_free(depsgraph_); + } + + /** + * Called from main thread. + * Copy result to original scene data. + * Note that since this is in the main thread, the viewport cannot be using the light cache. + * So there is no race condition here. + */ + void update() + { + BLI_assert(BLI_thread_is_main()); + std::cout << "update" << std::endl; + } + + /** + * Called from worker thread. + */ + void run(bool *stop = nullptr, bool *do_update = nullptr, float *progress = nullptr) + { + UNUSED_VARS(stop, do_update, progress); + + DEG_graph_relations_update(depsgraph_); + DEG_evaluate_on_framechange(depsgraph_, frame_); + + PIL_sleep_ms(1000); + std::cout << "run" << std::endl; + + delete_resources(); + } + + private: + void context_enable() + { + } + + void context_disable() + { + } + + /** + * Delete the engine instance and the optional contexts. + * This needs to run on the worker thread because the OpenGL context can only be ever bound to a + * single thread (because of some driver implementation), and the resources (textures, + * buffers,...) need to be freed with the right context bound. + */ + void delete_resources() + { + } +}; + +} // namespace blender::eevee + +extern "C" { + +using namespace blender::eevee; + +wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, + struct wmWindow *win, + struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + int delay_ms, + int frame) +{ + /* Do not bake if there is a render going on. */ + if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) { + return nullptr; + } + + /* Stop existing baking job. */ + WM_jobs_stop(wm, nullptr, (void *)EEVEE_NEXT_lightbake_job); + + wmJob *wm_job = WM_jobs_get(wm, + win, + scene, + "Bake Lighting", + WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, + WM_JOB_TYPE_LIGHT_BAKE); + + LightBake *bake = new LightBake(bmain, view_layer, scene, true, frame, delay_ms); + + WM_jobs_customdata_set(wm_job, bake, EEVEE_NEXT_lightbake_job_data_free); + WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0); + WM_jobs_callbacks(wm_job, + EEVEE_NEXT_lightbake_job, + nullptr, + EEVEE_NEXT_lightbake_update, + EEVEE_NEXT_lightbake_update); + + G.is_break = false; + + return wm_job; +} + +void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + bool run_as_job, + int frame) +{ + /* This should only be used for exec job. Eventually, remove `run_as_job` parameter later. */ + BLI_assert(run_as_job == false); + LightBake *bake = new LightBake(bmain, view_layer, scene, run_as_job, frame); + /* TODO(fclem): Can remove this cast once we remove the previous EEVEE light cache. */ + return reinterpret_cast(bake); +} + +void EEVEE_NEXT_lightbake_job_data_free(void *job_data) +{ + delete reinterpret_cast(job_data); +} + +void EEVEE_NEXT_lightbake_update(void *job_data) +{ + reinterpret_cast(job_data)->update(); +} + +void EEVEE_NEXT_lightbake_job(void *job_data, bool *stop, bool *do_update, float *progress) +{ + reinterpret_cast(job_data)->run(stop, do_update, progress); +} +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.h b/source/blender/draw/engines/eevee_next/eevee_lightcache.h new file mode 100644 index 00000000000..da148660a64 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** Opaque type hiding eevee::LightBake. */ +typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake; + +/** + * Create the job description. + * This is called for async (modal) bake operator. + * The actual work will be done by `EEVEE_NEXT_lightbake_job()`. + * Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()` or reuse data from an already + * existing baking job. + * IMPORTANT: Must run on the main thread because of potential GPUContext creation. + */ +struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, + struct wmWindow *win, + struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + int delay_ms, + int frame); + +/** + * Allocate dependency graph and job description (EEVEE_NEXT_LightBake). + * Dependency graph evaluation does *not* happen here. It is delayed until + * `EEVEE_NEXT_lightbake_job` runs. + * IMPORTANT: Must run on the main thread because of potential GPUContext creation. + * Return `EEVEE_NEXT_LightBake *` but cast to `void *` because of compatibility with existing + * EEVEE function. + */ +void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + bool run_as_job, + int frame); + +/** + * Free the job data. + * NOTE: Does not free the GPUContext. This is the responsibility of `EEVEE_NEXT_lightbake_job()` + */ +void EEVEE_NEXT_lightbake_job_data_free(void *job_data /* EEVEE_NEXT_LightBake */); + +/** + * Callback for updating original scene light cache with bake result. + * Run by the job system for each update step and the finish step. + * This is called manually by `EEVEE_NEXT_lightbake_job()` if not run from a job. + */ +void EEVEE_NEXT_lightbake_update(void *job_data /* EEVEE_NEXT_LightBake */); + +/** + * Do the full light baking for all samples. + * Will call `EEVEE_NEXT_lightbake_update()` on finish. + */ +void EEVEE_NEXT_lightbake_job(void *job_data /* EEVEE_NEXT_LightBake */, + bool *stop, + bool *do_update, + float *progress); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 5f21c51e28b..dc84d8f20b6 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -83,6 +83,7 @@ #include "RE_pipeline.h" #include "engines/eevee/eevee_lightcache.h" +#include "engines/eevee_next/eevee_lightcache.h" #include "render_intern.hh" /* own include */ @@ -1393,15 +1394,23 @@ static int light_cache_bake_exec(bContext *C, wmOperator *op) G.is_break = false; + RenderEngineType *engine_type = RE_engines_find(scene->r.engine); + bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); + /* TODO: abort if selected engine is not eevee. */ - void *rj = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false, scene->r.cfra); + void *rj = ((use_eevee_next) ? + EEVEE_NEXT_lightbake_job_data_alloc : + EEVEE_lightbake_job_data_alloc)(bmain, view_layer, scene, false, scene->r.cfra); light_cache_bake_tag_cache(scene, op); bool stop = false, do_update; float progress; /* Not actually used. */ - EEVEE_lightbake_job(rj, &stop, &do_update, &progress); - EEVEE_lightbake_job_data_free(rj); + /* Do the job. */ + ((use_eevee_next) ? EEVEE_NEXT_lightbake_job : + EEVEE_lightbake_job)(rj, &stop, &do_update, &progress); + /* Free baking data. Result is already stored in the scene data. */ + ((use_eevee_next) ? EEVEE_NEXT_lightbake_job_data_free : EEVEE_lightbake_job_data_free)(rj); /* No redraw needed, we leave state as we entered it. */ ED_update_for_newframe(bmain, CTX_data_depsgraph_pointer(C)); @@ -1420,7 +1429,11 @@ static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * Scene *scene = CTX_data_scene(C); int delay = RNA_int_get(op->ptr, "delay"); - wmJob *wm_job = EEVEE_lightbake_job_create( + RenderEngineType *engine_type = RE_engines_find(scene->r.engine); + bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); + + wmJob *wm_job = ((use_eevee_next) ? EEVEE_NEXT_lightbake_job_create : + EEVEE_lightbake_job_create)( wm, win, bmain, view_layer, scene, delay, scene->r.cfra); if (!wm_job) { -- 2.30.2 From dddfcd6f7abab96eda776576380bc610c27fd26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 20 Mar 2023 22:07:21 +0100 Subject: [PATCH 03/58] EEVEE-Next: LightProbe: Add auto update detection This allow probe update and deletion to trigger an auto bake. --- source/blender/blenkernel/intern/scene.cc | 4 +- source/blender/draw/CMakeLists.txt | 1 + .../draw/engines/eevee_next/eevee_instance.cc | 8 +- .../draw/engines/eevee_next/eevee_instance.hh | 3 + .../eevee_next/eevee_irradiance_cache.hh | 3 - .../engines/eevee_next/eevee_lightprobe.cc | 118 ++++++++++++++++++ .../engines/eevee_next/eevee_lightprobe.hh | 52 ++++++++ 7 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/eevee_lightprobe.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_lightprobe.hh diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index b5f7d5b68c8..814a0e00763 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -1747,6 +1747,7 @@ constexpr IDTypeInfo get_type_info() IDTypeInfo IDType_ID_SCE = get_type_info(); const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE"; +const char *RE_engine_id_BLENDER_EEVEE_NEXT = "BLENDER_EEVEE_NEXT"; const char *RE_engine_id_BLENDER_WORKBENCH = "BLENDER_WORKBENCH"; const char *RE_engine_id_BLENDER_WORKBENCH_NEXT = "BLENDER_WORKBENCH_NEXT"; const char *RE_engine_id_CYCLES = "CYCLES"; @@ -2935,7 +2936,8 @@ bool BKE_scene_use_spherical_stereo(Scene *scene) bool BKE_scene_uses_blender_eevee(const Scene *scene) { - return STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE); + return STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE) || + STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE_NEXT); } bool BKE_scene_uses_blender_workbench(const Scene *scene) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 55bad222bba..6408a0cd688 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC engines/eevee_next/eevee_instance.cc engines/eevee_next/eevee_irradiance_cache.cc engines/eevee_next/eevee_light.cc + engines/eevee_next/eevee_lightprobe.cc engines/eevee_next/eevee_lightcache.cc engines/eevee_next/eevee_material.cc engines/eevee_next/eevee_motion_blur.cc diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 0dc76355853..45a7c8a0535 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -107,6 +107,7 @@ void Instance::begin_sync() shadows.begin_sync(); pipelines.begin_sync(); cryptomatte.begin_sync(); + light_probes.begin_sync(); gpencil_engine_enabled = false; @@ -138,7 +139,8 @@ void Instance::scene_sync() void Instance::object_sync(Object *ob) { - const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL_LEGACY, OB_MESH, OB_LAMP); + const bool is_renderable_type = ELEM( + ob->type, OB_CURVES, OB_GPENCIL_LEGACY, OB_MESH, 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); @@ -179,6 +181,9 @@ void Instance::object_sync(Object *ob) case OB_GPENCIL_LEGACY: sync.sync_gpencil(ob, ob_handle, res_handle); break; + case OB_LIGHTPROBE: + light_probes.sync_probe(ob, ob_handle); + break; default: break; } @@ -207,6 +212,7 @@ void Instance::end_sync() film.end_sync(); cryptomatte.end_sync(); pipelines.end_sync(); + light_probes.end_sync(); } void Instance::render_sync() diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 8e7855961e4..926c67f50e2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -23,6 +23,7 @@ #include "eevee_hizbuffer.hh" #include "eevee_irradiance_cache.hh" #include "eevee_light.hh" +#include "eevee_lightprobe.hh" #include "eevee_material.hh" #include "eevee_motion_blur.hh" #include "eevee_pipeline.hh" @@ -63,6 +64,7 @@ class Instance { RenderBuffers render_buffers; MainView main_view; World world; + LightProbeModule light_probes; IrradianceCache irradiance_cache; /** Input data. */ @@ -108,6 +110,7 @@ class Instance { render_buffers(*this), main_view(*this), world(*this), + light_probes(*this), irradiance_cache(*this){}; ~Instance(){}; diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 46e5542c4d1..30a20436be2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -29,9 +29,6 @@ class IrradianceCache { Framebuffer empty_raster_fb_ = {"empty_raster_fb_"}; View view_ = {"ortho_raster_view"}; - /* TODO: Remove this. */ - void generate_random_surfels(); - public: IrradianceCache(Instance &inst) : inst_(inst){}; ~IrradianceCache(){}; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc new file mode 100644 index 00000000000..3edaeb4fa23 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + * + * Module that handles light probe update tagging. + * Lighting data is contained in their respective module `IrradianceCache` and `ReflectionProbes`. + */ + +#include "DNA_lightprobe_types.h" +#include "WM_api.h" + +#include "eevee_instance.hh" +#include "eevee_lightprobe.hh" + +namespace blender::eevee { + +void LightProbeModule::begin_sync() +{ + auto_bake_enabled_ = inst_.is_viewport() && + (inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0; + grid_update_ = false; + cube_update_ = false; +} + +void LightProbeModule::sync_grid(ObjectHandle &handle) +{ + LightProbe &grid = grid_map_.lookup_or_add_default(handle.object_key); + grid.used = true; + if (handle.recalc != 0 || grid.initialized == false) { + grid.initialized = true; + grid_update_ = true; + } +} + +void LightProbeModule::sync_cube(ObjectHandle &handle) +{ + LightProbe &cube = cube_map_.lookup_or_add_default(handle.object_key); + cube.used = true; + if (handle.recalc != 0 || cube.initialized == false) { + cube.initialized = true; + cube_update_ = true; + } +} + +void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle) +{ + const ::LightProbe *light_probe = (const ::LightProbe *)ob->data; + switch (light_probe->type) { + case LIGHTPROBE_TYPE_CUBE: + sync_cube(handle); + return; + case LIGHTPROBE_TYPE_PLANAR: + /* TODO(fclem): Remove support? Add support? */ + return; + case LIGHTPROBE_TYPE_GRID: + sync_grid(handle); + return; + } + BLI_assert_unreachable(); +} + +void LightProbeModule::end_sync() +{ + { + /* Check for deleted grid. */ + auto it_end = grid_map_.items().end(); + for (auto it = grid_map_.items().begin(); it != it_end; ++it) { + LightProbe &grid = (*it).value; + if (!grid.used) { + grid_map_.remove(it); + grid_update_ = true; + continue; + } + /* Untag for next sync. */ + grid.used = false; + } + } + { + /* Check for deleted cube. */ + auto it_end = cube_map_.items().end(); + for (auto it = cube_map_.items().begin(); it != it_end; ++it) { + LightProbe &cube = (*it).value; + if (!cube.used) { + cube_map_.remove(it); + cube_update_ = true; + continue; + } + /* Untag for next sync. */ + cube.used = false; + } + } + + /* If light-cache auto-update is enable we tag the relevant part + * of the cache to update and fire up a baking job. */ + if (auto_bake_enabled_ && (grid_update_ || cube_update_)) { + Scene *original_scene = DEG_get_input_scene(inst_.depsgraph); + LightCache *light_cache = original_scene->eevee.light_cache_data; + + if (light_cache != nullptr) { + if (grid_update_) { + light_cache->flag |= LIGHTCACHE_UPDATE_GRID; + } + /* TODO(fclem): Reflection Cubemap should capture albedo + normal and be + * relit at runtime. So no dependency like in the old system. */ + if (cube_update_) { + light_cache->flag |= LIGHTCACHE_UPDATE_CUBE; + } + /* Tag the lightcache to auto update. */ + light_cache->flag |= LIGHTCACHE_UPDATE_AUTO; + /* Use a notifier to trigger the operator after drawing. */ + /* TODO(fclem): Avoid usage of global DRW. */ + WM_event_add_notifier(DRW_context_state_get()->evil_C, NC_LIGHTPROBE, original_scene); + } + } +} + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh new file mode 100644 index 00000000000..db1ea8d72ed --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + * + * Module that handles light probe update tagging. + * Lighting data is contained in their respective module `IrradianceCache` and `ReflectionProbes`. + */ + +#pragma once + +#include "BLI_map.hh" + +#include "eevee_sync.hh" + +namespace blender::eevee { + +class Instance; + +struct LightProbe { + bool used = false; + bool initialized = false; +}; + +class LightProbeModule { + private: + Instance &inst_; + + /** Light Probe map to detect deletion. */ + Map grid_map_, cube_map_; + /** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */ + bool grid_update_; + /** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */ + bool cube_update_; + /** True if the auto bake feature is enabled & available in this context. */ + bool auto_bake_enabled_; + + public: + LightProbeModule(Instance &inst) : inst_(inst){}; + ~LightProbeModule(){}; + + void begin_sync(); + + void sync_cube(ObjectHandle &handle); + void sync_grid(ObjectHandle &handle); + + void sync_probe(const Object *ob, ObjectHandle &handle); + + void end_sync(); +}; + +} // namespace blender::eevee -- 2.30.2 From 795bc96cfe4449c125f7d6c1db0d49bf8ba89500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 21 Mar 2023 12:25:17 +0100 Subject: [PATCH 04/58] EEVEE-Next: LightCache: Add GL & GPU context functions --- .../engines/eevee_next/eevee_lightcache.cc | 102 ++++++++++++++---- 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index 045c1764ee7..58db048baa4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -16,15 +16,8 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" -#include "BKE_object.h" - -#include "DNA_collection_types.h" -#include "DNA_lightprobe_types.h" - #include "PIL_time.h" -#include "eevee_lightcache.h" - #include "GPU_capabilities.h" #include "GPU_context.h" @@ -35,19 +28,14 @@ #include "wm_window.h" +#include "eevee_instance.hh" + +#include "eevee_lightcache.h" + /* -------------------------------------------------------------------- */ /** \name Light Probe Baking * \{ */ -/* TODO: should be replace by a more elegant alternative. */ -extern void DRW_opengl_context_enable(void); -extern void DRW_opengl_context_disable(void); - -extern void DRW_opengl_render_context_enable(void *re_gl_context); -extern void DRW_opengl_render_context_disable(void *re_gl_context); -extern void DRW_gpu_render_context_enable(void *re_gpu_context); -extern void DRW_gpu_render_context_disable(void *re_gpu_context); - namespace blender::eevee { class LightBake { @@ -64,8 +52,11 @@ class LightBake { * Created on main thread but first bound in worker thread. */ void *gl_context_ = nullptr; - /** GPUContext associated to `gl_context_`. Created in the worker thread. */ - void *gpu_context_ = nullptr; + /** Context associated to `gl_context_`. Created in the worker thread. */ + GPUContext *gpu_context_ = nullptr; + + /** Baking instance. Created and freed in the worker thread. */ + Instance *instance_ = nullptr; public: LightBake(struct Main *bmain, @@ -84,13 +75,11 @@ class LightBake { gl_context_ = WM_opengl_context_create(); wm_window_reset_drawable(); } - std::cout << "Create" << std::endl; } ~LightBake() { BLI_assert(BLI_thread_is_main()); - std::cout << "Delete" << std::endl; DEG_graph_free(depsgraph_); } @@ -103,7 +92,6 @@ class LightBake { void update() { BLI_assert(BLI_thread_is_main()); - std::cout << "update" << std::endl; } /** @@ -117,18 +105,62 @@ class LightBake { DEG_evaluate_on_framechange(depsgraph_, frame_); PIL_sleep_ms(1000); - std::cout << "run" << std::endl; + + context_enable(); + + instance_ = new eevee::Instance(); + + context_disable(); delete_resources(); } private: - void context_enable() + void context_enable(bool render_begin = true) { + if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { + /* Reuse main draw context. */ + GPU_context_main_lock(); + DRW_opengl_context_enable(); + } + else if (gl_context_ == nullptr) { + /* Main thread case. */ + DRW_opengl_context_enable(); + } + else { + /* Worker thread case. */ + DRW_opengl_render_context_enable(gl_context_); + if (gpu_context_ == nullptr) { + /* Create GPUContext in worker thread as it needs the correct gl context bound (which can + * only be bound in worker thread because of some GL driver requirements). */ + gpu_context_ = GPU_context_create(nullptr, gl_context_); + } + DRW_gpu_render_context_enable(gpu_context_); + } + + if (render_begin) { + GPU_render_begin(); + } } void context_disable() { + GPU_render_end(); + + if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { + /* Reuse main draw context. */ + DRW_opengl_context_disable(); + GPU_context_main_unlock(); + } + else if (gl_context_ == nullptr) { + /* Main thread case. */ + DRW_opengl_context_disable(); + } + else { + /* Worker thread case. */ + DRW_gpu_render_context_disable(gpu_context_); + DRW_opengl_render_context_disable(gl_context_); + } } /** @@ -139,6 +171,30 @@ class LightBake { */ void delete_resources() { + /* Bind context without GPU_render_begin(). */ + context_enable(false); + + /* Free instance and its resources (Textures, Framebuffers, etc...). */ + delete instance_; + + /* Delete / unbind the GL & GPU context. Assumes it is currently bound. */ + if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { + /* Reuse main draw context. */ + DRW_opengl_context_disable(); + GPU_context_main_unlock(); + } + else if (gl_context_ == nullptr) { + /* Main thread case. */ + DRW_opengl_context_disable(); + } + else { + /* Worker thread case. */ + if (gpu_context_ != nullptr) { + GPU_context_discard(gpu_context_); + } + DRW_opengl_render_context_disable(gl_context_); + WM_opengl_context_dispose(gl_context_); + } } }; -- 2.30.2 From 7b3d4c9e4980e6ab4ad2b6231618ef4ff4d13e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 21 Mar 2023 20:15:59 +0100 Subject: [PATCH 05/58] EEVEE-Next: LightCache: Add basis of new lightcache version This should allow to keep both version working during the period where both eevee versions are available. --- source/blender/blenkernel/intern/scene.cc | 7 +- .../draw/engines/eevee/eevee_lightcache.c | 14 ++ .../engines/eevee_next/eevee_lightcache.cc | 127 ++++++++++++++++++ .../engines/eevee_next/eevee_lightcache.h | 37 +++++ .../blender/editors/render/render_shading.cc | 6 +- .../blender/makesdna/DNA_lightprobe_types.h | 22 +++ 6 files changed, 211 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 814a0e00763..e2ebdbfa003 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -107,6 +107,7 @@ #include "BLO_read_write.h" #include "engines/eevee/eevee_lightcache.h" +#include "engines/eevee_next/eevee_lightcache.h" #include "PIL_time.h" @@ -1439,7 +1440,11 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) EEVEE_lightcache_blend_read_data(reader, sce->eevee.light_cache_data); } } - EEVEE_lightcache_info_update(&sce->eevee); + + RenderEngineType *engine_type = RE_engines_find(sce->r.engine); + bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); + + (use_eevee_next ? EEVEE_NEXT_lightcache_info_update : EEVEE_lightcache_info_update)(&sce->eevee); BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading); diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 83a978ccff6..8afa63a484b 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -24,6 +24,7 @@ #include "PIL_time.h" +#include "../eevee_next/eevee_lightcache.h" #include "eevee_lightcache.h" #include "eevee_private.h" @@ -501,6 +502,11 @@ static void eevee_lightbake_readback_reflections(LightCache *lcache) void EEVEE_lightcache_free(LightCache *lcache) { + if (lcache->version == LIGHTCACHE_NEXT_STATIC_VERSION) { + EEVEE_NEXT_lightcache_free(lcache); + return; + } + DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex); MEM_SAFE_FREE(lcache->cube_tx.data); DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex); @@ -539,6 +545,10 @@ static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex void EEVEE_lightcache_blend_write(BlendWriter *writer, LightCache *cache) { + if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) { + EEVEE_NEXT_lightcache_blend_write(writer, cache); + } + write_lightcache_texture(writer, &cache->grid_tx); write_lightcache_texture(writer, &cache->cube_tx); @@ -579,6 +589,10 @@ static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTe void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache) { + if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) { + EEVEE_NEXT_lightcache_blend_read_data(reader, cache); + } + cache->flag &= ~LIGHTCACHE_NOT_USABLE; direct_link_lightcache_texture(reader, &cache->cube_tx); direct_link_lightcache_texture(reader, &cache->grid_tx); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index 58db048baa4..f0138e0058d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -92,6 +92,22 @@ class LightBake { void update() { BLI_assert(BLI_thread_is_main()); + Scene *original_scene = DEG_get_input_scene(depsgraph_); + + if (original_scene->eevee.light_cache_data == nullptr) { + LightCache *light_cache = EEVEE_NEXT_lightcache_create(); + + LightCacheIrradianceGrid *grids = (LightCacheIrradianceGrid *)MEM_callocN( + 1 * sizeof(LightCacheIrradianceGrid), "LightCacheIrradianceGrid"); + grids[0].resolution[0] = 1; + grids[0].resolution[1] = 2; + grids[0].resolution[2] = 3; + light_cache->grid_len = 1; + light_cache->grids = grids; + original_scene->eevee.light_cache_data = light_cache; + } + + EEVEE_NEXT_lightcache_info_update(&original_scene->eevee); } /** @@ -204,6 +220,10 @@ extern "C" { using namespace blender::eevee; +/* -------------------------------------------------------------------- */ +/** \name Light Bake Job + * \{ */ + wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, struct wmWindow *win, struct Main *bmain, @@ -269,6 +289,113 @@ void EEVEE_NEXT_lightbake_job(void *job_data, bool *stop, bool *do_update, float { reinterpret_cast(job_data)->run(stop, do_update, progress); } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Create / Delete + * \{ */ + +LightCache *EEVEE_NEXT_lightcache_create() +{ + LightCache *light_cache = (LightCache *)MEM_callocN(sizeof(LightCache), "LightCache"); + + light_cache->version = LIGHTCACHE_NEXT_STATIC_VERSION; + light_cache->type = LIGHTCACHE_TYPE_STATIC; + + return light_cache; +} + +void EEVEE_NEXT_lightcache_free(LightCache *light_cache) +{ + if (light_cache->version < LIGHTCACHE_NEXT_STATIC_VERSION) { + /* Allow deleting old EEVEE cache. */ + DRW_TEXTURE_FREE_SAFE(light_cache->cube_tx.tex); + MEM_SAFE_FREE(light_cache->cube_tx.data); + DRW_TEXTURE_FREE_SAFE(light_cache->grid_tx.tex); + MEM_SAFE_FREE(light_cache->grid_tx.data); + if (light_cache->cube_mips) { + for (int i = 0; i < light_cache->mips_len; i++) { + MEM_SAFE_FREE(light_cache->cube_mips[i].data); + } + MEM_SAFE_FREE(light_cache->cube_mips); + } + MEM_SAFE_FREE(light_cache->cube_data); + MEM_SAFE_FREE(light_cache->grid_data); + } + + MEM_SAFE_FREE(light_cache->grids); + MEM_freeN(light_cache); +} + +void EEVEE_NEXT_lightcache_info_update(SceneEEVEE *eevee) +{ + LightCache *light_cache = eevee->light_cache_data; + + if (light_cache == nullptr) { + BLI_strncpy(eevee->light_cache_info, + TIP_("No light cache in this scene"), + sizeof(eevee->light_cache_info)); + return; + } + + if (light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION) { + BLI_strncpy(eevee->light_cache_info, + TIP_("Incompatible Light cache version, please bake again"), + sizeof(eevee->light_cache_info)); + return; + } + + if (light_cache->flag & LIGHTCACHE_INVALID) { + BLI_strncpy(eevee->light_cache_info, + TIP_("Error: Light cache dimensions not supported by the GPU"), + sizeof(eevee->light_cache_info)); + return; + } + + if (light_cache->flag & LIGHTCACHE_BAKING) { + BLI_strncpy( + eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info)); + return; + } + + int irradiance_sample_len = 0; + for (const LightCacheIrradianceGrid &grid : + blender::Span(light_cache->grids, light_cache->grid_len)) { + irradiance_sample_len += grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; + } + + size_t size_in_bytes = irradiance_sample_len * sizeof(uint16_t) * 4 * 3; + + char formatted_mem[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE]; + BLI_str_format_byte_unit(formatted_mem, size_in_bytes, false); + + BLI_snprintf(eevee->light_cache_info, + sizeof(eevee->light_cache_info), + TIP_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"), + light_cache->cube_len, + irradiance_sample_len, + formatted_mem); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Read/Write to file + * \{ */ + +void EEVEE_NEXT_lightcache_blend_write(BlendWriter *writer, LightCache *light_cache) +{ + BLO_write_struct_array( + writer, LightCacheIrradianceGrid, light_cache->grid_len, light_cache->grids); +} + +void EEVEE_NEXT_lightcache_blend_read_data(BlendDataReader *reader, LightCache *light_cache) +{ + BLO_read_data_address(reader, &light_cache->grids); +} + +/** \} */ } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.h b/source/blender/draw/engines/eevee_next/eevee_lightcache.h index da148660a64..360da271c74 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.h +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.h @@ -13,6 +13,10 @@ extern "C" { /** Opaque type hiding eevee::LightBake. */ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake; +/* -------------------------------------------------------------------- */ +/** \name Light Bake Job + * \{ */ + /** * Create the job description. * This is called for async (modal) bake operator. @@ -65,6 +69,39 @@ void EEVEE_NEXT_lightbake_job(void *job_data /* EEVEE_NEXT_LightBake */, bool *do_update, float *progress); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Create / Delete + * \{ */ + +/** + * Create an empty light-cache. + */ +struct LightCache *EEVEE_NEXT_lightcache_create(void); + +/** + * Free a light-cache and its associated data. + */ +void EEVEE_NEXT_lightcache_free(struct LightCache *lcache); + +/** + * Update the UI message in the render panel about the state of the cache. + */ +void EEVEE_NEXT_lightcache_info_update(struct SceneEEVEE *eevee); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Read/Write to file + * \{ */ + +void EEVEE_NEXT_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *light_cache); +void EEVEE_NEXT_lightcache_blend_read_data(struct BlendDataReader *reader, + struct LightCache *light_cache); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index dc84d8f20b6..640d91b8c70 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -1533,7 +1533,11 @@ static int light_cache_free_exec(bContext *C, wmOperator * /*op*/) EEVEE_lightcache_free(scene->eevee.light_cache_data); scene->eevee.light_cache_data = nullptr; - EEVEE_lightcache_info_update(&scene->eevee); + RenderEngineType *engine_type = RE_engines_find(scene->r.engine); + bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); + + ((use_eevee_next) ? EEVEE_NEXT_lightcache_info_update : + EEVEE_lightcache_info_update)(&scene->eevee); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 5b5bc4c7392..357fb5a87e5 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -142,6 +142,21 @@ typedef struct LightCacheTexture { char _pad[2]; } LightCacheTexture; +typedef struct LightCacheIrradianceGrid { + /** Transform to local space [0..resolution-1] range. */ + float world_to_grid[4][4]; + /** Number of samples in each dimension. */ + int resolution[3]; + /** Number of surfels in the cache. */ + int surfels_len; + /** Currently only used at runtime for debugging the baking process. Not written to file. */ + // struct GPUStorageBuf *surfels; + /** Irradiance Data. Stored as spherical harmonic. */ + // LightCacheTexture *irradiance_L0_L1_a; + // LightCacheTexture *irradiance_L0_L1_b; + // LightCacheTexture *irradiance_L0_L1_c; +} LightCacheIrradianceGrid; + typedef struct LightCache { int flag; /** Version number to know if the cache data is compatible with this version of blender. */ @@ -167,10 +182,17 @@ typedef struct LightCache { /* All lightprobes data contained in the cache. */ LightProbeCache *cube_data; LightGridCache *grid_data; + + /* ---- EEVEE-Next ---- */ + LightCacheIrradianceGrid *grids; + // LightCacheReflectionCube *cubes; } LightCache; /* Bump the version number for lightcache data structure changes. */ #define LIGHTCACHE_STATIC_VERSION 2 +/* Cache generated by EEVEE-Next. Should be removed and be replaced by bumped + * LIGHTCACHE_STATIC_VERSION once EEVEE-Next is made default. */ +#define LIGHTCACHE_NEXT_STATIC_VERSION 3 /* LightCache->type */ enum { -- 2.30.2 From e6de2fc4f70655b360efc0f3257a97521df04842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 23 Mar 2023 16:39:02 +0100 Subject: [PATCH 06/58] EEVEE-Next: GI: Add irradiance sample count option --- scripts/startup/bl_ui/properties_render.py | 1 + source/blender/makesdna/DNA_scene_defaults.h | 1 + source/blender/makesdna/DNA_scene_types.h | 2 +- source/blender/makesrna/intern/rna_scene.c | 8 ++++++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index d9e9cba5bb7..bda7cc6622e 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -596,6 +596,7 @@ class RENDER_PT_eevee_next_indirect_lighting(RenderButtonsPanel, Panel): col.label(text=cache_info) col.prop(props, "gi_auto_bake") + col.prop(props, "gi_irradiance_samples") class RENDER_PT_eevee_indirect_lighting_display(RenderButtonsPanel, Panel): diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 6cc01d254ce..f6b77aede0d 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -163,6 +163,7 @@ #define _DNA_DEFAULT_SceneEEVEE \ { \ .gi_diffuse_bounces = 3, \ + .gi_irradiance_samples = 128, \ .gi_cubemap_resolution = 512, \ .gi_visibility_resolution = 32, \ .gi_cubemap_draw_size = 0.3f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 6fb38eae744..3d3de4f203c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1776,6 +1776,7 @@ typedef struct SceneDisplay { typedef struct SceneEEVEE { int flag; int gi_diffuse_bounces; + int gi_irradiance_samples; int gi_cubemap_resolution; int gi_visibility_resolution; float gi_irradiance_smoothing; @@ -1832,7 +1833,6 @@ typedef struct SceneEEVEE { int shadow_cube_size; int shadow_cascade_size; int shadow_pool_size; - char _pad[4]; struct LightCache *light_cache DNA_DEPRECATED; struct LightCache *light_cache_data; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index c47235f8c36..3d39ea8329c 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -7338,6 +7338,14 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_range(prop, 0, INT_MAX); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + prop = RNA_def_property(srna, "gi_irradiance_samples", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, + "Diffuse Samples", + "Number of rays direction to evaluate when baking a single " + "bounce of indirect lighting"); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + prop = RNA_def_property(srna, "gi_cubemap_resolution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, eevee_shadow_size_items); RNA_def_property_ui_text(prop, "Cubemap Size", "Size of every cubemaps"); -- 2.30.2 From 38f6de30cf15d3c43768f9a501a1bf7e5b7a6f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 23 Mar 2023 16:41:48 +0100 Subject: [PATCH 07/58] EEVEE-Next: LightCache: Solidify the baking pipeline --- .../draw/engines/eevee_next/eevee_engine.cc | 5 +- .../draw/engines/eevee_next/eevee_instance.cc | 143 +++++++++++++++++- .../draw/engines/eevee_next/eevee_instance.hh | 31 +++- .../eevee_next/eevee_irradiance_cache.cc | 91 +++++++++-- .../eevee_next/eevee_irradiance_cache.hh | 50 +++++- .../engines/eevee_next/eevee_lightcache.cc | 116 +++++++++++--- .../engines/eevee_next/eevee_lightprobe.cc | 11 ++ .../engines/eevee_next/eevee_lightprobe.hh | 12 ++ .../draw/engines/eevee_next/eevee_material.cc | 18 ++- .../draw/engines/eevee_next/eevee_pipeline.cc | 6 +- .../draw/engines/eevee_next/eevee_sampling.cc | 8 +- source/blender/draw/intern/DRW_render.h | 5 + source/blender/draw/intern/draw_manager.c | 28 +++- .../blender/makesdna/DNA_lightprobe_types.h | 8 +- 14 files changed, 458 insertions(+), 74 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc index 3ea6ea475d0..a6a0539378e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_engine.cc +++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc @@ -85,8 +85,7 @@ static void eevee_engine_init(void *vedata) } } - ved->instance->init( - size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d); + ved->instance->init(size, &rect, nullptr, depsgraph, camera, nullptr, default_view, v3d, rv3d); } static void eevee_draw_scene(void *vedata) @@ -161,7 +160,7 @@ static void eevee_render_to_image(void *vedata, rcti rect; RE_GetViewPlane(render, &view_rect, &rect); - instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer); + instance->init(size, &rect, engine, depsgraph, camera_original_ob, layer); instance->render_frame(layer, viewname); EEVEE_Data *ved = static_cast(vedata); diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 45a7c8a0535..37e8dda0949 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -19,7 +19,9 @@ #include "DNA_modifier_types.h" #include "RE_pipeline.h" +#include "eevee_engine.h" #include "eevee_instance.hh" +#include "eevee_lightcache.h" namespace blender::eevee { @@ -37,14 +39,12 @@ void Instance::init(const int2 &output_res, const rcti *output_rect, RenderEngine *render_, Depsgraph *depsgraph_, - const LightProbe *light_probe_, Object *camera_object_, const RenderLayer *render_layer_, const DRWView *drw_view_, const View3D *v3d_, const RegionView3D *rv3d_) { - UNUSED_VARS(light_probe_); render = render_; depsgraph = depsgraph_; camera_orig_object = camera_object_; @@ -73,6 +73,36 @@ void Instance::init(const int2 &output_res, irradiance_cache.init(); } +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; + + is_light_bake = true; + debug_mode = (eDebugMode)G.debug_value; + info = ""; + + update_eval_members(); + + 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); + velocity.init(); + depth_of_field.init(); + shadows.init(); + motion_blur.init(); + main_view.init(); + irradiance_cache.init(); +} + void Instance::set_time(float time) { BLI_assert(render); @@ -255,8 +285,6 @@ void Instance::render_sample() main_view.render(); - irradiance_cache.create_surfels(); - motion_blur.step(); } @@ -306,6 +334,50 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Light Bake + * \{ */ + +LightCache *Instance::light_cache_create(Vector grids, + Vector cubes) +{ + LightCache *light_cache = EEVEE_NEXT_lightcache_create(); + light_cache->flag = LIGHTCACHE_BAKING; + + light_cache->grid_len = grids.size(); + light_cache->grids = MEM_cnew_array(grids.size(), + "LightCacheIrradianceGrid"); + + for (const auto i : grids.index_range()) { + const IrradianceGrid &grid = grids[i]; + size_t sample_count = grid.resolution.x * grid.resolution.y * grid.resolution.z; + size_t grid_texture_sample_size = sizeof(uint16_t) * 4; + if (sample_count * grid_texture_sample_size > INT_MAX) { + /* The size of the texture doesn't fit on a 32bit system. */ + light_cache->flag |= LIGHTCACHE_INVALID; + info = "Scene contains an irradiance grid with too many samples points"; + return nullptr; + } + LightCacheIrradianceGrid &irradiance_grid = light_cache->grids[i]; + copy_m4_m4(irradiance_grid.world_to_grid, grid.transform.ptr()); + irradiance_grid.resolution[0] = grid.resolution.x; + irradiance_grid.resolution[1] = grid.resolution.y; + irradiance_grid.resolution[2] = grid.resolution.z; + } + + light_cache->cube_len = cubes.size(); + // light_cache->cubes = MEM_cnew_array(cubes.size(), + // "LightCacheIrradianceGrid"); + + // for (const auto i : cubes.index_range()) { + /* TODO */ + // } + + return light_cache; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Interface * \{ */ @@ -426,6 +498,69 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL); } +void Instance::light_bake_irradiance(LightCache *&r_light_cache, + std::function context_enable, + std::function context_disable) +{ + BLI_assert(is_baking()); + BLI_assert(r_light_cache == nullptr); + + auto custom_pipeline_wrapper = [&](std::function callback) { + context_enable(); + DRW_custom_pipeline_begin(&draw_engine_eevee_next_type, depsgraph); + callback(); + DRW_custom_pipeline_end(); + context_disable(); + }; + + auto context_wrapper = [&](std::function callback) { + context_enable(); + callback(); + context_disable(); + }; + + /* Count probes. */ + custom_pipeline_wrapper([&]() { render_sync(); }); + /* Allocate CPU storage. */ + r_light_cache = this->light_cache_create(light_probes.grids, light_probes.cubes); + + if (r_light_cache->flag & LIGHTCACHE_INVALID) { + /* Something happened and the light cache couldn't be created. */ + // stop = true; + // do_update = true; + r_light_cache->flag &= ~LIGHTCACHE_BAKING; + return; + } + + /* TODO(fclem): Multiple bounce. We need to use the previous bounce result. */ + for (int bounce = 0; bounce < 1; bounce++) { + for (auto i : light_probes.grids.index_range()) { + custom_pipeline_wrapper([&]() { + /* TODO: lightprobe visibility group option. */ + render_sync(); + irradiance_cache.bake.surfels_create(light_probes.grids[i]); + irradiance_cache.bake.surfels_lights_eval(); + }); + + sampling.reset(); + while (!sampling.finished()) { + context_wrapper([&]() { + /* Batch ray cast by pack of 16 to avoid too much overhead of + * the update function & context switch. */ + for (int i = 0; i < 16 && !sampling.finished(); i++) { + sampling.step(); + irradiance_cache.bake.propagate_light_sample(); + } + irradiance_cache.bake.read_result(r_light_cache->grids[i]); + }); + // do_update = true; + } + } + } + + r_light_cache->flag &= ~LIGHTCACHE_BAKING; +} + /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 926c67f50e2..f94f5624a7c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -73,6 +73,7 @@ class Instance { /** Evaluated IDs. */ Scene *scene; ViewLayer *view_layer; + /** Camera object if rendering through a camera. nullptr otherwise. */ Object *camera_eval_object; Object *camera_orig_object; /** Only available when rendering for final render. */ @@ -85,6 +86,8 @@ class Instance { /** True if the grease pencil engine might be running. */ bool gpencil_engine_enabled; + /** True if the instance is created for light baking. */ + bool is_light_bake = false; /** Info string displayed at the top of the render / viewport. */ std::string info = ""; @@ -114,11 +117,12 @@ class Instance { irradiance_cache(*this){}; ~Instance(){}; + /* Render & Viewport. */ + /* TODO(fclem): Split for clarity. */ void init(const int2 &output_res, const rcti *output_rect, RenderEngine *render, Depsgraph *depsgraph, - const LightProbe *light_probe_ = nullptr, Object *camera_object = nullptr, const RenderLayer *render_layer = nullptr, const DRWView *drw_view = nullptr, @@ -129,17 +133,33 @@ class Instance { void object_sync(Object *ob); void end_sync(); + /* Render. */ + void render_sync(); void render_frame(RenderLayer *render_layer, const char *view_name); void store_metadata(RenderResult *render_result); + /* Viewport. */ + void draw_viewport(DefaultFramebufferList *dfbl); + /* Light bake. */ + + void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager); + void light_bake_irradiance(LightCache *&r_light_cache, + std::function context_enable, + std::function context_disable); + static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer); bool is_viewport() const { - return render == nullptr; + return render == nullptr && !is_baking(); + } + + bool is_baking() const + { + return is_light_bake; } bool overlays_enabled() const @@ -176,6 +196,13 @@ class Instance { void scene_sync(); void mesh_sync(Object *ob, ObjectHandle &ob_handle); + /** + * Create a light cache big enough to fit all light-probes inside. + * IMPORTANT: Can return nullptr on failure, in which case, the `Instance::info` will be set + * to an error message. + */ + LightCache *light_cache_create(Vector grids, Vector cubes); + void update_eval_members(); void set_time(float time); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 7ea4b08caf7..0804cfb7a15 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -1,18 +1,30 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_lightprobe_types.h" +#include "GPU_capabilities.h" + #include "eevee_instance.hh" #include "eevee_irradiance_cache.hh" namespace blender::eevee { +/* -------------------------------------------------------------------- */ +/** \name Interface + * \{ */ + void IrradianceCache::init() { } void IrradianceCache::sync() { - debug_pass_sync(); + if (inst_.is_baking()) { + bake.sync(); + } + else { + debug_pass_sync(); + } } void IrradianceCache::debug_pass_sync() @@ -22,13 +34,32 @@ void IrradianceCache::debug_pass_sync() eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) { return; } + + LightCache *light_cache = inst_.scene->eevee.light_cache_data; + if (light_cache == nullptr || light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION || + light_cache->grids == nullptr) { + return; + } + debug_surfels_ps_.init(); debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL); debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); - debug_surfels_ps_.bind_ssbo("surfels_buf", surfels_buf_); debug_surfels_ps_.push_constant("surfel_radius", 0.5f / 4.0f); debug_surfels_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); + + surfels_buf_.clear(); + for (auto i : IndexRange(light_cache->grid_len)) { + LightCacheIrradianceGrid &grid = light_cache->grids[i]; + if (grid.surfels_len > 0 && grid.surfels != nullptr) { + Span grid_surfels(static_cast(grid.surfels), grid.surfels_len); + for (const Surfel &surfel : grid_surfels) { + surfels_buf_.append(surfel); + } + } + } + surfels_buf_.push_update(); + debug_surfels_ps_.bind_ssbo("surfels_buf", surfels_buf_); debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, surfels_buf_.size(), 4); } @@ -50,7 +81,17 @@ void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb) inst_.manager->submit(debug_surfels_ps_, view); } -void IrradianceCache::create_surfels() +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Baking + * \{ */ + +void IrradianceBake::sync() +{ +} + +void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) { /** * We rasterize the scene along the 3 axes. Each generated fragment will write a surface element @@ -60,7 +101,7 @@ void IrradianceCache::create_surfels() using namespace blender::math; /* Attachment-less frame-buffer. */ - empty_raster_fb_.ensure(int2(40 * 4)); + empty_raster_fb_.ensure(int2(10 * 4)); /** We could use multi-view rendering here to avoid multiple submissions but it is unlikely to * make any difference. The bottleneck is still the light propagation loop. */ @@ -89,17 +130,15 @@ void IrradianceCache::create_surfels() DRW_stats_group_end(); /* Allocate surfel pool. */ + GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); capture_info_buf_.read(); if (capture_info_buf_.surfel_len == 0) { /* Not surfel to allocated. */ return; } - /* 1000000 for testing. */ - if (capture_info_buf_.surfel_len * sizeof(Surfel) > 1000000) { - /* TODO(fclem): Display error message. */ - /* Not enough GPU memory to fit all needed surfels. */ - return; - } + + /* TODO(fclem): Check for GL limit and abort if the surfel cache doesn't fit the GPU memory. */ + std::cout << "Resize " << capture_info_buf_.surfel_len << std::endl; surfels_buf_.resize(capture_info_buf_.surfel_len); DRW_stats_group_start("IrradianceBake.SurfelsCreate"); @@ -114,17 +153,37 @@ void IrradianceCache::create_surfels() render_axis(Axis::Z); DRW_stats_group_end(); - - /* TODO(fclem): Resize at the end of the light propagation. */ } -void IrradianceCache::propagate_light() +void IrradianceBake::surfels_lights_eval() { - /* Evaluate direct lighting (and also clear the surfels radiance). */ - /* For every ray direction over the sphere. */ - /* Create the surfels lists. */ +} + +void IrradianceBake::propagate_light_sample() +{ + /* Pick random ray direction over the sphere. */ + /* Project to regular grid and create the surfels lists. */ /* Sort the surfels lists. */ /* Propagate light. */ } +void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) +{ + switch (inst_.debug_mode) { + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: + case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: + GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); + std::cout << "Read " << capture_info_buf_.surfel_len << std::endl; + surfels_buf_.read(); + light_cache_grid.surfels_len = capture_info_buf_.surfel_len; + light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data()); + break; + default: + /* Nothing to display. */ + return; + } +} + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 30a20436be2..0e18b235149 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -6,6 +6,9 @@ #pragma once +#include "DNA_lightprobe_types.h" + +#include "eevee_lightprobe.hh" #include "eevee_shader_shared.hh" namespace blender::eevee { @@ -13,32 +16,63 @@ namespace blender::eevee { class Instance; class CapturePipeline; -class IrradianceCache { +/** + * Baking related pass and data. Not used at runtime. + */ +class IrradianceBake { friend CapturePipeline; private: Instance &inst_; + /** Light cache being baked. */ + LightCache *light_cache_ = nullptr; /** Surface elements that represent the scene. */ SurfelBuf surfels_buf_; /** Capture state. */ CaptureInfoBuf capture_info_buf_; - - PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"}; - + /** Framebuffer. */ Framebuffer empty_raster_fb_ = {"empty_raster_fb_"}; View view_ = {"ortho_raster_view"}; public: - IrradianceCache(Instance &inst) : inst_(inst){}; + IrradianceBake(Instance &inst) : inst_(inst){}; + + void sync(); + + /** Create a surfel representation of the scene from the \a grid using the capture pipeline. */ + void surfels_create(const IrradianceGrid &grid); + /** Evaluate direct lighting (and also clear the surfels radiance). */ + void surfels_lights_eval(); + /** Propagate light from surfel to surfel in a random direction over the sphere. */ + void propagate_light_sample(); + + /** Read grid final irradiance back to CPU into \a light_cache_grid . */ + void read_result(LightCacheIrradianceGrid &light_cache_grid); +}; + +/** + * Runtime container of diffuse indirect lighting. + * Also have debug and baking components. + */ +class IrradianceCache { + public: + IrradianceBake bake; + + private: + Instance &inst_; + + PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"}; + /** Debug surfel elements copied from the light cache. */ + draw::StorageVectorBuffer surfels_buf_; + + public: + IrradianceCache(Instance &inst) : bake(inst), inst_(inst){}; ~IrradianceCache(){}; void init(); void sync(); - void create_surfels(); - void propagate_light(); - void debug_pass_sync(); void debug_draw(View &view, GPUFrameBuffer *view_fb); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index f0138e0058d..32c3caf4c00 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -28,6 +28,7 @@ #include "wm_window.h" +#include "eevee_engine.h" #include "eevee_instance.hh" #include "eevee_lightcache.h" @@ -57,6 +58,11 @@ class LightBake { /** Baking instance. Created and freed in the worker thread. */ Instance *instance_ = nullptr; + /** Light Cache being baked. Create in worker thread and pass ownership to original scene on + * first `update()` call. */ + ::LightCache *light_cache_ = nullptr; + /** Manager used for command submission. Created and freed in the worker thread. */ + draw::Manager *manager_ = nullptr; public: LightBake(struct Main *bmain, @@ -93,18 +99,14 @@ class LightBake { { BLI_assert(BLI_thread_is_main()); Scene *original_scene = DEG_get_input_scene(depsgraph_); + LightCache *&scene_light_cache = original_scene->eevee.light_cache_data; - if (original_scene->eevee.light_cache_data == nullptr) { - LightCache *light_cache = EEVEE_NEXT_lightcache_create(); - - LightCacheIrradianceGrid *grids = (LightCacheIrradianceGrid *)MEM_callocN( - 1 * sizeof(LightCacheIrradianceGrid), "LightCacheIrradianceGrid"); - grids[0].resolution[0] = 1; - grids[0].resolution[1] = 2; - grids[0].resolution[2] = 3; - light_cache->grid_len = 1; - light_cache->grids = grids; - original_scene->eevee.light_cache_data = light_cache; + if (scene_light_cache != light_cache_) { + if (scene_light_cache != nullptr) { + /* Delete old data if existing. */ + EEVEE_NEXT_lightcache_free(scene_light_cache); + } + scene_light_cache = light_cache_; } EEVEE_NEXT_lightcache_info_update(&original_scene->eevee); @@ -120,14 +122,19 @@ class LightBake { DEG_graph_relations_update(depsgraph_); DEG_evaluate_on_framechange(depsgraph_, frame_); - PIL_sleep_ms(1000); - context_enable(); - + manager_ = new draw::Manager(); instance_ = new eevee::Instance(); - + instance_->init_light_bake(depsgraph_, manager_); context_disable(); + if (delay_ms_ > 0) { + PIL_sleep_ms(delay_ms_); + } + + instance_->light_bake_irradiance( + light_cache_, [this]() { context_enable(); }, [this]() { context_disable(); }); + delete_resources(); } @@ -190,8 +197,9 @@ class LightBake { /* Bind context without GPU_render_begin(). */ context_enable(false); - /* Free instance and its resources (Textures, Framebuffers, etc...). */ + /* Free GPU data (Textures, Framebuffers, etc...). */ delete instance_; + delete manager_; /* Delete / unbind the GL & GPU context. Assumes it is currently bound. */ if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { @@ -323,8 +331,15 @@ void EEVEE_NEXT_lightcache_free(LightCache *light_cache) MEM_SAFE_FREE(light_cache->cube_data); MEM_SAFE_FREE(light_cache->grid_data); } - - MEM_SAFE_FREE(light_cache->grids); + else { + for (int i = 0; i < light_cache->grid_len; i++) { + MEM_SAFE_FREE(light_cache->grids[i].surfels); + MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_a.data); + MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_b.data); + MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_c.data); + } + MEM_SAFE_FREE(light_cache->grids); + } MEM_freeN(light_cache); } @@ -386,13 +401,76 @@ void EEVEE_NEXT_lightcache_info_update(SceneEEVEE *eevee) void EEVEE_NEXT_lightcache_blend_write(BlendWriter *writer, LightCache *light_cache) { + auto write_lightcache_texture = [&](LightCacheTexture &tex) { + if (tex.data) { + size_t data_size = tex.components * tex.tex_size[0] * tex.tex_size[1] * tex.tex_size[2]; + if (tex.data_type == LIGHTCACHETEX_FLOAT) { + data_size *= sizeof(float); + } + else if (tex.data_type == LIGHTCACHETEX_UINT) { + data_size *= sizeof(uint); + } + + /* FIXME: We can't save more than what 32bit systems can handle. + * The solution would be to split the texture but it is too late for 2.90. (see #78529) */ + if (data_size < INT_MAX) { + BLO_write_raw(writer, data_size, tex.data); + } + } + }; + BLO_write_struct_array( writer, LightCacheIrradianceGrid, light_cache->grid_len, light_cache->grids); + + for (int i = 0; i < light_cache->grid_len; i++) { + LightCacheIrradianceGrid &grid = light_cache->grids[i]; + /* Surfels are runtime data. Not stored in the blend file. */ + write_lightcache_texture(grid.irradiance_L0_L1_a); + write_lightcache_texture(grid.irradiance_L0_L1_b); + write_lightcache_texture(grid.irradiance_L0_L1_c); + } } void EEVEE_NEXT_lightcache_blend_read_data(BlendDataReader *reader, LightCache *light_cache) { - BLO_read_data_address(reader, &light_cache->grids); + + if (light_cache->grids) { + BLO_read_data_address(reader, &light_cache->grids); + + auto direct_link_lightcache_texture = [&](LightCacheTexture &lctex) { + /* Runtime data. Not stored in the blend file. */ + lctex.tex = nullptr; + + if (lctex.data) { + BLO_read_data_address(reader, &lctex.data); + if (lctex.data && BLO_read_requires_endian_switch(reader)) { + int data_size = lctex.components * lctex.tex_size[0] * lctex.tex_size[1] * + lctex.tex_size[2]; + + if (lctex.data_type == LIGHTCACHETEX_FLOAT) { + BLI_endian_switch_float_array((float *)lctex.data, data_size * sizeof(float)); + } + else if (lctex.data_type == LIGHTCACHETEX_UINT) { + BLI_endian_switch_uint32_array((uint *)lctex.data, data_size * sizeof(uint)); + } + } + } + + if (lctex.data == nullptr) { + zero_v3_int(lctex.tex_size); + } + }; + + for (int i = 0; i < light_cache->grid_len; i++) { + LightCacheIrradianceGrid &grid = light_cache->grids[i]; + /* Runtime data. Not stored in the blend file. */ + grid.surfels_len = 0; + grid.surfels = nullptr; + direct_link_lightcache_texture(grid.irradiance_L0_L1_a); + direct_link_lightcache_texture(grid.irradiance_L0_L1_b); + direct_link_lightcache_texture(grid.irradiance_L0_L1_c); + } + } } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 3edaeb4fa23..6e30b531416 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -21,6 +21,9 @@ void LightProbeModule::begin_sync() (inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0; grid_update_ = false; cube_update_ = false; + + grids.clear(); + cubes.clear(); } void LightProbeModule::sync_grid(ObjectHandle &handle) @@ -31,6 +34,10 @@ void LightProbeModule::sync_grid(ObjectHandle &handle) grid.initialized = true; grid_update_ = true; } + + if (inst_.is_baking()) { + grids.append({}); + } } void LightProbeModule::sync_cube(ObjectHandle &handle) @@ -41,6 +48,10 @@ void LightProbeModule::sync_cube(ObjectHandle &handle) cube.initialized = true; cube_update_ = true; } + + if (inst_.is_baking()) { + cubes.append({}); + } } void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle) diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index db1ea8d72ed..edbf5f36c98 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -17,12 +17,24 @@ namespace blender::eevee { class Instance; +struct IrradianceGrid { + float4x4 transform; + int3 resolution; +}; + +struct ReflectionCube {}; + struct LightProbe { bool used = false; bool initialized = false; }; class LightProbeModule { + public: + /** Synced probe data. Only valid if the `eevee::Instance` is a baking instance. */ + Vector grids; + Vector cubes; + private: Instance &inst_; diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index eb13fd1d965..0f625d94a86 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -239,14 +239,20 @@ Material &MaterialModule::material_sync(Object *ob, Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() { Material mat; - /* Order is important for transparent. */ - mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type); - mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); - if (true) { - /* TODO(fclem): This can be expensive since it can trigger a shader compilation. So better - * avoid this if we can. */ + if (inst_.is_baking()) { + mat.prepass = MaterialPass(); + /* TODO(fclem): Still need the shading pass for correct attribute extraction. Would be better + * to avoid this shader compilation in another context. */ + mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); mat.capture = material_pass_get(ob, blender_mat, MAT_PIPE_CAPTURE, geometry_type); } + else { + /* Order is important for transparent. */ + mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type); + mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type); + mat.capture = MaterialPass(); + } + if (blender_mat->blend_shadow == MA_BS_NONE) { mat.shadow = MaterialPass(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 23ad5525d36..5d4ec4288ca 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -514,10 +514,10 @@ void CapturePipeline::sync() /* Surfel output is done using a SSBO, so no need for a fragment shader output color or depth. */ /* WORKAROUND: Avoid rasterizer discard, but the shaders actually use no fragment output. */ surface_ps_.state_set(DRW_STATE_WRITE_STENCIL); - surface_ps_.framebuffer_set(&inst_.irradiance_cache.empty_raster_fb_); + surface_ps_.framebuffer_set(&inst_.irradiance_cache.bake.empty_raster_fb_); - surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.irradiance_cache.surfels_buf_); - surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.irradiance_cache.capture_info_buf_); + surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.irradiance_cache.bake.surfels_buf_); + surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.irradiance_cache.bake.capture_info_buf_); surface_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); /* TODO(fclem): Remove. There should be no view dependent behavior during capture. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 521b6d603df..16a139ac766 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -21,7 +21,13 @@ namespace blender::eevee { void Sampling::init(const Scene *scene) { - sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : scene->eevee.taa_render_samples; + if (inst_.is_baking()) { + sample_count_ = max_ii(1, scene->eevee.gi_irradiance_samples); + } + else { + sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : + scene->eevee.taa_render_samples; + } if (sample_count_ == 0) { BLI_assert(inst_.is_viewport()); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index a316eef9293..dcb206d258c 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -838,6 +838,11 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, struct Depsgraph *depsgraph, void (*callback)(void *vedata, void *user_data), void *user_data); +/** + * Same as `DRW_custom_pipeline` but allow better code-flow than a callback. + */ +void DRW_custom_pipeline_begin(DrawEngineType *draw_engine_type, struct Depsgraph *depsgraph); +void DRW_custom_pipeline_end(void); /** * Used when the render engine want to redo another cache populate inside the same render frame. diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b84c15f0471..d59e569bb8f 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2094,10 +2094,7 @@ void DRW_render_object_iter( drw_task_graph_deinit(); } -void DRW_custom_pipeline(DrawEngineType *draw_engine_type, - struct Depsgraph *depsgraph, - void (*callback)(void *vedata, void *user_data), - void *user_data) +void DRW_custom_pipeline_begin(DrawEngineType *draw_engine_type, struct Depsgraph *depsgraph) { Scene *scene = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); @@ -2124,11 +2121,11 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); - ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, - draw_engine_type); + DRW_view_data_engine_data_get_ensure(DST.view_data_active, draw_engine_type); +} - /* Execute the callback */ - callback(data, user_data); +void DRW_custom_pipeline_end() +{ DST.buffer_finish_called = false; DRW_smoke_exit(DST.vmempool); @@ -2147,6 +2144,21 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, drw_manager_exit(&DST); } +void DRW_custom_pipeline(DrawEngineType *draw_engine_type, + struct Depsgraph *depsgraph, + void (*callback)(void *vedata, void *user_data), + void *user_data) +{ + DRW_custom_pipeline_begin(draw_engine_type, depsgraph); + + ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + draw_engine_type); + /* Execute the callback. */ + callback(data, user_data); + + DRW_custom_pipeline_end(); +} + void DRW_cache_restart(void) { DRW_smoke_exit(DST.vmempool); diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 357fb5a87e5..94ad46be693 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -150,11 +150,11 @@ typedef struct LightCacheIrradianceGrid { /** Number of surfels in the cache. */ int surfels_len; /** Currently only used at runtime for debugging the baking process. Not written to file. */ - // struct GPUStorageBuf *surfels; + void *surfels; /** Irradiance Data. Stored as spherical harmonic. */ - // LightCacheTexture *irradiance_L0_L1_a; - // LightCacheTexture *irradiance_L0_L1_b; - // LightCacheTexture *irradiance_L0_L1_c; + LightCacheTexture irradiance_L0_L1_a; + LightCacheTexture irradiance_L0_L1_b; + LightCacheTexture irradiance_L0_L1_c; } LightCacheIrradianceGrid; typedef struct LightCache { -- 2.30.2 From 6669771215c81607a2fe8542666143fb56d45ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 23 Mar 2023 18:16:22 +0100 Subject: [PATCH 08/58] EEVEE-Next: LightBake: Fix few mistakes preventing correct baking & debug Was missing manager sync, a buffer read and copy-on-write tagging. --- .../blender/draw/engines/eevee_next/eevee_instance.cc | 8 +++++++- .../draw/engines/eevee_next/eevee_irradiance_cache.cc | 10 ++++------ .../draw/engines/eevee_next/eevee_lightcache.cc | 2 ++ .../blender/draw/engines/eevee_next/eevee_material.cc | 4 +++- .../eevee_next/shaders/eevee_debug_surfels_frag.glsl | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 37e8dda0949..15d72c5e732 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -520,7 +520,11 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, }; /* Count probes. */ - custom_pipeline_wrapper([&]() { render_sync(); }); + custom_pipeline_wrapper([&]() { + manager->begin_sync(); + render_sync(); + manager->end_sync(); + }); /* Allocate CPU storage. */ r_light_cache = this->light_cache_create(light_probes.grids, light_probes.cubes); @@ -537,7 +541,9 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, for (auto i : light_probes.grids.index_range()) { custom_pipeline_wrapper([&]() { /* TODO: lightprobe visibility group option. */ + manager->begin_sync(); render_sync(); + manager->end_sync(); irradiance_cache.bake.surfels_create(light_probes.grids[i]); irradiance_cache.bake.surfels_lights_eval(); }); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 0804cfb7a15..8bf7e6f1804 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -37,7 +37,7 @@ void IrradianceCache::debug_pass_sync() LightCache *light_cache = inst_.scene->eevee.light_cache_data; if (light_cache == nullptr || light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION || - light_cache->grids == nullptr) { + light_cache->grids == nullptr || light_cache->grid_len == 0) { return; } @@ -101,7 +101,7 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) using namespace blender::math; /* Attachment-less frame-buffer. */ - empty_raster_fb_.ensure(int2(10 * 4)); + empty_raster_fb_.ensure(int2(20 * 4)); /** We could use multi-view rendering here to avoid multiple submissions but it is unlikely to * make any difference. The bottleneck is still the light propagation loop. */ @@ -111,7 +111,6 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) CartesianBasis basis = from_orthonormal_axes(AxisSigned(axis).next_after(), axis); view_.sync(from_rotation(basis), winmat); - view_.visibility_test(false); inst_.pipelines.capture.render(view_); }; @@ -133,12 +132,11 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); capture_info_buf_.read(); if (capture_info_buf_.surfel_len == 0) { - /* Not surfel to allocated. */ + /* No surfel to allocated. */ return; } /* TODO(fclem): Check for GL limit and abort if the surfel cache doesn't fit the GPU memory. */ - std::cout << "Resize " << capture_info_buf_.surfel_len << std::endl; surfels_buf_.resize(capture_info_buf_.surfel_len); DRW_stats_group_start("IrradianceBake.SurfelsCreate"); @@ -173,7 +171,7 @@ void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); - std::cout << "Read " << capture_info_buf_.surfel_len << std::endl; + capture_info_buf_.read(); surfels_buf_.read(); light_cache_grid.surfels_len = capture_info_buf_.surfel_len; light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data()); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index 32c3caf4c00..c9915c7265c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -110,6 +110,8 @@ class LightBake { } EEVEE_NEXT_lightcache_info_update(&original_scene->eevee); + + DEG_id_tag_update(&original_scene->id, ID_RECALC_COPY_ON_WRITE); } /** diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 0f625d94a86..677ba8015dc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -164,9 +164,11 @@ MaterialPass MaterialModule::material_pass_get(Object *ob, blender_mat->nodetree : default_surface_ntree_.nodetree_get(blender_mat); + bool use_deferred_compilation = inst_.is_viewport(); + MaterialPass matpass = MaterialPass(); matpass.gpumat = inst_.shaders.material_shader_get( - blender_mat, ntree, pipeline_type, geometry_type, true); + blender_mat, ntree, pipeline_type, geometry_type, use_deferred_compilation); switch (GPU_material_status(matpass.gpumat)) { case GPU_MAT_SUCCESS: diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl index 156773abb26..1ed95da36a4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl @@ -6,7 +6,7 @@ void main() switch (eDebugMode(debug_mode)) { default: case DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: - out_color = vec4(surfel.normal, 1.0); + out_color = vec4(pow(surfel.normal * 0.5 + 0.5, vec3(2.2)), 1.0); break; case DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: out_color = vec4(surfel.radiance, 1.0); -- 2.30.2 From caa0e7edcfc122a26a701cba9f2b3ffe4385752e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 24 Mar 2023 17:34:20 +0100 Subject: [PATCH 09/58] EEVEE-Next: IrradianceBake: Correct surfel placement from grid objects --- source/blender/draw/CMakeLists.txt | 5 +- .../draw/engines/eevee_next/eevee_defines.hh | 3 + .../eevee_next/eevee_irradiance_cache.cc | 118 ++++++++++++++---- .../eevee_next/eevee_irradiance_cache.hh | 21 +++- .../engines/eevee_next/eevee_lightcache.h | 3 +- .../engines/eevee_next/eevee_lightprobe.cc | 7 +- .../engines/eevee_next/eevee_lightprobe.hh | 2 +- .../draw/engines/eevee_next/eevee_material.cc | 6 + .../draw/engines/eevee_next/eevee_shader.cc | 6 + .../draw/engines/eevee_next/eevee_shader.hh | 4 + .../shaders/eevee_light_eval_lib.glsl | 6 +- .../shaders/eevee_surfel_light_comp.glsl | 79 ++++++++++++ .../shaders/eevee_surfel_list_build_comp.glsl | 10 ++ .../shaders/eevee_surfel_list_sort_comp.glsl | 12 ++ .../infos/eevee_irradiance_cache_info.hh | 26 ++++ 15 files changed, 274 insertions(+), 34 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6408a0cd688..2ad6c7fa56e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -428,7 +428,6 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_camera_lib.glsl engines/eevee_next/shaders/eevee_colorspace_lib.glsl engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl - engines/eevee_next/shaders/eevee_transparency_lib.glsl engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -500,6 +499,10 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surf_lib.glsl engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl engines/eevee_next/shaders/eevee_surf_world_frag.glsl + engines/eevee_next/shaders/eevee_surfel_light_comp.glsl + engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl + engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl + engines/eevee_next/shaders/eevee_transparency_lib.glsl engines/eevee_next/shaders/eevee_velocity_lib.glsl engines/eevee_next/eevee_defines.hh diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index ce179693ec4..ef5c2bd487b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -86,6 +86,9 @@ #define DOF_GATHER_GROUP_SIZE DOF_TILES_SIZE #define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2) +/* IrradianceBake. */ +#define SURFEL_LIGHT_GROUP_SIZE 256 + /* Resource bindings. */ /* Texture. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 8bf7e6f1804..617721f8127 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "DNA_lightprobe_types.h" + #include "GPU_capabilities.h" +#include "GPU_debug.h" + +#include "BLI_math_rotation.hh" #include "eevee_instance.hh" @@ -19,10 +23,7 @@ void IrradianceCache::init() void IrradianceCache::sync() { - if (inst_.is_baking()) { - bake.sync(); - } - else { + if (!inst_.is_baking()) { debug_pass_sync(); } } @@ -87,11 +88,7 @@ void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb) /** \name Baking * \{ */ -void IrradianceBake::sync() -{ -} - -void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) +void IrradianceBake::surfels_create(const IrradianceGrid &grid) { /** * We rasterize the scene along the 3 axes. Each generated fragment will write a surface element @@ -100,32 +97,47 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) */ using namespace blender::math; - /* Attachment-less frame-buffer. */ - empty_raster_fb_.ensure(int2(20 * 4)); + float4x4 transform(grid.transform); + float3 location, scale; + Quaternion rotation; + math::to_loc_rot_scale(transform, location, rotation, scale); /** We could use multi-view rendering here to avoid multiple submissions but it is unlikely to * make any difference. The bottleneck is still the light propagation loop. */ - auto render_axis = [&](Axis axis) { - /* TODO(fclem): get scene bounds GPU or CPU side. Or use the irradiance grid extents. */ - float4x4 winmat = math::projection::orthographic(-20.0f, 20.0f, -20.0f, 20.0f, -20.0f, 20.0f); - - CartesianBasis basis = from_orthonormal_axes(AxisSigned(axis).next_after(), axis); - view_.sync(from_rotation(basis), winmat); - - inst_.pipelines.capture.render(view_); + auto sync_view = [&](View &view, CartesianBasis basis) { + float3 extent = scale; + float4x4 winmat = projection::orthographic( + -extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z); + float4x4 viewinv = math::from_loc_rot(location, + rotation * to_quaternion(basis)); + view.sync(invert(viewinv), winmat); }; + sync_view(view_x_, basis_x_); + sync_view(view_y_, basis_y_); + sync_view(view_z_, basis_z_); + + /* Surfel per unit distance. */ + float surfel_density = 2.0f; + grid_pixel_extent_ = max(int3(1), int3(surfel_density * 2.0f * scale)); + DRW_stats_group_start("IrradianceBake.SurfelsCount"); + GPU_debug_capture_begin(); /* Raster the scene to query the number of surfel needed. */ capture_info_buf_.do_surfel_count = true; capture_info_buf_.do_surfel_output = false; capture_info_buf_.surfel_len = 0u; capture_info_buf_.push_update(); - render_axis(Axis::X); - render_axis(Axis::Y); - render_axis(Axis::Z); + empty_raster_fb_.ensure(grid_pixel_extent_.yz()); + inst_.pipelines.capture.render(view_x_); + empty_raster_fb_.ensure(int2(grid_pixel_extent_.x, grid_pixel_extent_.z)); + inst_.pipelines.capture.render(view_y_); + empty_raster_fb_.ensure(grid_pixel_extent_.xy()); + inst_.pipelines.capture.render(view_z_); + + GPU_debug_capture_end(); DRW_stats_group_end(); /* Allocate surfel pool. */ @@ -146,15 +158,71 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */) capture_info_buf_.do_surfel_output = true; capture_info_buf_.surfel_len = 0u; capture_info_buf_.push_update(); - render_axis(Axis::X); - render_axis(Axis::Y); - render_axis(Axis::Z); + empty_raster_fb_.ensure(grid_pixel_extent_.yz()); + inst_.pipelines.capture.render(view_x_); + empty_raster_fb_.ensure(int2(grid_pixel_extent_.x, grid_pixel_extent_.z)); + inst_.pipelines.capture.render(view_y_); + empty_raster_fb_.ensure(grid_pixel_extent_.xy()); + inst_.pipelines.capture.render(view_z_); DRW_stats_group_end(); + + /* Sync needs to happen after `surfels_buf_` is resized for correct dispatch size. */ + sync(); +} + +void IrradianceBake::sync() +{ + { + PassSimple &pass = surfel_light_eval_ps_; + pass.init(); + /* Apply lights contribution to scene surfel representation. */ + pass.shader_set(inst_.shaders.static_shader_get(SURFEL_LIGHT)); + pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + inst_.lights.bind_resources(&pass); + inst_.shadows.bind_resources(&pass); + /* Sync with the surfel creation stage. */ + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + pass.dispatch(int3(divide_ceil_u(surfels_buf_.size(), SURFEL_LIGHT_GROUP_SIZE), 1, 1)); + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + } + { + PassSimple &pass = surfel_light_propagate_ps_; + pass.init(); + { + PassSimple::Sub &sub = pass.sub("ListBuild"); + sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_BUILD)); + /* TODO */ + } + { + PassSimple::Sub &sub = pass.sub("ListSort"); + sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT)); + /* TODO */ + } + } + { + PassSimple &pass = irradiance_capture_ps_; + pass.init(); + /* TODO */ + } } void IrradianceBake::surfels_lights_eval() { + GPU_debug_capture_begin(); + /* Use the last setup view. This should work since the view is orthographic. */ + /* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */ + inst_.render_buffers.acquire(int2(1)); + inst_.lights.set_view(view_z_, grid_pixel_extent_.xy()); + /* TODO: Instead of using the volume tagging we should tag using the surfels. */ + inst_.shadows.set_view(view_z_); + inst_.render_buffers.release(); + + inst_.manager->submit(surfel_light_eval_ps_); + + GPU_debug_capture_end(); } void IrradianceBake::propagate_light_sample() diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 0e18b235149..6d6367bb3fa 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -33,7 +33,26 @@ class IrradianceBake { CaptureInfoBuf capture_info_buf_; /** Framebuffer. */ Framebuffer empty_raster_fb_ = {"empty_raster_fb_"}; - View view_ = {"ortho_raster_view"}; + /** Evaluate light object contribution and store result to surfel. */ + PassSimple surfel_light_eval_ps_ = {"LightEval"}; + /** Propagate light from surfel to surfel. */ + PassSimple surfel_light_propagate_ps_ = {"LightPropagate"}; + /** Capture surfel lighting to irradiance samples. */ + PassSimple irradiance_capture_ps_ = {"IrradianceCapture"}; + + /** Basis orientation for each baking projection. */ + math::CartesianBasis basis_x_ = math::from_orthonormal_axes(math::AxisSigned::Y_POS, + math::AxisSigned::X_POS); + math::CartesianBasis basis_y_ = math::from_orthonormal_axes(math::AxisSigned::Z_POS, + math::AxisSigned::Y_POS); + math::CartesianBasis basis_z_ = math::from_orthonormal_axes(math::AxisSigned::X_POS, + math::AxisSigned::Z_POS); + /** Views for each baking projection. */ + View view_x_ = {"BakingViewX"}; + View view_y_ = {"BakingViewY"}; + View view_z_ = {"BakingViewZ"}; + /** Pixel resolution in each of the projection axes. Match the target surfel density. */ + int3 grid_pixel_extent_ = int3(0); public: IrradianceBake(Instance &inst) : inst_(inst){}; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.h b/source/blender/draw/engines/eevee_next/eevee_lightcache.h index 360da271c74..c96b944e292 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.h +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.h @@ -21,8 +21,7 @@ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake; * Create the job description. * This is called for async (modal) bake operator. * The actual work will be done by `EEVEE_NEXT_lightbake_job()`. - * Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()` or reuse data from an already - * existing baking job. + * Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()`. * IMPORTANT: Must run on the main thread because of potential GPUContext creation. */ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 6e30b531416..3d6ad935162 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -26,7 +26,7 @@ void LightProbeModule::begin_sync() cubes.clear(); } -void LightProbeModule::sync_grid(ObjectHandle &handle) +void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle) { LightProbe &grid = grid_map_.lookup_or_add_default(handle.object_key); grid.used = true; @@ -36,7 +36,8 @@ void LightProbeModule::sync_grid(ObjectHandle &handle) } if (inst_.is_baking()) { - grids.append({}); + const ::LightProbe *light_probe = (const ::LightProbe *)ob->data; + grids.append({float4x4(ob->object_to_world), &light_probe->grid_resolution_x}); } } @@ -65,7 +66,7 @@ void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle) /* TODO(fclem): Remove support? Add support? */ return; case LIGHTPROBE_TYPE_GRID: - sync_grid(handle); + sync_grid(ob, handle); return; } BLI_assert_unreachable(); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index edbf5f36c98..cfbae98f908 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -54,7 +54,7 @@ class LightProbeModule { void begin_sync(); void sync_cube(ObjectHandle &handle); - void sync_grid(ObjectHandle &handle); + void sync_grid(const Object *ob, ObjectHandle &handle); void sync_probe(const Object *ob, ObjectHandle &handle); diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 677ba8015dc..648cc67663a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -264,6 +264,12 @@ Material &MaterialModule::material_sync(Object *ob, mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) && GPU_material_flag_get(mat.shading.gpumat, GPU_MATFLAG_TRANSPARENT); + if (inst_.is_baking()) { + /* WORKAROUND(fclem): This is to request the shadow for the surfels. This will well + * over-request the number of shadow tiles. A better way would be to request from the surfels + * directly. */ + mat.is_alpha_blend_transparent = true; + } return mat; }); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 586c043eefd..d511d491f4a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -172,6 +172,12 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_shadow_tag_usage_opaque"; case SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT: return "eevee_shadow_tag_usage_transparent"; + case SURFEL_LIGHT: + return "eevee_surfel_light"; + case SURFEL_LIST_BUILD: + return "eevee_surfel_list_build"; + case SURFEL_LIST_SORT: + return "eevee_surfel_list_sort"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index d987105e3fc..8b70b00eb08 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -80,6 +80,10 @@ enum eShaderType { SHADOW_TILEMAP_TAG_USAGE_OPAQUE, SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT, + SURFEL_LIGHT, + SURFEL_LIST_BUILD, + SURFEL_LIST_SORT, + MAX_SHADER_TYPE, }; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl index 4a57a850d34..4215e794b21 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl @@ -22,7 +22,7 @@ void light_eval_ex(ClosureDiffuse diffuse, vec3 P, vec3 Ng, vec3 V, - float vP_z, + float vP_z, /* TODO(fclem): Remove, is unused. */ float thickness, vec4 ltc_mat, uint l_idx, @@ -111,7 +111,11 @@ void light_eval(ClosureDiffuse diffuse, } LIGHT_FOREACH_END +#ifdef GPU_FRAGMENT_SHADER vec2 px = gl_FragCoord.xy; +#else + vec2 px = vec2(0.0); +#endif LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx) { light_eval_ex(diffuse, reflection, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl new file mode 100644 index 00000000000..a0fb506f89c --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl @@ -0,0 +1,79 @@ + +/** + * Apply lights contribution to scene surfel representation. + */ + +#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) + +void light_eval_surfel( + ClosureDiffuse diffuse, vec3 P, vec3 Ng, float thickness, inout vec3 out_diffuse) +{ + /* Dummy closure. Not used. */ + ClosureReflection reflection; + reflection.N = vec3(1.0, 0.0, 0.0); + reflection.roughness = 0.0; + vec3 out_specular = vec3(0.0); + /* Dummy ltc mat parameters. Not used since we have no reflections. */ + vec4 ltc_mat_dummy = utility_tx_sample(utility_tx, vec2(0.0), UTIL_LTC_MAT_LAYER); + + vec3 V = Ng; + float vP_z = 0.0; + + LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { + light_eval_ex(diffuse, + reflection, + true, + P, + Ng, + V, + vP_z, + thickness, + ltc_mat_dummy, + l_idx, + out_diffuse, + out_specular); + } + LIGHT_FOREACH_END + + LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL (light_cull_buf, l_idx) { + light_eval_ex(diffuse, + reflection, + false, + P, + Ng, + V, + vP_z, + thickness, + ltc_mat_dummy, + l_idx, + out_diffuse, + out_specular); + } + LIGHT_FOREACH_END +} + +void main() +{ + int index = int(gl_GlobalInvocationID.x); + if (index >= capture_info_buf.surfel_len) { + return; + } + + Surfel surfel = surfel_buf[index]; + + ClosureDiffuse diffuse_data; + diffuse_data.N = surfel.normal; + /* TODO: These could saved inside the surfel. */ + diffuse_data.sss_radius = vec3(0.0); + diffuse_data.sss_id = 0u; + float thickness = 0.0; + + vec3 diffuse_light = vec3(0.0); + vec3 reflection_light = vec3(0.0); + + light_eval_surfel(diffuse_data, surfel.position, surfel.normal, thickness, diffuse_light); + + surfel.radiance += diffuse_light * surfel.albedo; + + surfel_buf[index] = surfel; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl new file mode 100644 index 00000000000..acb0b938175 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl @@ -0,0 +1,10 @@ + +/** + * Takes scene surfel representation and build list of surfels aligning in a given direction. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +void main() +{ +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl new file mode 100644 index 00000000000..0cad587b4e0 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl @@ -0,0 +1,12 @@ + +/** + * Sort a buffer of surfel list by distance along a direction. + * The resulting surfel lists are then the equivalent of a series of ray cast in the same + * direction. The fact that the surfels are sorted gives proper occlusion. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +void main() +{ +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 2de51b6907c..412e3e10ab2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -17,3 +17,29 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .push_constant(Type::FLOAT, "surfel_radius") .push_constant(Type::INT, "debug_mode") .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_surfel_light) + .local_group_size(CULLING_SELECT_GROUP_SIZE) + .additional_info("eevee_shared", + "draw_view", + "eevee_utility_texture", + "eevee_light_data", + "eevee_shadow_data") + .compute_source("eevee_surfel_light_comp.glsl") + .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") + .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_surfel_list_build) + .local_group_size(CULLING_SELECT_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .compute_source("eevee_surfel_list_build_comp.glsl") + .storage_buf(0, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort) + .local_group_size(CULLING_SELECT_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .compute_source("eevee_surfel_list_sort_comp.glsl") + .storage_buf(0, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]") + .do_static_compilation(true); -- 2.30.2 From 0297bde6290d38d9b7267bab358793d7d59d03d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 24 Mar 2023 17:34:39 +0100 Subject: [PATCH 10/58] EEVEE-Next: Debug: Fix surfel debugging showing background --- .../engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl index 1ed95da36a4..956bd528ed1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl @@ -6,10 +6,10 @@ void main() switch (eDebugMode(debug_mode)) { default: case DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: - out_color = vec4(pow(surfel.normal * 0.5 + 0.5, vec3(2.2)), 1.0); + out_color = vec4(pow(surfel.normal * 0.5 + 0.5, vec3(2.2)), 0.0); break; case DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: - out_color = vec4(surfel.radiance, 1.0); + out_color = vec4(surfel.radiance, 0.0); break; } -- 2.30.2 From 75b62a9cfb2cecde91300ae0f19c2a746bfe671a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 24 Mar 2023 20:46:02 +0100 Subject: [PATCH 11/58] EEVEE-Next: IrradianceBake: Add surfel list & sorting --- .../draw/engines/eevee_next/eevee_defines.hh | 3 +- .../eevee_next/eevee_irradiance_cache.cc | 174 ++++++++++++------ .../eevee_next/eevee_irradiance_cache.hh | 20 ++ .../draw/engines/eevee_next/eevee_sampling.cc | 11 ++ .../draw/engines/eevee_next/eevee_sampling.hh | 7 + .../engines/eevee_next/eevee_shader_shared.hh | 33 ++-- .../shaders/eevee_debug_surfels_vert.glsl | 11 ++ .../shaders/eevee_surfel_list_build_comp.glsl | 29 ++- .../shaders/eevee_surfel_list_sort_comp.glsl | 105 ++++++++++- .../infos/eevee_irradiance_cache_info.hh | 15 +- source/blender/draw/tests/eevee_test.cc | 109 +++++++++++ 11 files changed, 433 insertions(+), 84 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index ef5c2bd487b..32769c0638b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -87,7 +87,8 @@ #define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2) /* IrradianceBake. */ -#define SURFEL_LIGHT_GROUP_SIZE 256 +#define SURFEL_GROUP_SIZE 256 +#define SURFEL_LIST_GROUP_SIZE 256 /* Resource bindings. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 617721f8127..6f5709a2a90 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -26,6 +26,9 @@ void IrradianceCache::sync() if (!inst_.is_baking()) { debug_pass_sync(); } + else { + bake.sync(); + } } void IrradianceCache::debug_pass_sync() @@ -88,6 +91,55 @@ void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb) /** \name Baking * \{ */ +void IrradianceBake::sync() +{ + { + PassSimple &pass = surfel_light_eval_ps_; + pass.init(); + /* Apply lights contribution to scene surfel representation. */ + pass.shader_set(inst_.shaders.static_shader_get(SURFEL_LIGHT)); + pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + inst_.lights.bind_resources(&pass); + inst_.shadows.bind_resources(&pass); + /* Sync with the surfel creation stage. */ + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + pass.dispatch(&dispatch_per_surfel_); + } + { + PassSimple &pass = surfel_light_propagate_ps_; + pass.init(); + { + PassSimple::Sub &sub = pass.sub("ListBuild"); + sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_BUILD)); + sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + sub.bind_ssbo("list_start_buf", &list_start_buf_); + sub.bind_ssbo("list_info_buf", &list_info_buf_); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); + sub.dispatch(&dispatch_per_surfel_); + } + { + PassSimple::Sub &sub = pass.sub("ListSort"); + sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT)); + sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + sub.bind_ssbo("list_start_buf", &list_start_buf_); + sub.bind_ssbo("list_info_buf", &list_info_buf_); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); + sub.dispatch(&dispatch_per_list_); + } + { + // PassSimple::Sub &sub = pass.sub("LightPropagate"); + } + } + { + PassSimple &pass = irradiance_capture_ps_; + pass.init(); + /* TODO */ + } +} + void IrradianceBake::surfels_create(const IrradianceGrid &grid) { /** @@ -97,19 +149,32 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) */ using namespace blender::math; - float4x4 transform(grid.transform); - float3 location, scale; - Quaternion rotation; - math::to_loc_rot_scale(transform, location, rotation, scale); + const float4x4 transform(grid.transform); + float3 scale; + math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale); - /** We could use multi-view rendering here to avoid multiple submissions but it is unlikely to + /* Extract bounding box. Order is arbitrary as it is not important for our usage. */ + const std::array bbox_corners({float3{+1, +1, +1}, + float3{-1, +1, +1}, + float3{+1, -1, +1}, + float3{-1, -1, +1}, + float3{+1, +1, -1}, + float3{-1, +1, -1}, + float3{+1, -1, -1}, + float3{-1, -1, -1}}); + grid_bbox_vertices.clear(); + for (const float3 &point : bbox_corners) { + grid_bbox_vertices.append(transform_point(transform, point)); + } + + /* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to * make any difference. The bottleneck is still the light propagation loop. */ auto sync_view = [&](View &view, CartesianBasis basis) { float3 extent = scale; float4x4 winmat = projection::orthographic( -extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z); - float4x4 viewinv = math::from_loc_rot(location, - rotation * to_quaternion(basis)); + float4x4 viewinv = math::from_loc_rot( + grid_location_, grid_orientation_ * to_quaternion(basis)); view.sync(invert(viewinv), winmat); }; @@ -117,12 +182,9 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) sync_view(view_y_, basis_y_); sync_view(view_z_, basis_z_); - /* Surfel per unit distance. */ - float surfel_density = 2.0f; - grid_pixel_extent_ = max(int3(1), int3(surfel_density * 2.0f * scale)); + grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * 2.0f * scale)); DRW_stats_group_start("IrradianceBake.SurfelsCount"); - GPU_debug_capture_begin(); /* Raster the scene to query the number of surfel needed. */ capture_info_buf_.do_surfel_count = true; @@ -137,7 +199,6 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) empty_raster_fb_.ensure(grid_pixel_extent_.xy()); inst_.pipelines.capture.render(view_z_); - GPU_debug_capture_end(); DRW_stats_group_end(); /* Allocate surfel pool. */ @@ -151,6 +212,8 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) /* TODO(fclem): Check for GL limit and abort if the surfel cache doesn't fit the GPU memory. */ surfels_buf_.resize(capture_info_buf_.surfel_len); + dispatch_per_surfel_.x = divide_ceil_u(surfels_buf_.size(), SURFEL_GROUP_SIZE); + DRW_stats_group_start("IrradianceBake.SurfelsCreate"); /* Raster the scene to generate the surfels. */ @@ -166,52 +229,10 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) inst_.pipelines.capture.render(view_z_); DRW_stats_group_end(); - - /* Sync needs to happen after `surfels_buf_` is resized for correct dispatch size. */ - sync(); -} - -void IrradianceBake::sync() -{ - { - PassSimple &pass = surfel_light_eval_ps_; - pass.init(); - /* Apply lights contribution to scene surfel representation. */ - pass.shader_set(inst_.shaders.static_shader_get(SURFEL_LIGHT)); - pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); - pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); - pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); - inst_.lights.bind_resources(&pass); - inst_.shadows.bind_resources(&pass); - /* Sync with the surfel creation stage. */ - pass.barrier(GPU_BARRIER_SHADER_STORAGE); - pass.dispatch(int3(divide_ceil_u(surfels_buf_.size(), SURFEL_LIGHT_GROUP_SIZE), 1, 1)); - pass.barrier(GPU_BARRIER_SHADER_STORAGE); - } - { - PassSimple &pass = surfel_light_propagate_ps_; - pass.init(); - { - PassSimple::Sub &sub = pass.sub("ListBuild"); - sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_BUILD)); - /* TODO */ - } - { - PassSimple::Sub &sub = pass.sub("ListSort"); - sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT)); - /* TODO */ - } - } - { - PassSimple &pass = irradiance_capture_ps_; - pass.init(); - /* TODO */ - } } void IrradianceBake::surfels_lights_eval() { - GPU_debug_capture_begin(); /* Use the last setup view. This should work since the view is orthographic. */ /* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */ inst_.render_buffers.acquire(int2(1)); @@ -221,16 +242,49 @@ void IrradianceBake::surfels_lights_eval() inst_.render_buffers.release(); inst_.manager->submit(surfel_light_eval_ps_); - - GPU_debug_capture_end(); } void IrradianceBake::propagate_light_sample() { - /* Pick random ray direction over the sphere. */ - /* Project to regular grid and create the surfels lists. */ - /* Sort the surfels lists. */ - /* Propagate light. */ + using namespace blender::math; + + float2 rand_uv = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_FILTER_U); + const float3 ray_direction = inst_.sampling.sample_hemisphere(rand_uv); + const float3 up = ray_direction; + /* Find the closest axis. */ + const float3 grid_local_ray_direction = transform_point(grid_orientation_, ray_direction); + Axis closest_grid_axis = Axis::from_int(dominant_axis(grid_local_ray_direction)); + /* Use one of the other 2 grid axes to get a reference right vector. */ + Axis right_axis = AxisSigned(closest_grid_axis).next_after().axis(); + const float3 grid_right = from_rotation(grid_orientation_)[right_axis.as_int()]; + /* Create a view around the grid position with the ray direction as up axis. + * The other axes are aligned to the grid local axes to avoid to allocate too many list start. */ + const float4x4 viewmat = invert( + from_orthonormal_axes(grid_location_, normalize(cross(up, grid_right)), up)); + + /* Compute projection bounds. */ + float2 min, max; + INIT_MINMAX2(min, max); + for (const float3 &point : grid_bbox_vertices) { + min_max(transform_point(viewmat, point).xy(), min, max); + } + + /* NOTE: Z values do not really matter since we are not doing any rasterization. */ + const float4x4 winmat = projection::orthographic(min.x, max.x, min.y, max.y, 0, 1); + + View ray_view = {"RayProjectionView"}; + ray_view.sync(viewmat, winmat); + + list_info_buf_.ray_grid_size = math::max(int2(1), int2(surfel_density_ * (max - min))); + list_info_buf_.list_max = list_info_buf_.ray_grid_size.x * list_info_buf_.ray_grid_size.y; + list_info_buf_.push_update(); + + dispatch_per_list_.x = divide_ceil_u(list_info_buf_.list_max, SURFEL_LIST_GROUP_SIZE); + + list_start_buf_.resize(ceil_to_multiple_u(list_info_buf_.list_max, 4)); + + GPU_storagebuf_clear(list_start_buf_, -1); + inst_.manager->submit(surfel_light_propagate_ps_, ray_view); } void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 6d6367bb3fa..2f03e2370e8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -8,6 +8,8 @@ #include "DNA_lightprobe_types.h" +#include "BLI_math_quaternion_types.hh" + #include "eevee_lightprobe.hh" #include "eevee_shader_shared.hh" @@ -53,6 +55,24 @@ class IrradianceBake { View view_z_ = {"BakingViewZ"}; /** Pixel resolution in each of the projection axes. Match the target surfel density. */ int3 grid_pixel_extent_ = int3(0); + /** Information for surfel list building. */ + SurfelListInfoBuf list_info_buf_ = {"list_info_buf_"}; + /** List array containing list start surfel index. Cleared to -1. */ + StorageArrayBuffer list_start_buf_ = {"list_start_buf_"}; + + /* Dispatch size for per surfel workload. */ + int3 dispatch_per_surfel_ = int3(1); + /* Dispatch size for per surfel list workload. */ + int3 dispatch_per_list_ = int3(1); + + /* Surfel per unit distance. */ + float surfel_density_ = 2.0f; + /* Orientation of the irradiance grid being baked. */ + math::Quaternion grid_orientation_; + /* Object center of the irradiance grid being baked. */ + float3 grid_location_; + /* Bounding box vertices of the irradiance grid being baked. In world space. */ + Vector grid_bbox_vertices; public: IrradianceBake(Instance &inst) : inst_(inst){}; diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 16a139ac766..e4b33bb40dd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -10,6 +10,9 @@ #include "BLI_rand.h" +#include "BLI_math_base.hh" +#include "BLI_math_base_safe.h" + #include "eevee_instance.hh" #include "eevee_sampling.hh" @@ -180,6 +183,14 @@ float2 Sampling::sample_disk(const float2 &rand) return sqrtf(rand.x) * float2(cosf(omega), sinf(omega)); } +float3 Sampling::sample_hemisphere(const float2 &rand) +{ + const float omega = rand.y * 2.0f * M_PI; + const float cos_theta = rand.x; + const float sin_theta = safe_sqrtf(1.0f - square_f(cos_theta)); + return float3(sin_theta * float2(cosf(omega), sinf(omega)), cos_theta); +} + float2 Sampling::sample_spiral(const float2 &rand) { /* Fibonacci spiral. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh index c2bf23d20fc..f70aac5f8ea 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh @@ -158,6 +158,13 @@ class Sampling { */ static float2 sample_disk(const float2 &rand); + /** + * Uniform hemisphere distribution. + * \a rand is 2 random float in the [0..1] range. + * Returns point on a Z positive hemisphere of radius 1 and centered on the origin. + */ + static float3 sample_hemisphere(const float2 &rand); + /** * Uniform disc distribution using Fibonacci spiral sampling. * \a rand is 2 random float in the [0..1] range. diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 681eeb6eb01..96c6d819979 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -833,16 +833,19 @@ static inline ShadowTileDataPacked shadow_tile_pack(ShadowTileData tile) struct Surfel { /** World position of the surfel. */ packed_float3 position; - int _pad0; + /** Previous surfel index in the ray link-list. Only valid after sorting. */ + int prev; /** World orientation of the surface. */ packed_float3 normal; - int _pad1; + /** Next surfel index in the ray link-list. */ + int next; /** Surface albedo to apply to incoming radiance. */ packed_float3 albedo; - int _pad2; - /** Accumulated reflected radiance at this point. */ + /** Distance along the ray direction for sorting. */ + float ray_distance; + /** Accumulated reflected radiance. */ packed_float3 radiance; - int _pad3; + int _pad0; }; BLI_STATIC_ASSERT_ALIGN(Surfel, 16) @@ -857,18 +860,15 @@ struct CaptureInfoData { }; BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16) -enum SurfelListEntryType : uint32_t { - ENTRY_SURFEL = 0u, - ENTRY_IRRADIANCE_SAMPLE = 1u, -}; +struct SurfelListInfoData { + /** Size of the grid used to project the surfels into linked lists. */ + int2 ray_grid_size; + /** Maximum number of list. Is equal to `ray_grid_size.x * ray_grid_size.y`. */ + int list_max; -struct SurfelListEntry { - uint next_entry_index; - SurfelListEntryType type; - uint payload; - uint _pad0; + int _pad0; }; -BLI_STATIC_ASSERT_ALIGN(SurfelListEntry, 16) +BLI_STATIC_ASSERT_ALIGN(SurfelListInfoData, 16) /** \} */ @@ -996,8 +996,9 @@ using ShadowPageCacheBuf = draw::StorageArrayBuffer; using ShadowTileMapClipBuf = draw::StorageArrayBuffer; using ShadowTileDataBuf = draw::StorageArrayBuffer; -using SurfelBuf = draw::StorageArrayBuffer; +using SurfelBuf = draw::StorageArrayBuffer; using CaptureInfoBuf = draw::StorageBuffer; +using SurfelListInfoBuf = draw::StorageBuffer; using VelocityGeometryBuf = draw::StorageArrayBuffer; using VelocityIndexBuf = draw::StorageArrayBuffer; using VelocityObjectBuf = draw::StorageArrayBuffer; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl index e0c82de7299..dc406ecf8b9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl @@ -1,11 +1,22 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_debug_draw_lib.glsl) void main() { surfel_index = gl_InstanceID; Surfel surfel = surfels_buf[surfel_index]; +#if 0 /* Debug surfel lists. TODO allow in release build with a dedicated shader. */ + if (gl_VertexID == 0 && surfel.next > -1) { + Surfel surfel_next = surfels_buf[surfel.next]; + vec4 line_color = (surfel.prev == -1) ? vec4(1.0, 1.0, 0.0, 1.0) : + (surfel_next.next == -1) ? vec4(0.0, 1.0, 1.0, 1.0) : + vec4(0.0, 1.0, 0.0, 1.0); + drw_debug_line(surfel_next.position, surfel.position, line_color); + } +#endif + vec3 lP; switch (gl_VertexID) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl index acb0b938175..982af7c5b0f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl @@ -1,10 +1,37 @@ /** * Takes scene surfel representation and build list of surfels aligning in a given direction. + * + * The lists head are allocated to fit the surfel granularity. + * + * Due to alignment the link and list head are split into several int arrays to avoid too much + * memory waste. + * + * Dispatch 1 thread per surfel. */ -#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) void main() { + int surfel_index = int(gl_GlobalInvocationID); + if (surfel_index >= capture_info_buf.surfel_len) { + return; + } + + vec4 hP = point_world_to_ndc(surfel_buf[surfel_index].position); + + surfel_buf[surfel_index].ray_distance = -hP.z; + + vec2 ssP_surfel = hP.xy * 0.5 + 0.5; + ivec2 ray_coord_on_grid = clamp(ivec2(ssP_surfel * vec2(list_info_buf.ray_grid_size)), + ivec2(0), + list_info_buf.ray_grid_size - 1); + int list_index = ray_coord_on_grid.y * list_info_buf.ray_grid_size.x + ray_coord_on_grid.x; + + /* NOTE: We only need to init the `list_start_buf` to -1 for the whole list to be valid since + * every surfel will load its `next` value from the list head. */ + surfel_buf[surfel_index].next = atomicExchange(list_start_buf[list_index], surfel_index); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl index 0cad587b4e0..a8cf64b9743 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl @@ -3,10 +3,113 @@ * Sort a buffer of surfel list by distance along a direction. * The resulting surfel lists are then the equivalent of a series of ray cast in the same * direction. The fact that the surfels are sorted gives proper occlusion. + * + * Sort by increasing `ray_distance`. Start of list is smallest value. + * + * Outputs a flat array of surfel indices. Each ray is a range inside the array. This allows + * parallel processing in the light propagation phase. + * Dispatched as 1 thread per list. */ -#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) + +/** + * A doubly-linked list implementation. + * IMPORTANT: It is not general purpose as it only cover the cases needed by this shader. + */ +struct List { + int first, last; +}; + +/* Return the split list after link_index. */ +List list_split_after(inout List original, int link_index) +{ + int next_link = surfel_buf[link_index].next; + int last_link = original.last; + + original.last = link_index; + + List split; + split.first = next_link; + split.last = last_link; + + surfel_buf[link_index].next = -1; + surfel_buf[next_link].prev = -1; + + return split; +} + +void list_add_tail(inout List list, int link_index) +{ + surfel_buf[link_index].next = -1; + surfel_buf[link_index].prev = list.last; + surfel_buf[list.last].next = link_index; + list.last = link_index; +} + +void list_insert_link_before(inout List list, int next_link, int new_link) +{ + if (list.first == next_link) { + /* At beginning of list. */ + list.first = new_link; + } + int prev_link = surfel_buf[next_link].prev; + surfel_buf[new_link].next = next_link; + surfel_buf[new_link].prev = prev_link; + surfel_buf[next_link].prev = new_link; + if (prev_link != -1) { + surfel_buf[prev_link].next = new_link; + } +} void main() { + int list_index = int(gl_GlobalInvocationID); + if (list_index >= list_info_buf.list_max) { + return; + } + + int list_start = list_start_buf[list_index]; + + if (list_start == -1) { + /* Empty list. */ + return; + } + + /* Create Surfel.prev pointers. */ + int prev_id = -1; + for (int i = list_start; i > -1; i = surfel_buf[i].next) { + surfel_buf[i].prev = prev_id; + prev_id = i; + } + + List sorted_list; + sorted_list.first = list_start; + sorted_list.last = prev_id; + + if (sorted_list.first == sorted_list.last) { + /* Only one item. Nothing to sort. */ + return; + } + + /* Using insertion sort as it is easier to implement. */ + + List unsorted_list = list_split_after(sorted_list, sorted_list.first); + + /* Mutable foreach. */ + for (int i = unsorted_list.first, next; i > -1; i = next) { + next = surfel_buf[i].next; + + bool insert = false; + for (int j = sorted_list.first; j > -1; j = surfel_buf[j].next) { + if (surfel_buf[j].ray_distance < surfel_buf[i].ray_distance) { + list_insert_link_before(sorted_list, j, i); + insert = true; + break; + } + } + if (insert == false) { + list_add_tail(sorted_list, i); + } + } } diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 412e3e10ab2..c3ec8453052 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -19,7 +19,7 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_surfel_light) - .local_group_size(CULLING_SELECT_GROUP_SIZE) + .local_group_size(SURFEL_GROUP_SIZE) .additional_info("eevee_shared", "draw_view", "eevee_utility_texture", @@ -31,15 +31,20 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_light) .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_surfel_list_build) - .local_group_size(CULLING_SELECT_GROUP_SIZE) + .local_group_size(SURFEL_GROUP_SIZE) .additional_info("eevee_shared", "draw_view") + .storage_buf(0, Qualifier::READ_WRITE, "int", "list_start_buf[]") + .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") + .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") + .storage_buf(6, Qualifier::READ_WRITE, "SurfelListInfoData", "list_info_buf") .compute_source("eevee_surfel_list_build_comp.glsl") - .storage_buf(0, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]") .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort) - .local_group_size(CULLING_SELECT_GROUP_SIZE) + .local_group_size(SURFEL_LIST_GROUP_SIZE) .additional_info("eevee_shared", "draw_view") + .storage_buf(0, Qualifier::READ_WRITE, "int", "list_start_buf[]") + .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") + .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") .compute_source("eevee_surfel_list_sort_comp.glsl") - .storage_buf(0, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]") .do_static_compilation(true); diff --git a/source/blender/draw/tests/eevee_test.cc b/source/blender/draw/tests/eevee_test.cc index 40a26ecdcc5..e397d634eab 100644 --- a/source/blender/draw/tests/eevee_test.cc +++ b/source/blender/draw/tests/eevee_test.cc @@ -1144,4 +1144,113 @@ static void test_eevee_shadow_page_mask() } DRAW_TEST(eevee_shadow_page_mask) +static void test_eevee_surfel_list() +{ + StorageArrayBuffer list_start_buf = {"list_start_buf"}; + StorageVectorBuffer surfel_buf = {"surfel_buf"}; + CaptureInfoBuf capture_info_buf = {"capture_info_buf"}; + SurfelListInfoBuf list_info_buf = {"list_info_buf"}; + + /** + * Simulate surfels on a 2x2 projection grid covering [0..2] on the Z axis. + */ + { + Surfel surfel; + /* NOTE: Expected link assumes linear increasing processing order [0->5]. But this is + * multithreaded and we can't know the execution order in advance. */ + /* 0: Project to (1, 0) = list 1. Unsorted Next = -1; Next = -1; Prev = 3. */ + surfel.position = {1.1f, 0.1f, 0.1f}; + surfel_buf.append(surfel); + /* 1: Project to (1, 0) = list 1. Unsorted Next = 0; Next = 2; Prev = -1. */ + surfel.position = {1.1f, 0.2f, 0.5f}; + surfel_buf.append(surfel); + /* 2: Project to (1, 0) = list 1. Unsorted Next = 1; Next = 3; Prev = 1. */ + surfel.position = {1.1f, 0.3f, 0.3f}; + surfel_buf.append(surfel); + /* 3: Project to (1, 0) = list 1. Unsorted Next = 2; Next = 0; Prev = 2. */ + surfel.position = {1.2f, 0.4f, 0.2f}; + surfel_buf.append(surfel); + /* 4: Project to (1, 1) = list 3. Unsorted Next = -1; Next = -1; Prev = -1. */ + surfel.position = {1.0f, 1.0f, 0.5f}; + surfel_buf.append(surfel); + /* 5: Project to (0, 1) = list 2. Unsorted Next = -1; Next = -1; Prev = -1. */ + surfel.position = {0.1f, 1.1f, 0.5f}; + surfel_buf.append(surfel); + + surfel_buf.push_update(); + } + { + capture_info_buf.surfel_len = surfel_buf.size(); + capture_info_buf.push_update(); + } + { + list_info_buf.ray_grid_size = int2(2); + list_info_buf.list_max = list_info_buf.ray_grid_size.x * list_info_buf.ray_grid_size.y; + list_info_buf.push_update(); + } + { + list_start_buf.resize(ceil_to_multiple_u(list_info_buf.list_max, 4u)); + list_start_buf.push_update(); + GPU_storagebuf_clear(list_start_buf, -1); + } + + /* Top-down view. */ + View view = {"RayProjectionView"}; + view.sync(float4x4::identity(), math::projection::orthographic(0, 2, 0, 2, 0, 1)); + + GPUShader *sh_build = GPU_shader_create_from_info_name("eevee_surfel_list_build"); + GPUShader *sh_sort = GPU_shader_create_from_info_name("eevee_surfel_list_sort"); + + PassSimple pass("Build_and_Sort"); + pass.shader_set(sh_build); + pass.bind_ssbo("list_start_buf", list_start_buf); + pass.bind_ssbo("surfel_buf", surfel_buf); + pass.bind_ssbo("capture_info_buf", capture_info_buf); + pass.bind_ssbo("list_info_buf", list_info_buf); + pass.dispatch(int3(1, 1, 1)); + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + + pass.shader_set(sh_sort); + pass.bind_ssbo("list_start_buf", list_start_buf); + pass.bind_ssbo("surfel_buf", surfel_buf); + pass.bind_ssbo("list_info_buf", list_info_buf); + pass.dispatch(int3(1, 1, 1)); + pass.barrier(GPU_BARRIER_BUFFER_UPDATE); + + Manager manager; + manager.submit(pass, view); + + list_start_buf.read(); + surfel_buf.read(); + + /* NOTE: All of these are unstable by definition (atomic + multithread). + * But should be consistent since we only dispatch one thread-group. */ + /* Expect last added surfel index. It is the list start index before sorting. */ + Vector expect_list_start = {-1, 3, 5, 4}; + /* Expect surfel list. */ + Vector expect_link_next = {-1, +2, +3, +0, -1, -1}; + Vector expect_link_prev = {+3, -1, +1, +2, -1, -1}; + + Vector link_next, link_prev; + for (auto &surfel : Span(surfel_buf.data(), surfel_buf.size())) { + link_next.append(surfel.next); + link_prev.append(surfel.prev); + } + +#if 0 /* Useful for debugging */ + // Span(list_start_buf.data(), expect_list_start.size()).print_as_lines("list_start"); + // link_next.as_span().print_as_lines("link_next"); + // link_prev.as_span().print_as_lines("link_prev"); +#endif + + EXPECT_EQ_ARRAY(list_start_buf.data(), expect_list_start.data(), expect_list_start.size()); + EXPECT_EQ_ARRAY(link_next.data(), expect_link_next.data(), expect_link_next.size()); + EXPECT_EQ_ARRAY(link_prev.data(), expect_link_prev.data(), expect_link_prev.size()); + + GPU_shader_free(sh_build); + GPU_shader_free(sh_sort); + DRW_shaders_free(); +} +DRAW_TEST(eevee_surfel_list) + } // namespace blender::draw -- 2.30.2 From da91a2ccb028dc7399a10fbae277a6d1dcac42b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 26 Mar 2023 15:39:17 +0200 Subject: [PATCH 12/58] EEVEE-Next: IrradianceBake: Fix memleak --- .../blender/draw/engines/eevee_next/eevee_irradiance_cache.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 6f5709a2a90..dd7b4223abf 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -296,6 +296,8 @@ void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) capture_info_buf_.read(); surfels_buf_.read(); light_cache_grid.surfels_len = capture_info_buf_.surfel_len; + /* TODO(fclem): This isn't threadsafe. */ + MEM_SAFE_FREE(light_cache_grid.surfels); light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data()); break; default: -- 2.30.2 From 5de76bbbffde17d56a4c9f601e56048aaac5f244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 28 Mar 2023 13:17:25 +0200 Subject: [PATCH 13/58] EEVEE-Next: IrradianceBake: Fix light-leak This fixes the light leaking issue caused by the surfel lists. The fix consists in clumping more surfel together when creating the lists then rewire the coplanar surfels to more valid surfels up and down the same list. --- .../eevee_next/eevee_irradiance_cache.cc | 14 +++++- .../shaders/eevee_surfel_list_sort_comp.glsl | 48 ++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index dd7b4223abf..71222887d2d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -275,7 +275,19 @@ void IrradianceBake::propagate_light_sample() View ray_view = {"RayProjectionView"}; ray_view.sync(viewmat, winmat); - list_info_buf_.ray_grid_size = math::max(int2(1), int2(surfel_density_ * (max - min))); + /* This avoid light leaking by making sure that for one surface there will always be at least 1 + * surfel capture inside a ray list. Since the surface with the maximum distance (after + * projection) between adjacent surfels is a slope that goes through 3 corners of a cube, + * the distance the grid needs to cover is the diagonal of a cube face. + * + * The lower the number the more surfels it clumps together in the same surfel-list. + * Biasing the grid_density like that will create many invalid link between coplanar surfels. + * These are dealt with during the list sorting pass. + * + * We add an extra epsilon just in case. We really need this step to be leak free. */ + const float max_distance_between_neighbor_surfels_inv = M_SQRT1_2 - 1e-4; + const float ray_grid_density = surfel_density_ * max_distance_between_neighbor_surfels_inv; + list_info_buf_.ray_grid_size = math::max(int2(1), int2(ray_grid_density * (max - min))); list_info_buf_.list_max = list_info_buf_.ray_grid_size.x * list_info_buf_.ray_grid_size.y; list_info_buf_.push_update(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl index a8cf64b9743..1fab81fc859 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl @@ -6,8 +6,6 @@ * * Sort by increasing `ray_distance`. Start of list is smallest value. * - * Outputs a flat array of surfel indices. Each ray is a range inside the array. This allows - * parallel processing in the light propagation phase. * Dispatched as 1 thread per list. */ @@ -62,6 +60,18 @@ void list_insert_link_before(inout List list, int next_link, int new_link) } } +/** + * Return true if link from `surfel[a]` to `surfel[b]` is valid. + * WARNING: this function is not commutative : `f(a, b) != f(b, a)` + */ +bool is_valid_surfel_link(int a, int b) +{ + vec3 link_vector = normalize(surfel_buf[b].position - surfel_buf[a].position); + float link_angle_cos = dot(surfel_buf[a].normal, link_vector); + bool is_coplanar = abs(link_angle_cos) < 1.0e-3; + return !is_coplanar; +} + void main() { int list_index = int(gl_GlobalInvocationID); @@ -112,4 +122,38 @@ void main() list_add_tail(sorted_list, i); } } + + /* Now that we have a sorted list, try to avoid connection from coplanar surfels. + * For that we disconnect them and link them to the first non-coplanar surfel. + * Note that this changes the list to a tree, which doesn't affect the rest of the algorithm. + * + * This is a really important step since it allows to clump more surfels into one ray list and + * avoid light leaking through surfaces. If we don't disconnect coplanar surfels, we loose many + * good rays by evaluating null radiance transfer between the coplanar surfels for rays that + * are not directly perpendicular to the surface. */ + + /* Mutable foreach. */ + for (int i = sorted_list.first, next; i > -1; i = next) { + next = surfel_buf[i].next; + + int valid_next = surfel_buf[i].next; + int valid_prev = surfel_buf[i].prev; + + /* Search the list for the first valid next and previous surfel. */ + while (valid_next > -1) { + if (is_valid_surfel_link(i, valid_next)) { + break; + } + valid_next = surfel_buf[valid_next].next; + } + while (valid_prev > -1) { + if (is_valid_surfel_link(i, valid_prev)) { + break; + } + valid_prev = surfel_buf[valid_prev].prev; + } + + surfel_buf[i].next = valid_next; + surfel_buf[i].prev = valid_prev; + } } -- 2.30.2 From 4e3eb2f7a5d1509da3fe2685a9f29d339c2018d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 28 Mar 2023 13:18:22 +0200 Subject: [PATCH 14/58] EEVEE-Next: IrradianceBake: Fix crash caused by motion blur --- source/blender/draw/engines/eevee_next/eevee_instance.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 15d72c5e732..5f395b6353c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -98,7 +98,6 @@ void Instance::init_light_bake(Depsgraph *depsgraph, draw::Manager *manager) velocity.init(); depth_of_field.init(); shadows.init(); - motion_blur.init(); main_view.init(); irradiance_cache.init(); } -- 2.30.2 From db3028a6249d719913523a2c2a8a69108a655def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 28 Mar 2023 13:21:04 +0200 Subject: [PATCH 15/58] EEVEE-Next: IrradianceBake: Implement light propagation --- source/blender/draw/CMakeLists.txt | 1 + .../eevee_next/eevee_irradiance_cache.cc | 9 +- .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 1 + .../engines/eevee_next/eevee_shader_shared.hh | 18 +++- .../shaders/eevee_debug_surfels_frag.glsl | 17 ++-- .../shaders/eevee_surf_capture_frag.glsl | 10 ++- .../shaders/eevee_surfel_light_comp.glsl | 13 ++- .../shaders/eevee_surfel_ray_comp.glsl | 86 +++++++++++++++++++ .../infos/eevee_irradiance_cache_info.hh | 8 ++ 10 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 2ad6c7fa56e..c6fd486eb77 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -502,6 +502,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surfel_light_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl + engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl engines/eevee_next/shaders/eevee_transparency_lib.glsl engines/eevee_next/shaders/eevee_velocity_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 71222887d2d..18071f6f98c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -49,7 +49,7 @@ void IrradianceCache::debug_pass_sync() debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL); debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); - debug_surfels_ps_.push_constant("surfel_radius", 0.5f / 4.0f); + debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f); debug_surfels_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); surfels_buf_.clear(); @@ -130,7 +130,12 @@ void IrradianceBake::sync() sub.dispatch(&dispatch_per_list_); } { - // PassSimple::Sub &sub = pass.sub("LightPropagate"); + PassSimple::Sub &sub = pass.sub("RayEval"); + sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY)); + sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + sub.barrier(GPU_BARRIER_SHADER_STORAGE); + sub.dispatch(&dispatch_per_surfel_); } } { diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index d511d491f4a..a8fb963aea7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -178,6 +178,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_surfel_list_build"; case SURFEL_LIST_SORT: return "eevee_surfel_list_sort"; + case SURFEL_RAY: + return "eevee_surfel_ray"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 8b70b00eb08..7f827b41a24 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -83,6 +83,7 @@ enum eShaderType { SURFEL_LIGHT, SURFEL_LIST_BUILD, SURFEL_LIST_SORT, + SURFEL_RAY, MAX_SHADER_TYPE, }; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 96c6d819979..3606b80439d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -840,12 +840,22 @@ struct Surfel { /** Next surfel index in the ray link-list. */ int next; /** Surface albedo to apply to incoming radiance. */ - packed_float3 albedo; + packed_float3 albedo_front; + int _pad0; + packed_float3 albedo_back; /** Distance along the ray direction for sorting. */ float ray_distance; - /** Accumulated reflected radiance. */ - packed_float3 radiance; - int _pad0; + /** Reflected radiance from previous bounce. */ + packed_float3 radiance_front; + int _pad1; + packed_float3 radiance_back; + int _pad2; + /** + * Accumulated reflected radiance for the current bounce. + * Weight is stored in the fourth component. + */ + float4 radiance_bounce_front; + float4 radiance_bounce_back; }; BLI_STATIC_ASSERT_ALIGN(Surfel, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl index 956bd528ed1..76950b65c1d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl @@ -3,13 +3,19 @@ void main() { Surfel surfel = surfels_buf[surfel_index]; + vec4 radiance_bounce = gl_FrontFacing ? surfel.radiance_bounce_front : + surfel.radiance_bounce_back; + vec3 radiance = gl_FrontFacing ? surfel.radiance_front : surfel.radiance_back; + switch (eDebugMode(debug_mode)) { default: case DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: out_color = vec4(pow(surfel.normal * 0.5 + 0.5, vec3(2.2)), 0.0); break; case DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: - out_color = vec4(surfel.radiance, 0.0); + out_color = (radiance_bounce.w > 0.0) ? + vec4(radiance + radiance_bounce.rgb / radiance_bounce.w, 0.0) : + vec4(radiance, 0.0); break; } @@ -18,13 +24,4 @@ void main() discard; return; } - - /* Display backfacing surfels with a transparent checkerboard grid. */ - if (!gl_FrontFacing) { - ivec2 grid_uv = ivec2(gl_FragCoord) / 5; - if ((grid_uv.x + grid_uv.y) % 2 == 0) { - discard; - return; - } - } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl index eedb3280dbe..00daf9940a2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl @@ -45,8 +45,14 @@ void main() if (capture_info_buf.do_surfel_output) { surfel_buf[surfel_id].position = g_data.P; surfel_buf[surfel_id].normal = gl_FrontFacing ? g_data.Ng : -g_data.Ng; - surfel_buf[surfel_id].albedo = albedo; - surfel_buf[surfel_id].radiance = g_emission; + surfel_buf[surfel_id].albedo_front = albedo; + surfel_buf[surfel_id].radiance_front = g_emission; + /* TODO(fclem): 2nd surface evaluation. */ + surfel_buf[surfel_id].albedo_back = albedo; + surfel_buf[surfel_id].radiance_back = g_emission; + + surfel_buf[surfel_id].radiance_bounce_front = vec4(0.0); + surfel_buf[surfel_id].radiance_bounce_back = vec4(0.0); } } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl index a0fb506f89c..b3b8378bd89 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl @@ -35,7 +35,8 @@ void light_eval_surfel( } LIGHT_FOREACH_END - LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL (light_cull_buf, l_idx) { + LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx) + { light_eval_ex(diffuse, reflection, false, @@ -73,7 +74,13 @@ void main() light_eval_surfel(diffuse_data, surfel.position, surfel.normal, thickness, diffuse_light); - surfel.radiance += diffuse_light * surfel.albedo; + surfel_buf[index].radiance_front += diffuse_light * surfel.albedo_front; - surfel_buf[index] = surfel; + diffuse_data.N = -surfel.normal; + diffuse_light = vec3(0.0); + reflection_light = vec3(0.0); + + light_eval_surfel(diffuse_data, surfel.position, -surfel.normal, thickness, diffuse_light); + + surfel_buf[index].radiance_back += diffuse_light * surfel.albedo_back; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl new file mode 100644 index 00000000000..d3fe796f53c --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -0,0 +1,86 @@ + +/** + * For every surfel, compute the incomming radiance from both side. + * For that, walk the ray surfel linked-list and gather the light from the neighbor surfels. + * + * Dispatched as 1 thread per surfel. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) + +/** + * Evaluate radiance transfer from `surfel_a` to `surfel_b`. + * Assume ray direction is uniformly distributed over the sphere. + * NOTE: In practice the ray is not evenly distributed but that's a by-product + * of the surfel list method. + * + * Return radiance + pdf. + */ +void radiance_transfer(Surfel surfel_a, inout Surfel surfel_b) +{ + bool facing = dot(surfel_a.normal, surfel_b.normal) < 0.0; + + vec3 L = normalize(surfel_a.position - surfel_b.position); + vec3 N = surfel_b.normal; + float NL = dot(N, L); + float pdf = abs(NL); + vec3 irradiance = facing ? surfel_a.radiance_front : surfel_a.radiance_back; + + if (NL > 0.0) { + surfel_b.radiance_bounce_front += vec4(surfel_b.albedo_front * irradiance * pdf, pdf); + } + else { + surfel_b.radiance_bounce_back += vec4(surfel_b.albedo_back * irradiance * pdf, pdf); + } +} + +void sky_radiance(inout Surfel surfel) +{ + vec3 L = cameraVec(surfel.position); + vec3 N = surfel.normal; + float NL = dot(N, L); + /* TODO(fclem): Sky cubemap sampling. */ + vec3 Li = vec3(0.0); + /* Assume white albedo. */ + // float pdf = M_1_PI; + float pdf = abs(NL); + + vec3 radiance = Li; + + if (NL > 0.0) { + surfel.radiance_bounce_front += vec4(radiance * pdf, pdf); + } + else { + surfel.radiance_bounce_back += vec4(radiance * pdf, pdf); + } +} + +void main() +{ + int surfel_index = int(gl_GlobalInvocationID); + if (surfel_index >= capture_info_buf.surfel_len) { + return; + } + + Surfel surfel = surfel_buf[surfel_index]; + + vec4 radiance_with_pdf = vec4(0.0); + if (surfel.next > -1) { + radiance_transfer(surfel_buf[surfel.next], surfel); + } + else { + sky_radiance(surfel); + } + + if (surfel.prev > -1) { + radiance_transfer(surfel_buf[surfel.prev], surfel); + } + else { + sky_radiance(surfel); + } + + surfel_buf[surfel_index].radiance_bounce_front = surfel.radiance_bounce_front; + surfel_buf[surfel_index].radiance_bounce_back = surfel.radiance_bounce_back; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index c3ec8453052..7e51014a490 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -48,3 +48,11 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort) .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") .compute_source("eevee_surfel_list_sort_comp.glsl") .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_surfel_ray) + .local_group_size(SURFEL_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") + .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") + .compute_source("eevee_surfel_ray_comp.glsl") + .do_static_compilation(true); -- 2.30.2 From 73f96fe2161e0f8c6c2ba539546e37ff9096bd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 28 Mar 2023 18:12:28 +0200 Subject: [PATCH 16/58] EEVEE-Next: IrradianceBake: Implement multi bounce support This is still broken as the light seems to never converge. --- scripts/startup/bl_ui/properties_render.py | 1 + source/blender/draw/CMakeLists.txt | 1 + .../draw/engines/eevee_next/eevee_instance.cc | 33 ++++++++++++------- .../eevee_next/eevee_irradiance_cache.cc | 15 +++++++++ .../eevee_next/eevee_irradiance_cache.hh | 4 +++ .../draw/engines/eevee_next/eevee_sampling.cc | 1 + .../draw/engines/eevee_next/eevee_shader.cc | 2 ++ .../draw/engines/eevee_next/eevee_shader.hh | 1 + .../shaders/eevee_debug_surfels_frag.glsl | 6 +--- .../shaders/eevee_surfel_bounce_comp.glsl | 24 ++++++++++++++ .../shaders/eevee_surfel_ray_comp.glsl | 4 +-- .../infos/eevee_irradiance_cache_info.hh | 24 ++++++++------ 12 files changed, 87 insertions(+), 29 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index bda7cc6622e..197702d66aa 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -596,6 +596,7 @@ class RENDER_PT_eevee_next_indirect_lighting(RenderButtonsPanel, Panel): col.label(text=cache_info) col.prop(props, "gi_auto_bake") + col.prop(props, "gi_diffuse_bounces") col.prop(props, "gi_irradiance_samples") diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c6fd486eb77..7730afe0276 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -499,6 +499,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surf_lib.glsl engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl engines/eevee_next/shaders/eevee_surf_world_frag.glsl + engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl engines/eevee_next/shaders/eevee_surfel_light_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 5f395b6353c..789892665ab 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -535,19 +535,20 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, return; } - /* TODO(fclem): Multiple bounce. We need to use the previous bounce result. */ - for (int bounce = 0; bounce < 1; bounce++) { - for (auto i : light_probes.grids.index_range()) { - custom_pipeline_wrapper([&]() { - /* TODO: lightprobe visibility group option. */ - manager->begin_sync(); - render_sync(); - manager->end_sync(); - irradiance_cache.bake.surfels_create(light_probes.grids[i]); - irradiance_cache.bake.surfels_lights_eval(); - }); + for (auto i : light_probes.grids.index_range()) { + custom_pipeline_wrapper([&]() { + /* TODO: lightprobe visibility group option. */ + manager->begin_sync(); + render_sync(); + manager->end_sync(); + irradiance_cache.bake.surfels_create(light_probes.grids[i]); + irradiance_cache.bake.surfels_lights_eval(); + }); - sampling.reset(); + int bounce_len = scene->eevee.gi_diffuse_bounces; + /* Start at bounce 1 as 0 bounce is no indirect lighting. */ + for (int bounce = 1; bounce <= bounce_len; bounce++) { + sampling.init(scene); while (!sampling.finished()) { context_wrapper([&]() { /* Batch ray cast by pack of 16 to avoid too much overhead of @@ -556,11 +557,19 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, sampling.step(); irradiance_cache.bake.propagate_light_sample(); } + if (sampling.finished()) { + irradiance_cache.bake.accumulate_bounce(); + } irradiance_cache.bake.read_result(r_light_cache->grids[i]); }); // do_update = true; } } + + if (bounce_len == 0) { + /* Still read result for debugging surfel direct lighting. */ + context_wrapper([&]() { irradiance_cache.bake.read_result(r_light_cache->grids[i]); }); + } } r_light_cache->flag &= ~LIGHTCACHE_BAKING; diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 18071f6f98c..e888777d81f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -124,6 +124,7 @@ void IrradianceBake::sync() PassSimple::Sub &sub = pass.sub("ListSort"); sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT)); sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); sub.bind_ssbo("list_start_buf", &list_start_buf_); sub.bind_ssbo("list_info_buf", &list_info_buf_); sub.barrier(GPU_BARRIER_SHADER_STORAGE); @@ -143,6 +144,15 @@ void IrradianceBake::sync() pass.init(); /* TODO */ } + { + PassSimple &pass = surfel_light_bounce_ps_; + pass.init(); + pass.shader_set(inst_.shaders.static_shader_get(SURFEL_BOUNCE)); + pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + pass.dispatch(&dispatch_per_surfel_); + } } void IrradianceBake::surfels_create(const IrradianceGrid &grid) @@ -304,6 +314,11 @@ void IrradianceBake::propagate_light_sample() inst_.manager->submit(surfel_light_propagate_ps_, ray_view); } +void IrradianceBake::accumulate_bounce() +{ + inst_.manager->submit(surfel_light_bounce_ps_); +} + void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) { switch (inst_.debug_mode) { diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 2f03e2370e8..1d481caa4e4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -39,6 +39,8 @@ class IrradianceBake { PassSimple surfel_light_eval_ps_ = {"LightEval"}; /** Propagate light from surfel to surfel. */ PassSimple surfel_light_propagate_ps_ = {"LightPropagate"}; + /** Start of a light bounce. Accumulate light from previous propagation. */ + PassSimple surfel_light_bounce_ps_ = {"LightBounce"}; /** Capture surfel lighting to irradiance samples. */ PassSimple irradiance_capture_ps_ = {"IrradianceCapture"}; @@ -85,6 +87,8 @@ class IrradianceBake { void surfels_lights_eval(); /** Propagate light from surfel to surfel in a random direction over the sphere. */ void propagate_light_sample(); + /** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */ + void accumulate_bounce(); /** Read grid final irradiance back to CPU into \a light_cache_grid . */ void read_result(LightCacheIrradianceGrid &light_cache_grid); diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index e4b33bb40dd..19e3d3edce5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -26,6 +26,7 @@ void Sampling::init(const Scene *scene) { if (inst_.is_baking()) { sample_count_ = max_ii(1, scene->eevee.gi_irradiance_samples); + sample_ = 0; } else { sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index a8fb963aea7..95760301951 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -172,6 +172,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_shadow_tag_usage_opaque"; case SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT: return "eevee_shadow_tag_usage_transparent"; + case SURFEL_BOUNCE: + return "eevee_surfel_bounce"; case SURFEL_LIGHT: return "eevee_surfel_light"; case SURFEL_LIST_BUILD: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 7f827b41a24..f34b61b00bb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -80,6 +80,7 @@ enum eShaderType { SHADOW_TILEMAP_TAG_USAGE_OPAQUE, SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT, + SURFEL_BOUNCE, SURFEL_LIGHT, SURFEL_LIST_BUILD, SURFEL_LIST_SORT, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl index 76950b65c1d..48c27ba3ff6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl @@ -3,8 +3,6 @@ void main() { Surfel surfel = surfels_buf[surfel_index]; - vec4 radiance_bounce = gl_FrontFacing ? surfel.radiance_bounce_front : - surfel.radiance_bounce_back; vec3 radiance = gl_FrontFacing ? surfel.radiance_front : surfel.radiance_back; switch (eDebugMode(debug_mode)) { @@ -13,9 +11,7 @@ void main() out_color = vec4(pow(surfel.normal * 0.5 + 0.5, vec3(2.2)), 0.0); break; case DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: - out_color = (radiance_bounce.w > 0.0) ? - vec4(radiance + radiance_bounce.rgb / radiance_bounce.w, 0.0) : - vec4(radiance, 0.0); + out_color = vec4(radiance, 0.0); break; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl new file mode 100644 index 00000000000..9059d7c387f --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl @@ -0,0 +1,24 @@ + +/** + * Accumulate light from a bounce of indirect light into each surfel radiance. + * This feeds back the light for the next bounce. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +void main() +{ + int surfel_index = int(gl_GlobalInvocationID); + if (surfel_index >= capture_info_buf.surfel_len) { + return; + } + + vec4 radiance_bounce_front = surfel_buf[surfel_index].radiance_bounce_front; + vec4 radiance_bounce_back = surfel_buf[surfel_index].radiance_bounce_back; + surfel_buf[surfel_index].radiance_front += radiance_bounce_front.rgb * + safe_rcp(radiance_bounce_front.w); + surfel_buf[surfel_index].radiance_back += radiance_bounce_back.rgb * + safe_rcp(radiance_bounce_back.w); + surfel_buf[surfel_index].radiance_bounce_front = vec4(0.0); + surfel_buf[surfel_index].radiance_bounce_back = vec4(0.0); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index d3fe796f53c..b748faf8af8 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -20,13 +20,13 @@ */ void radiance_transfer(Surfel surfel_a, inout Surfel surfel_b) { - bool facing = dot(surfel_a.normal, surfel_b.normal) < 0.0; - vec3 L = normalize(surfel_a.position - surfel_b.position); vec3 N = surfel_b.normal; float NL = dot(N, L); float pdf = abs(NL); + bool facing = dot(-L, surfel_a.normal) > 0.0; vec3 irradiance = facing ? surfel_a.radiance_front : surfel_a.radiance_back; + irradiance *= M_1_PI; if (NL > 0.0) { surfel_b.radiance_bounce_front += vec4(surfel_b.albedo_front * irradiance * pdf, pdf); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 7e51014a490..2f3e94d1345 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -18,41 +18,45 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .push_constant(Type::INT, "debug_mode") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_surfel_common) + .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") + .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf"); + GPU_SHADER_CREATE_INFO(eevee_surfel_light) .local_group_size(SURFEL_GROUP_SIZE) .additional_info("eevee_shared", "draw_view", "eevee_utility_texture", + "eevee_surfel_common", "eevee_light_data", "eevee_shadow_data") .compute_source("eevee_surfel_light_comp.glsl") - .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") - .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_surfel_bounce) + .local_group_size(SURFEL_GROUP_SIZE) + .additional_info("eevee_shared", "eevee_surfel_common") + .compute_source("eevee_surfel_bounce_comp.glsl") .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_surfel_list_build) .local_group_size(SURFEL_GROUP_SIZE) - .additional_info("eevee_shared", "draw_view") + .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") .storage_buf(0, Qualifier::READ_WRITE, "int", "list_start_buf[]") - .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") - .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") .storage_buf(6, Qualifier::READ_WRITE, "SurfelListInfoData", "list_info_buf") .compute_source("eevee_surfel_list_build_comp.glsl") .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort) .local_group_size(SURFEL_LIST_GROUP_SIZE) - .additional_info("eevee_shared", "draw_view") + .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") .storage_buf(0, Qualifier::READ_WRITE, "int", "list_start_buf[]") - .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") .compute_source("eevee_surfel_list_sort_comp.glsl") .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_surfel_ray) .local_group_size(SURFEL_GROUP_SIZE) - .additional_info("eevee_shared", "draw_view") - .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") - .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf") + .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") .compute_source("eevee_surfel_ray_comp.glsl") .do_static_compilation(true); -- 2.30.2 From 22ccf837001221201b2ea00a6dc0a1d2f34a4157 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 28 Mar 2023 17:51:27 +0200 Subject: [PATCH 17/58] Cleanup: Fix typo --- .../eevee_next/shaders/infos/eevee_irradiance_cache_info.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 2f3e94d1345..c49ab6ed555 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -3,14 +3,14 @@ #include "eevee_defines.hh" #include "gpu_shader_create_info.hh" -GPU_SHADER_INTERFACE_INFO(eeve_debug_surfel_iface, "") +GPU_SHADER_INTERFACE_INFO(eevee_debug_surfel_iface, "") .smooth(Type::VEC3, "P") .flat(Type::INT, "surfel_index"); GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .additional_info("eevee_shared", "draw_view") .vertex_source("eevee_debug_surfels_vert.glsl") - .vertex_out(eeve_debug_surfel_iface) + .vertex_out(eevee_debug_surfel_iface) .fragment_source("eevee_debug_surfels_frag.glsl") .fragment_out(0, Type::VEC4, "out_color") .storage_buf(0, Qualifier::READ, "Surfel", "surfels_buf[]") -- 2.30.2 From a4c571dc8e0f32ec3f5ddc1d576155256abce1a0 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 28 Mar 2023 19:59:35 +0200 Subject: [PATCH 18/58] Fix irradiance_grid.world_to_grid --- source/blender/draw/engines/eevee_next/eevee_instance.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 789892665ab..cc89fde5655 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -358,7 +358,8 @@ LightCache *Instance::light_cache_create(Vector grids, return nullptr; } LightCacheIrradianceGrid &irradiance_grid = light_cache->grids[i]; - copy_m4_m4(irradiance_grid.world_to_grid, grid.transform.ptr()); + float4x4 world_to_grid = math::invert(grid.transform); + copy_m4_m4(irradiance_grid.world_to_grid, world_to_grid.ptr()); irradiance_grid.resolution[0] = grid.resolution.x; irradiance_grid.resolution[1] = grid.resolution.y; irradiance_grid.resolution[2] = grid.resolution.z; -- 2.30.2 From 114713c3544ec727ba5f41f69d89ad0a14806e66 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 28 Mar 2023 20:00:19 +0200 Subject: [PATCH 19/58] Workaround compilation error on Win Nvidia drivers --- .../engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index b748faf8af8..c27d48f24c5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -68,14 +68,16 @@ void main() vec4 radiance_with_pdf = vec4(0.0); if (surfel.next > -1) { - radiance_transfer(surfel_buf[surfel.next], surfel); + Surfel next = surfel_buf[surfel.next]; + radiance_transfer(next, surfel); } else { sky_radiance(surfel); } if (surfel.prev > -1) { - radiance_transfer(surfel_buf[surfel.prev], surfel); + Surfel prev = surfel_buf[surfel.prev]; + radiance_transfer(prev, surfel); } else { sky_radiance(surfel); -- 2.30.2 From a484f43a177dd30d1a1bd360cd7634acab95352a Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 28 Mar 2023 20:05:15 +0200 Subject: [PATCH 20/58] Irradiance Cache: Display Grid --- scripts/startup/bl_ui/properties_render.py | 1 + source/blender/draw/CMakeLists.txt | 2 + .../eevee_next/eevee_irradiance_cache.cc | 47 ++++++++++++++++++- .../eevee_next/eevee_irradiance_cache.hh | 9 +++- .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 2 + .../draw/engines/eevee_next/eevee_view.cc | 2 +- .../eevee_display_probe_grid_frag.glsl | 16 +++++++ .../eevee_display_probe_grid_vert.glsl | 34 ++++++++++++++ .../infos/eevee_irradiance_cache_info.hh | 15 ++++++ 10 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 197702d66aa..cc7507b163b 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -598,6 +598,7 @@ class RENDER_PT_eevee_next_indirect_lighting(RenderButtonsPanel, Panel): col.prop(props, "gi_auto_bake") col.prop(props, "gi_diffuse_bounces") col.prop(props, "gi_irradiance_samples") + col.prop(props, "gi_show_irradiance") class RENDER_PT_eevee_indirect_lighting_display(RenderButtonsPanel, Panel): diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 7730afe0276..6df7e0646f4 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -446,6 +446,8 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl + engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl + engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl engines/eevee_next/shaders/eevee_film_comp.glsl engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl engines/eevee_next/shaders/eevee_film_frag.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index e888777d81f..71df0790400 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -25,12 +25,21 @@ void IrradianceCache::sync() { if (!inst_.is_baking()) { debug_pass_sync(); + display_pass_sync(); } else { bake.sync(); } } +void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb) +{ + if (!inst_.is_baking()) { + debug_pass_draw(view, view_fb); + display_pass_draw(view, view_fb); + } +} + void IrradianceCache::debug_pass_sync() { if (!ELEM(inst_.debug_mode, @@ -67,7 +76,7 @@ void IrradianceCache::debug_pass_sync() debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, surfels_buf_.size(), 4); } -void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb) +void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) { switch (inst_.debug_mode) { case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: @@ -85,6 +94,42 @@ void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb) inst_.manager->submit(debug_surfels_ps_, view); } +void IrradianceCache::display_pass_sync() +{ + LightCache *light_cache = inst_.scene->eevee.light_cache_data; + + display_grids_enabled_ = light_cache && light_cache->grid_len > 0 && light_cache->grids && + DRW_state_draw_support() && + inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE; + if (!display_grids_enabled_) { + return; + } + + display_grids_ps_.init(); + display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK); + display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID)); + + for (auto i : IndexRange(light_cache->grid_len)) { + LightCacheIrradianceGrid &grid = light_cache->grids[i]; + display_grids_ps_.push_constant("sphere_radius", 0.1f); + display_grids_ps_.push_constant("grid_resolution", int3(grid.resolution)); + float4x4 grid_to_world = math::invert(float4x4(grid.world_to_grid)); + display_grids_ps_.push_constant("grid_to_world", grid_to_world); + + int cell_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; + display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, cell_count * 3 * 2); + } +} + +void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb) +{ + if (display_grids_enabled_) { + GPU_framebuffer_bind(view_fb); + inst_.manager->submit(display_grids_ps_, view); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 1d481caa4e4..429c3061ca1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -109,15 +109,22 @@ class IrradianceCache { /** Debug surfel elements copied from the light cache. */ draw::StorageVectorBuffer surfels_buf_; + bool display_grids_enabled_ = false; + PassSimple display_grids_ps_ = {"IrradianceCache.Display Grids"}; + public: IrradianceCache(Instance &inst) : bake(inst), inst_(inst){}; ~IrradianceCache(){}; void init(); void sync(); + void viewport_draw(View &view, GPUFrameBuffer *view_fb); + private: void debug_pass_sync(); - void debug_draw(View &view, GPUFrameBuffer *view_fb); + void debug_pass_draw(View &view, GPUFrameBuffer *view_fb); + void display_pass_sync(); + void display_pass_draw(View &view, GPUFrameBuffer *view_fb); }; } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 95760301951..8185c9b1147 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -102,6 +102,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_motion_blur_tiles_flatten_viewport"; case DEBUG_SURFELS: return "eevee_debug_surfels"; + case DISPLAY_PROBE_GRID: + return "eevee_display_probe_grid"; case DOF_BOKEH_LUT: return "eevee_depth_of_field_bokeh_lut"; case DOF_DOWNSAMPLE: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index f34b61b00bb..04599976679 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -34,6 +34,8 @@ enum eShaderType { DEBUG_SURFELS, + DISPLAY_PROBE_GRID, + DOF_BOKEH_LUT, DOF_DOWNSAMPLE, DOF_FILTER, diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 6b2a5beac95..216223408a5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -134,7 +134,7 @@ void ShadingView::render() inst_.lights.debug_draw(render_view_new_, combined_fb_); inst_.hiz_buffer.debug_draw(render_view_new_, combined_fb_); inst_.shadows.debug_draw(render_view_new_, combined_fb_); - inst_.irradiance_cache.debug_draw(render_view_new_, combined_fb_); + inst_.irradiance_cache.viewport_draw(render_view_new_, combined_fb_); GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl new file mode 100644 index 00000000000..e3b43d0e744 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + float dist_sqr = dot(lP, lP); + + /* Discard outside the circle. */ + if (dist_sqr > 1.0) { + discard; + return; + } + + vec3 vN = vec3(lP, sqrt(max(0.0, 1.0 - dist_sqr))); + vec3 P = mat3(ViewMatrixInverse) * vN; + out_color = vec4(normal_view_to_world(vN) * 0.5 + 0.5, 1.0); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl new file mode 100644 index 00000000000..e2174b876fc --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl @@ -0,0 +1,34 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + /* Constant array moved inside function scope. + * Minimises local register allocation in MSL. */ + const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0), + vec2(1.0, -1.0), + vec2(-1.0, 1.0), + + vec2(1.0, -1.0), + vec2(1.0, 1.0), + vec2(-1.0, 1.0)); + + lP = pos[gl_VertexID % 6]; + cell_index = gl_VertexID / 6; + + /* Keep in sync with update_irradiance_probe. */ + ivec3 cell = ivec3(cell_index / (grid_resolution.z * grid_resolution.y), + (cell_index / grid_resolution.z) % grid_resolution.y, + cell_index % grid_resolution.z); + + vec3 ls_cell_pos = (vec3(cell) + vec3(0.5)) / vec3(grid_resolution); + ls_cell_pos = ls_cell_pos * 2.0 - 1.0; /* Remap to (-1 ... +1). */ + + vec3 ws_cell_pos = (grid_to_world * vec4(ls_cell_pos, 1.0)).xyz; + + vec3 vs_offset = vec3(lP, 0.0) * sphere_radius; + vec3 vP = (ViewMatrix * vec4(ws_cell_pos, 1.0)).xyz + vs_offset; + + gl_Position = ProjectionMatrix * vec4(vP, 1.0); + gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting. */ +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index c49ab6ed555..e1475cf3cd4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -60,3 +60,18 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray) .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") .compute_source("eevee_surfel_ray_comp.glsl") .do_static_compilation(true); + +GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "") + .smooth(Type::VEC2, "lP") + .flat(Type::INT, "cell_index"); + +GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) + .additional_info("eevee_shared", "draw_view") + .vertex_source("eevee_display_probe_grid_vert.glsl") + .vertex_out(eevee_display_probe_grid_iface) + .fragment_source("eevee_display_probe_grid_frag.glsl") + .fragment_out(0, Type::VEC4, "out_color") + .push_constant(Type::FLOAT, "sphere_radius") + .push_constant(Type::IVEC3, "grid_resolution") + .push_constant(Type::MAT4, "grid_to_world") + .do_static_compilation(true); -- 2.30.2 From 5dc6535591153306618640c2466e3b0087f595a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 29 Mar 2023 14:52:44 +0200 Subject: [PATCH 21/58] EEVEE-Next: IrradianceBake: Fix integration power and inconsistencies Now it follows proper conventional notation. # Conflicts: # source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl --- .../shaders/eevee_surfel_bounce_comp.glsl | 19 +++-- .../shaders/eevee_surfel_ray_comp.glsl | 69 +++++++------------ 2 files changed, 40 insertions(+), 48 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl index 9059d7c387f..baa76b2214e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl @@ -2,10 +2,23 @@ /** * Accumulate light from a bounce of indirect light into each surfel radiance. * This feeds back the light for the next bounce. + * + * Dispatched as one thread per surfel. */ +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) +vec3 finalize_integration(vec4 radiance) +{ + /* Divide by sample count. */ + radiance.rgb *= safe_rcp(radiance.w); + /* TODO: Find why this is needed. */ + radiance.rgb *= 2.0; + /* Multiply by hemisphere area. */ + return radiance.rgb * M_TAU; +} + void main() { int surfel_index = int(gl_GlobalInvocationID); @@ -15,10 +28,8 @@ void main() vec4 radiance_bounce_front = surfel_buf[surfel_index].radiance_bounce_front; vec4 radiance_bounce_back = surfel_buf[surfel_index].radiance_bounce_back; - surfel_buf[surfel_index].radiance_front += radiance_bounce_front.rgb * - safe_rcp(radiance_bounce_front.w); - surfel_buf[surfel_index].radiance_back += radiance_bounce_back.rgb * - safe_rcp(radiance_bounce_back.w); + surfel_buf[surfel_index].radiance_front += finalize_integration(radiance_bounce_front); + surfel_buf[surfel_index].radiance_back += finalize_integration(radiance_bounce_back); surfel_buf[surfel_index].radiance_bounce_front = vec4(0.0); surfel_buf[surfel_index].radiance_bounce_back = vec4(0.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index c27d48f24c5..64ed7cf605d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -2,59 +2,39 @@ /** * For every surfel, compute the incomming radiance from both side. * For that, walk the ray surfel linked-list and gather the light from the neighbor surfels. + * This shader is dispatched for a random ray in a uniform hemisphere as we evaluate the + * radiance in both directions. * * Dispatched as 1 thread per surfel. */ -#pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) -/** - * Evaluate radiance transfer from `surfel_a` to `surfel_b`. - * Assume ray direction is uniformly distributed over the sphere. - * NOTE: In practice the ray is not evenly distributed but that's a by-product - * of the surfel list method. - * - * Return radiance + pdf. - */ -void radiance_transfer(Surfel surfel_a, inout Surfel surfel_b) +void radiance_transfer(inout Surfel surfel, vec3 irradiance, vec3 L) { - vec3 L = normalize(surfel_a.position - surfel_b.position); - vec3 N = surfel_b.normal; - float NL = dot(N, L); - float pdf = abs(NL); - bool facing = dot(-L, surfel_a.normal) > 0.0; - vec3 irradiance = facing ? surfel_a.radiance_front : surfel_a.radiance_back; - irradiance *= M_1_PI; - + float NL = dot(surfel.normal, L); + /* Lambertian BSDF. */ + vec3 bsdf = irradiance * M_1_PI; + /* Outgoing light. */ + vec3 radiance = bsdf * abs(NL); if (NL > 0.0) { - surfel_b.radiance_bounce_front += vec4(surfel_b.albedo_front * irradiance * pdf, pdf); + surfel.radiance_bounce_front += vec4(radiance, 1.0); } else { - surfel_b.radiance_bounce_back += vec4(surfel_b.albedo_back * irradiance * pdf, pdf); + surfel.radiance_bounce_back += vec4(radiance, 1.0); } } -void sky_radiance(inout Surfel surfel) +void radiance_transfer(inout Surfel surfel, Surfel surfel_emitter) { - vec3 L = cameraVec(surfel.position); - vec3 N = surfel.normal; - float NL = dot(N, L); - /* TODO(fclem): Sky cubemap sampling. */ - vec3 Li = vec3(0.0); - /* Assume white albedo. */ - // float pdf = M_1_PI; - float pdf = abs(NL); + vec3 L = safe_normalize(surfel_emitter.position - surfel.position); + bool facing = dot(-L, surfel_emitter.normal) > 0.0; + vec3 irradiance = facing ? surfel_emitter.radiance_front : surfel_emitter.radiance_back; - vec3 radiance = Li; - - if (NL > 0.0) { - surfel.radiance_bounce_front += vec4(radiance * pdf, pdf); - } - else { - surfel.radiance_bounce_back += vec4(radiance * pdf, pdf); - } + radiance_transfer(surfel, irradiance, L); } void main() @@ -66,21 +46,22 @@ void main() Surfel surfel = surfel_buf[surfel_index]; - vec4 radiance_with_pdf = vec4(0.0); + vec3 sky_L = cameraVec(surfel.position); + if (surfel.next > -1) { - Surfel next = surfel_buf[surfel.next]; - radiance_transfer(next, surfel); + radiance_transfer(surfel, surfel_buf[surfel.next]); } else { - sky_radiance(surfel); + /* TODO(fclem): Sky radiance. */ + radiance_transfer(surfel, vec3(0.0), sky_L); } if (surfel.prev > -1) { - Surfel prev = surfel_buf[surfel.prev]; - radiance_transfer(prev, surfel); + radiance_transfer(surfel, surfel_buf[surfel.prev]); } else { - sky_radiance(surfel); + /* TODO(fclem): Sky radiance. */ + radiance_transfer(surfel, vec3(0.0), -sky_L); } surfel_buf[surfel_index].radiance_bounce_front = surfel.radiance_bounce_front; -- 2.30.2 From bfeb1a1a8d9f4398e7c25bb614948061c8cc9672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 29 Mar 2023 21:01:09 +0200 Subject: [PATCH 22/58] EEVEE-Next: IrradianceBake: Fix light bounce Light bouncing needs a feedback mechanism. We cannot reuse the same radiance for scattering light and accumulating it. This splits the final accumulation to `radiance` and bouncing light into `incomming_light` and `outgoing_light`. --- .../engines/eevee_next/eevee_shader_shared.hh | 20 ++++++++++-------- .../shaders/eevee_surf_capture_frag.glsl | 7 ++++--- .../shaders/eevee_surfel_bounce_comp.glsl | 21 ++++++++++++------- .../shaders/eevee_surfel_ray_comp.glsl | 17 ++++++++------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 3606b80439d..a643710da9f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -841,21 +841,23 @@ struct Surfel { int next; /** Surface albedo to apply to incoming radiance. */ packed_float3 albedo_front; - int _pad0; + int _pad3; packed_float3 albedo_back; /** Distance along the ray direction for sorting. */ float ray_distance; - /** Reflected radiance from previous bounce. */ + /** Surface radiance. */ packed_float3 radiance_front; - int _pad1; + int _pad4; packed_float3 radiance_back; + int _pad0; + /** Radiance from previous bounce. This is what is being scattered during a bounce. */ + packed_float3 outgoing_light_front; + int _pad1; + packed_float3 outgoing_light_back; int _pad2; - /** - * Accumulated reflected radiance for the current bounce. - * Weight is stored in the fourth component. - */ - float4 radiance_bounce_front; - float4 radiance_bounce_back; + /** Accumulated radiance for the current bounce. Weight is stored in the fourth component. */ + float4 incomming_light_front; + float4 incomming_light_back; }; BLI_STATIC_ASSERT_ALIGN(Surfel, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl index 00daf9940a2..e8f2ac58d50 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl @@ -47,12 +47,13 @@ void main() surfel_buf[surfel_id].normal = gl_FrontFacing ? g_data.Ng : -g_data.Ng; surfel_buf[surfel_id].albedo_front = albedo; surfel_buf[surfel_id].radiance_front = g_emission; + surfel_buf[surfel_id].outgoing_light_front = g_emission; + surfel_buf[surfel_id].incomming_light_front = vec4(0.0); /* TODO(fclem): 2nd surface evaluation. */ surfel_buf[surfel_id].albedo_back = albedo; surfel_buf[surfel_id].radiance_back = g_emission; - - surfel_buf[surfel_id].radiance_bounce_front = vec4(0.0); - surfel_buf[surfel_id].radiance_bounce_back = vec4(0.0); + surfel_buf[surfel_id].outgoing_light_back = g_emission; + surfel_buf[surfel_id].incomming_light_back = vec4(0.0); } } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl index baa76b2214e..77bcb65fb7f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl @@ -15,7 +15,7 @@ vec3 finalize_integration(vec4 radiance) radiance.rgb *= safe_rcp(radiance.w); /* TODO: Find why this is needed. */ radiance.rgb *= 2.0; - /* Multiply by hemisphere area. */ + /* Multiply by hemisphere area since we are integrating over it. */ return radiance.rgb * M_TAU; } @@ -25,11 +25,16 @@ void main() if (surfel_index >= capture_info_buf.surfel_len) { return; } - - vec4 radiance_bounce_front = surfel_buf[surfel_index].radiance_bounce_front; - vec4 radiance_bounce_back = surfel_buf[surfel_index].radiance_bounce_back; - surfel_buf[surfel_index].radiance_front += finalize_integration(radiance_bounce_front); - surfel_buf[surfel_index].radiance_back += finalize_integration(radiance_bounce_back); - surfel_buf[surfel_index].radiance_bounce_front = vec4(0.0); - surfel_buf[surfel_index].radiance_bounce_back = vec4(0.0); + Surfel surfel = surfel_buf[surfel_index]; + vec3 radiance_front = finalize_integration(surfel.incomming_light_front); + vec3 radiance_back = finalize_integration(surfel.incomming_light_back); + /* Re-inject the bounced light for the next bounce event. */ + surfel_buf[surfel_index].outgoing_light_front = radiance_front; + surfel_buf[surfel_index].outgoing_light_back = radiance_back; + /* Add to final radiance. */ + surfel_buf[surfel_index].radiance_front += radiance_front; + surfel_buf[surfel_index].radiance_back += radiance_back; + /* Reset accumulator for next bounce. */ + surfel_buf[surfel_index].incomming_light_front = vec4(0.0); + surfel_buf[surfel_index].incomming_light_back = vec4(0.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index 64ed7cf605d..712fa9252f9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -16,15 +16,15 @@ void radiance_transfer(inout Surfel surfel, vec3 irradiance, vec3 L) { float NL = dot(surfel.normal, L); - /* Lambertian BSDF. */ - vec3 bsdf = irradiance * M_1_PI; + /* Lambertian BSDF. Albedo applied later depending on which side of the surfel was hit. */ + float bsdf = M_1_PI; /* Outgoing light. */ - vec3 radiance = bsdf * abs(NL); + vec3 radiance = bsdf * irradiance * abs(NL); if (NL > 0.0) { - surfel.radiance_bounce_front += vec4(radiance, 1.0); + surfel.incomming_light_front += vec4(radiance * surfel.albedo_front, 1.0); } else { - surfel.radiance_bounce_back += vec4(radiance, 1.0); + surfel.incomming_light_back += vec4(radiance * surfel.albedo_back, 1.0); } } @@ -32,7 +32,8 @@ void radiance_transfer(inout Surfel surfel, Surfel surfel_emitter) { vec3 L = safe_normalize(surfel_emitter.position - surfel.position); bool facing = dot(-L, surfel_emitter.normal) > 0.0; - vec3 irradiance = facing ? surfel_emitter.radiance_front : surfel_emitter.radiance_back; + vec3 irradiance = facing ? surfel_emitter.outgoing_light_front : + surfel_emitter.outgoing_light_back; radiance_transfer(surfel, irradiance, L); } @@ -64,6 +65,6 @@ void main() radiance_transfer(surfel, vec3(0.0), -sky_L); } - surfel_buf[surfel_index].radiance_bounce_front = surfel.radiance_bounce_front; - surfel_buf[surfel_index].radiance_bounce_back = surfel.radiance_bounce_back; + surfel_buf[surfel_index].incomming_light_front = surfel.incomming_light_front; + surfel_buf[surfel_index].incomming_light_back = surfel.incomming_light_back; } -- 2.30.2 From 70970299ef2c28356c76b999dbeb1af30ff8f1ae Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Wed, 29 Mar 2023 18:48:24 +0200 Subject: [PATCH 23/58] Shadow tag usage for surfels --- source/blender/draw/CMakeLists.txt | 1 + .../eevee_next/eevee_irradiance_cache.cc | 2 + .../eevee_next/eevee_irradiance_cache.hh | 9 +++-- .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 1 + .../draw/engines/eevee_next/eevee_shadow.cc | 25 ++++++++++++- .../shaders/eevee_shadow_tag_usage_lib.glsl | 37 ++++++++++++++++++- .../eevee_shadow_tag_usage_surfels_comp.glsl | 16 ++++++++ .../shaders/infos/eevee_shadow_info.hh | 11 ++++++ 9 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6df7e0646f4..e8aa8c40c72 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -487,6 +487,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_shadow_tag_usage_comp.glsl engines/eevee_next/shaders/eevee_shadow_tag_usage_frag.glsl engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl + engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl engines/eevee_next/shaders/eevee_shadow_tag_usage_vert.glsl engines/eevee_next/shaders/eevee_shadow_test.glsl engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 71df0790400..2e31af11a18 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -150,6 +150,8 @@ void IrradianceBake::sync() inst_.shadows.bind_resources(&pass); /* Sync with the surfel creation stage. */ pass.barrier(GPU_BARRIER_SHADER_STORAGE); + pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); + pass.barrier(GPU_BARRIER_TEXTURE_FETCH); pass.dispatch(&dispatch_per_surfel_); } { diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 429c3061ca1..901e4e57479 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -29,8 +29,6 @@ class IrradianceBake { /** Light cache being baked. */ LightCache *light_cache_ = nullptr; - /** Surface elements that represent the scene. */ - SurfelBuf surfels_buf_; /** Capture state. */ CaptureInfoBuf capture_info_buf_; /** Framebuffer. */ @@ -67,8 +65,6 @@ class IrradianceBake { /* Dispatch size for per surfel list workload. */ int3 dispatch_per_list_ = int3(1); - /* Surfel per unit distance. */ - float surfel_density_ = 2.0f; /* Orientation of the irradiance grid being baked. */ math::Quaternion grid_orientation_; /* Object center of the irradiance grid being baked. */ @@ -77,6 +73,11 @@ class IrradianceBake { Vector grid_bbox_vertices; public: + /** Surface elements that represent the scene. */ + SurfelBuf surfels_buf_; + /* Surfel per unit distance. */ + float surfel_density_ = 2.0f; + IrradianceBake(Instance &inst) : inst_(inst){}; void sync(); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 8185c9b1147..34f2ce49e72 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -172,6 +172,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_shadow_tag_update"; case SHADOW_TILEMAP_TAG_USAGE_OPAQUE: return "eevee_shadow_tag_usage_opaque"; + case SHADOW_TILEMAP_TAG_USAGE_SURFELS: + return "eevee_shadow_tag_usage_surfels"; case SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT: return "eevee_shadow_tag_usage_transparent"; case SURFEL_BOUNCE: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 04599976679..a90445e4d09 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -80,6 +80,7 @@ enum eShaderType { SHADOW_TILEMAP_INIT, SHADOW_TILEMAP_TAG_UPDATE, SHADOW_TILEMAP_TAG_USAGE_OPAQUE, + SHADOW_TILEMAP_TAG_USAGE_SURFELS, SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT, SURFEL_BOUNCE, diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index 81e84d12961..90f8e843499 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -706,6 +706,29 @@ void ShadowModule::begin_sync() PassMain &pass = tilemap_usage_ps_; pass.init(); + if (inst_.is_baking()) { + SurfelBuf &surfels_buf = inst_.irradiance_cache.bake.surfels_buf_; + float surfel_coverage_area = inst_.irradiance_cache.bake.surfel_density_; + + /* Directional shadows. */ + float texel_size = ShadowDirectional::tile_size_get(0) / float(SHADOW_PAGE_RES); + int directional_level = std::max(0, int(std::ceil(log2(surfel_coverage_area / texel_size)))); + /* Punctual shadows. */ + float projection_ratio = tilemap_pixel_radius() / (surfel_coverage_area / 2.0); + + PassMain::Sub &sub = pass.sub("Surfels"); + sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_USAGE_SURFELS)); + sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data); + sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data); + sub.bind_ssbo("surfels_buf", &surfels_buf); + sub.push_constant("directional_level", directional_level); + sub.push_constant("tilemap_projection_ratio", projection_ratio); + inst_.lights.bind_resources(&sub); + sub.dispatch(int3(surfels_buf.size(), 1, 1)); + + return; /* Skip opaque and transparent tagging for light baking. */ + } + { /** Use depth buffer to tag needed shadow pages for opaque geometry. */ PassMain::Sub &sub = pass.sub("Opaque"); @@ -766,7 +789,7 @@ void ShadowModule::sync_object(const ObjectHandle &handle, curr_casters_.append(resource_handle.raw); } - if (is_alpha_blend) { + if (is_alpha_blend && !inst_.is_baking()) { tilemap_usage_transparent_ps_->draw(box_batch_, resource_handle); } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index bb18f56ec74..97f0ab1b6c8 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -24,6 +24,22 @@ void shadow_tag_usage_tile(LightData light, ivec2 tile_co, int lod, int tilemap_ atomicOr(tiles_buf[tile_index], SHADOW_IS_USED); } +void shadow_tag_usage_tilemap_directional_at_level(uint l_idx, vec3 P, int level) +{ + LightData light = light_buf[l_idx]; + + if (light.tilemap_index == LIGHT_NO_SHADOW) { + return; + } + + vec3 lP = shadow_world_to_local(light, P); + + level = clamp(level, light.clipmap_lod_min, light.clipmap_lod_max); + + ShadowCoordinates coord = shadow_directional_coordinates_at_level(light, lP, level); + shadow_tag_usage_tile(light, coord.tile_coord, 0, coord.tilemap_index); +} + void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radius) { LightData light = light_buf[l_idx]; @@ -59,7 +75,7 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi } } -void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, vec3 V, float dist_to_cam, float radius) +void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, float dist_to_cam, float radius) { LightData light = light_buf[l_idx]; @@ -159,7 +175,7 @@ void shadow_tag_usage(vec3 vP, vec3 P, vec3 V, float radius, float dist_to_cam, LIGHT_FOREACH_END LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) { - shadow_tag_usage_tilemap_punctual(l_idx, P, V, dist_to_cam, radius); + shadow_tag_usage_tilemap_punctual(l_idx, P, dist_to_cam, radius); } LIGHT_FOREACH_END } @@ -170,3 +186,20 @@ void shadow_tag_usage(vec3 vP, vec3 P, vec2 pixel) shadow_tag_usage(vP, P, vec3(0), 0, dist_to_cam, pixel); } + +void shadow_tag_usage_surfel(Surfel surfel, int directional_level) +{ + vec3 P = surfel.position; + + LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { + shadow_tag_usage_tilemap_directional_at_level(l_idx, P, directional_level); + } + LIGHT_FOREACH_END + + LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx) + { + float dist_to_cam = 1; /* Set it to 1 to avoid changing footprint_ratio. */ + shadow_tag_usage_tilemap_punctual(l_idx, P, dist_to_cam, 0); + } + LIGHT_FOREACH_END +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl new file mode 100644 index 00000000000..efa387467aa --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl @@ -0,0 +1,16 @@ + +/** + * Virtual shadowmapping: Usage tagging + * + * Shadow pages are only allocated if they are visible. + * This pass iterates the surfels buffer and tag all tiles that are needed for light shadowing as + * needed. + */ + +#pragma BLENDER_REQUIRE(eevee_shadow_tag_usage_lib.glsl) + +void main() +{ + Surfel surfel = surfels_buf[gl_GlobalInvocationID.x]; + shadow_tag_usage_surfel(surfel, directional_level); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh index 347928e5061..aeee7ff9520 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh @@ -60,6 +60,17 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque) .additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data") .compute_source("eevee_shadow_tag_usage_comp.glsl"); +GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_surfels) + .do_static_compilation(true) + .local_group_size(1, 1, 1) + .storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]") + .storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]") + .storage_buf(7, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]") + .push_constant(Type::INT, "directional_level") + .push_constant(Type::FLOAT, "tilemap_projection_ratio") + .additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data") + .compute_source("eevee_shadow_tag_usage_surfels_comp.glsl"); + GPU_SHADER_INTERFACE_INFO(eevee_shadow_tag_transparent_iface, "interp") .smooth(Type::VEC3, "P") .smooth(Type::VEC3, "vP") -- 2.30.2 From 34adebcf43e489c8707bfc79ddf8b0091c946e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 1 Apr 2023 11:54:02 +0200 Subject: [PATCH 24/58] GPU: FrameBuffer: Fix empty framebuffer update The framebuffer default size was only set during the first bind. This is because the `dirty_attachments_ tag` wasn't set and thus the framebuffer size was never passed down to the GL. Split to `default_size_set()` to not affect other code paths that use `size_set()`. --- source/blender/gpu/intern/gpu_framebuffer.cc | 2 +- source/blender/gpu/intern/gpu_framebuffer_private.hh | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index b8212124025..659c0e8ad95 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -378,7 +378,7 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *gpu_fb, void GPU_framebuffer_default_size(GPUFrameBuffer *gpu_fb, int width, int height) { - unwrap(gpu_fb)->size_set(width, height); + unwrap(gpu_fb)->default_size_set(width, height); } /* ---------- Viewport & Scissor Region ----------- */ diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index cb7fd62445c..267fc4f71c9 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -137,6 +137,7 @@ class FrameBuffer { void *userData); uint get_bits_per_pixel(); + /* Sets the size after creation. */ inline void size_set(int width, int height) { width_ = width; @@ -144,6 +145,15 @@ class FrameBuffer { dirty_state_ = true; } + /* Sets the size for frame-buffer with no attachments. */ + inline void default_size_set(int width, int height) + { + width_ = width; + height_ = height; + dirty_attachments_ = true; + dirty_state_ = true; + } + inline void viewport_set(const int viewport[4]) { if (!equals_v4v4_int(viewport_, viewport)) { -- 2.30.2 From 14973fabe617ad640ba8bd4a6665081dbbf4ceb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 1 Apr 2023 12:00:33 +0200 Subject: [PATCH 25/58] BLI: Rotation: Add CartesianBasis `transform_point` and `invert` --- .../blender/blenlib/BLI_math_basis_types.hh | 34 +++++++++++++++++++ .../blenlib/tests/BLI_math_rotation_test.cc | 31 +++++++++++++---- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/source/blender/blenlib/BLI_math_basis_types.hh b/source/blender/blenlib/BLI_math_basis_types.hh index 20d4a5209c4..749b3344e9a 100644 --- a/source/blender/blenlib/BLI_math_basis_types.hh +++ b/source/blender/blenlib/BLI_math_basis_types.hh @@ -468,6 +468,40 @@ struct CartesianBasis { from_orthonormal_axes(b_forward, b_up)); } +template +[[nodiscard]] inline VecBase transform_point(const CartesianBasis &basis, + const VecBase &v) +{ + VecBase result; + result[basis.x().axis().as_int()] = basis.x().is_negative() ? -v[0] : v[0]; + result[basis.y().axis().as_int()] = basis.y().is_negative() ? -v[1] : v[1]; + result[basis.z().axis().as_int()] = basis.z().is_negative() ? -v[2] : v[2]; + return result; +} + +/** + * Return the inverse transformation represented by the given basis. + * This is conceptually the equivalent to a rotation matrix transpose, but much faster. + */ +[[nodiscard]] inline CartesianBasis invert(const CartesianBasis &basis) +{ + /* Returns the column where the `axis` is found in. The sign is taken from the axis value. */ + auto search_axis = [](const CartesianBasis &basis, const Axis axis) { + if (basis.x().axis() == axis) { + return basis.x().is_negative() ? AxisSigned::X_NEG : AxisSigned::X_POS; + } + if (basis.y().axis() == axis) { + return basis.y().is_negative() ? AxisSigned::Y_NEG : AxisSigned::Y_POS; + } + return basis.z().is_negative() ? AxisSigned::Z_NEG : AxisSigned::Z_POS; + }; + CartesianBasis result; + result.x() = search_axis(basis, Axis::X); + result.y() = search_axis(basis, Axis::Y); + result.z() = search_axis(basis, Axis::Z); + return result; +} + /** \} */ } // namespace blender::math diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc index 497adc93456..24d3439717f 100644 --- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc +++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc @@ -641,10 +641,9 @@ TEST(math_rotation, CartesianBasis) expect.ptr()); } - EXPECT_EQ(from_rotation( - rotation_between(from_orthonormal_axes(src_forward, src_up), - from_orthonormal_axes(dst_forward, dst_up))), - expect); + CartesianBasis rotation = rotation_between(from_orthonormal_axes(src_forward, src_up), + from_orthonormal_axes(dst_forward, dst_up)); + EXPECT_EQ(from_rotation(rotation), expect); if (src_forward == dst_forward) { expect = float3x3::identity(); @@ -656,6 +655,11 @@ TEST(math_rotation, CartesianBasis) } EXPECT_EQ(from_rotation(rotation_between(src_forward, dst_forward)), expect); + + float3 point(1.0f, 2.0f, 3.0f); + CartesianBasis rotation_inv = invert(rotation); + /* Test inversion identity. */ + EXPECT_EQ(transform_point(rotation_inv, transform_point(rotation, point)), point); } } } @@ -667,8 +671,23 @@ TEST(math_rotation, Transform) Quaternion q(0.927091f, 0.211322f, -0.124857f, 0.283295f); float3 p(0.576f, -0.6546f, 46.354f); - p = transform_point(q, p); - EXPECT_V3_NEAR(p, float3(-4.33722f, -21.661f, 40.7608f), 1e-4f); + float3 result = transform_point(q, p); + EXPECT_V3_NEAR(result, float3(-4.33722f, -21.661f, 40.7608f), 1e-4f); + + /* Validated using `to_quaternion` before doing the transform. */ + float3 p2(1.0f, 2.0f, 3.0f); + result = transform_point(CartesianBasis(AxisSigned::X_POS, AxisSigned::Y_POS, AxisSigned::Z_POS), + p2); + EXPECT_EQ(result, float3(1.0f, 2.0f, 3.0f)); + result = transform_point( + rotation_between(from_orthonormal_axes(AxisSigned::Y_POS, AxisSigned::Z_POS), + from_orthonormal_axes(AxisSigned::X_POS, AxisSigned::Z_POS)), + p2); + EXPECT_EQ(result, float3(-2.0f, 1.0f, 3.0f)); + result = transform_point(from_orthonormal_axes(AxisSigned::Z_POS, AxisSigned::X_POS), p2); + EXPECT_EQ(result, float3(3.0f, 1.0f, 2.0f)); + result = transform_point(from_orthonormal_axes(AxisSigned::X_NEG, AxisSigned::Y_POS), p2); + EXPECT_EQ(result, float3(-2.0f, 3.0f, -1.0f)); } TEST(math_rotation, DualQuaternionNormalize) -- 2.30.2 From 85bbf53f8490f89ac71dc4c18955a87471207acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 1 Apr 2023 12:02:46 +0200 Subject: [PATCH 26/58] EEVEE-Next: Fix wrong irradiance surfel spawning area --- .../eevee_next/eevee_irradiance_cache.cc | 60 +++++++++++-------- .../eevee_next/eevee_irradiance_cache.hh | 19 +++--- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 71df0790400..76e6d49ee48 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -200,6 +200,32 @@ void IrradianceBake::sync() } } +void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid) +{ + using namespace blender::math; + const float4x4 transform(grid.transform); + + float3 scale; + math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale); + + grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * 2.0f * scale)); + + /* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to + * make any difference. The bottleneck is still the light propagation loop. */ + auto sync_view = [&](View &view, CartesianBasis basis) { + float3 extent = transform_point(invert(basis), scale); + float4x4 winmat = projection::orthographic( + -extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z); + float4x4 viewinv = math::from_loc_rot( + grid_location_, grid_orientation_ * to_quaternion(basis)); + view.sync(invert(viewinv), winmat); + }; + + sync_view(view_x_, basis_x_); + sync_view(view_y_, basis_y_); + sync_view(view_z_, basis_z_); +} + void IrradianceBake::surfels_create(const IrradianceGrid &grid) { /** @@ -209,9 +235,9 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) */ using namespace blender::math; + surfel_raster_views_sync(grid); + const float4x4 transform(grid.transform); - float3 scale; - math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale); /* Extract bounding box. Order is arbitrary as it is not important for our usage. */ const std::array bbox_corners({float3{+1, +1, +1}, @@ -227,23 +253,6 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) grid_bbox_vertices.append(transform_point(transform, point)); } - /* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to - * make any difference. The bottleneck is still the light propagation loop. */ - auto sync_view = [&](View &view, CartesianBasis basis) { - float3 extent = scale; - float4x4 winmat = projection::orthographic( - -extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z); - float4x4 viewinv = math::from_loc_rot( - grid_location_, grid_orientation_ * to_quaternion(basis)); - view.sync(invert(viewinv), winmat); - }; - - sync_view(view_x_, basis_x_); - sync_view(view_y_, basis_y_); - sync_view(view_z_, basis_z_); - - grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * 2.0f * scale)); - DRW_stats_group_start("IrradianceBake.SurfelsCount"); /* Raster the scene to query the number of surfel needed. */ @@ -252,11 +261,11 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) capture_info_buf_.surfel_len = 0u; capture_info_buf_.push_update(); - empty_raster_fb_.ensure(grid_pixel_extent_.yz()); + empty_raster_fb_.ensure(transform_point(invert(basis_x_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_x_); - empty_raster_fb_.ensure(int2(grid_pixel_extent_.x, grid_pixel_extent_.z)); + empty_raster_fb_.ensure(transform_point(invert(basis_y_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_y_); - empty_raster_fb_.ensure(grid_pixel_extent_.xy()); + empty_raster_fb_.ensure(transform_point(invert(basis_z_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_z_); DRW_stats_group_end(); @@ -281,11 +290,12 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) capture_info_buf_.do_surfel_output = true; capture_info_buf_.surfel_len = 0u; capture_info_buf_.push_update(); - empty_raster_fb_.ensure(grid_pixel_extent_.yz()); + + empty_raster_fb_.ensure(transform_point(invert(basis_x_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_x_); - empty_raster_fb_.ensure(int2(grid_pixel_extent_.x, grid_pixel_extent_.z)); + empty_raster_fb_.ensure(transform_point(invert(basis_y_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_y_); - empty_raster_fb_.ensure(grid_pixel_extent_.xy()); + empty_raster_fb_.ensure(transform_point(invert(basis_z_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_z_); DRW_stats_group_end(); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 429c3061ca1..4c89ece5576 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -44,13 +44,16 @@ class IrradianceBake { /** Capture surfel lighting to irradiance samples. */ PassSimple irradiance_capture_ps_ = {"IrradianceCapture"}; - /** Basis orientation for each baking projection. */ - math::CartesianBasis basis_x_ = math::from_orthonormal_axes(math::AxisSigned::Y_POS, - math::AxisSigned::X_POS); - math::CartesianBasis basis_y_ = math::from_orthonormal_axes(math::AxisSigned::Z_POS, - math::AxisSigned::Y_POS); - math::CartesianBasis basis_z_ = math::from_orthonormal_axes(math::AxisSigned::X_POS, - math::AxisSigned::Z_POS); + /** + * Basis orientation for each baking projection. + * Note that this is the view orientation. The projection matrix will take the negative Z axis + * as forward and Y as up. */ + math::CartesianBasis basis_x_ = { + math::AxisSigned::Y_POS, math::AxisSigned::Z_POS, math::AxisSigned::X_POS}; + math::CartesianBasis basis_y_ = { + math::AxisSigned::X_POS, math::AxisSigned::Z_POS, math::AxisSigned::Y_NEG}; + math::CartesianBasis basis_z_ = { + math::AxisSigned::X_POS, math::AxisSigned::Y_POS, math::AxisSigned::Z_POS}; /** Views for each baking projection. */ View view_x_ = {"BakingViewX"}; View view_y_ = {"BakingViewY"}; @@ -81,6 +84,8 @@ class IrradianceBake { void sync(); + /** Create the views used to rasterize the scene into surfel representation. */ + void surfel_raster_views_sync(const IrradianceGrid &grid); /** Create a surfel representation of the scene from the \a grid using the capture pipeline. */ void surfels_create(const IrradianceGrid &grid); /** Evaluate direct lighting (and also clear the surfels radiance). */ -- 2.30.2 From 9461d7fc9ad5edf66171c780b47384b6b8423d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 1 Apr 2023 17:10:23 +0200 Subject: [PATCH 27/58] EEVEE-Next: IrradianceBake: Add spherical harmonic encoding This adds a separate pass to project the neighbor surfels to the final irradiance sample points. --- .../bl_ui/properties_data_lightprobe.py | 4 +- source/blender/draw/CMakeLists.txt | 3 + .../draw/engines/eevee_next/eevee_defines.hh | 1 + .../draw/engines/eevee_next/eevee_instance.cc | 1 + .../eevee_next/eevee_irradiance_cache.cc | 73 +++++++++++++-- .../eevee_next/eevee_irradiance_cache.hh | 7 ++ .../engines/eevee_next/eevee_lightcache.cc | 3 + .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 2 + .../engines/eevee_next/eevee_shader_shared.hh | 9 +- .../eevee_display_probe_grid_frag.glsl | 11 ++- .../eevee_display_probe_grid_vert.glsl | 19 ++-- .../eevee_lightprobe_irradiance_ray_comp.glsl | 88 +++++++++++++++++++ .../shaders/eevee_lightprobe_lib.glsl | 10 +++ .../eevee_spherical_harmonics_lib.glsl | 32 +++++++ .../shaders/eevee_surfel_list_build_comp.glsl | 17 ++-- .../shaders/eevee_surfel_list_lib.glsl | 19 ++++ .../shaders/eevee_surfel_list_sort_comp.glsl | 3 + .../infos/eevee_irradiance_cache_info.hh | 18 +++- 19 files changed, 291 insertions(+), 31 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl diff --git a/scripts/startup/bl_ui/properties_data_lightprobe.py b/scripts/startup/bl_ui/properties_data_lightprobe.py index 60f8dcc0946..7ef9243653d 100644 --- a/scripts/startup/bl_ui/properties_data_lightprobe.py +++ b/scripts/startup/bl_ui/properties_data_lightprobe.py @@ -16,7 +16,7 @@ class DataButtonsPanel: class DATA_PT_context_lightprobe(DataButtonsPanel, Panel): bl_label = "" bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT'} def draw(self, context): layout = self.layout @@ -33,7 +33,7 @@ class DATA_PT_context_lightprobe(DataButtonsPanel, Panel): class DATA_PT_lightprobe(DataButtonsPanel, Panel): bl_label = "Probe" - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT'} def draw(self, context): layout = self.layout diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index a3f997a6dfd..e4c9fd92491 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -465,6 +465,8 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_light_eval_lib.glsl engines/eevee_next/shaders/eevee_light_iter_lib.glsl engines/eevee_next/shaders/eevee_light_lib.glsl + engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl + engines/eevee_next/shaders/eevee_lightprobe_lib.glsl engines/eevee_next/shaders/eevee_ltc_lib.glsl engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl @@ -502,6 +504,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl engines/eevee_next/shaders/eevee_surfel_light_comp.glsl engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl + engines/eevee_next/shaders/eevee_surfel_list_lib.glsl engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl engines/eevee_next/shaders/eevee_transparency_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 32769c0638b..b6c6686ed11 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -89,6 +89,7 @@ /* IrradianceBake. */ #define SURFEL_GROUP_SIZE 256 #define SURFEL_LIST_GROUP_SIZE 256 +#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, do 4x4x4 workgroup size. */ /* Resource bindings. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index cc89fde5655..9a58ced7ae7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -573,6 +573,7 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, } } + r_light_cache->flag |= LIGHTCACHE_BAKED; r_light_cache->flag &= ~LIGHTCACHE_BAKING; } diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 76e6d49ee48..01d4c3be31b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -17,9 +17,7 @@ namespace blender::eevee { /** \name Interface * \{ */ -void IrradianceCache::init() -{ -} +void IrradianceCache::init() {} void IrradianceCache::sync() { @@ -112,10 +110,33 @@ void IrradianceCache::display_pass_sync() for (auto i : IndexRange(light_cache->grid_len)) { LightCacheIrradianceGrid &grid = light_cache->grids[i]; - display_grids_ps_.push_constant("sphere_radius", 0.1f); + + if (grid.irradiance_L0_L1_a.data == nullptr) { + continue; + } + + auto load_texture = [&](const char *name, LightCacheTexture &cache_texture) { + if ((light_cache->flag & LIGHTCACHE_BAKED) && cache_texture.tex != nullptr) { + return; + } + if (light_cache->flag & LIGHTCACHE_BAKING) { + GPU_TEXTURE_FREE_SAFE(cache_texture.tex); + } + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; + cache_texture.tex = GPU_texture_create_3d( + name, UNPACK3(cache_texture.tex_size), 1, GPU_RGBA16F, usage, cache_texture.data); + }; + load_texture("grid.irradiance_L0_L1_a", grid.irradiance_L0_L1_a); + load_texture("grid.irradiance_L0_L1_b", grid.irradiance_L0_L1_b); + load_texture("grid.irradiance_L0_L1_c", grid.irradiance_L0_L1_c); + + display_grids_ps_.push_constant("sphere_radius", 0.3f); display_grids_ps_.push_constant("grid_resolution", int3(grid.resolution)); float4x4 grid_to_world = math::invert(float4x4(grid.world_to_grid)); display_grids_ps_.push_constant("grid_to_world", grid_to_world); + display_grids_ps_.bind_texture("irradiance_a_tx", grid.irradiance_L0_L1_a.tex); + display_grids_ps_.bind_texture("irradiance_b_tx", grid.irradiance_L0_L1_b.tex); + display_grids_ps_.bind_texture("irradiance_c_tx", grid.irradiance_L0_L1_c.tex); int cell_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, cell_count * 3 * 2); @@ -187,7 +208,16 @@ void IrradianceBake::sync() { PassSimple &pass = irradiance_capture_ps_; pass.init(); - /* TODO */ + pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_RAY)); + pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); + pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + pass.bind_ssbo("list_start_buf", &list_start_buf_); + pass.bind_ssbo("list_info_buf", &list_info_buf_); + pass.bind_image("irradiance_L0_L1_a_img", &irradiance_L0_L1_a_tx_); + pass.bind_image("irradiance_L0_L1_b_img", &irradiance_L0_L1_b_tx_); + pass.bind_image("irradiance_L0_L1_c_img", &irradiance_L0_L1_c_tx_); + pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS); + pass.dispatch(&dispatch_per_grid_sample_); } { PassSimple &pass = surfel_light_bounce_ps_; @@ -237,6 +267,26 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) surfel_raster_views_sync(grid); + dispatch_per_grid_sample_ = math::divide_ceil(grid.resolution, int3(IRRADIANCE_GRID_GROUP_SIZE)); + capture_info_buf_.irradiance_grid_size = grid.resolution; + capture_info_buf_.irradiance_grid_local_to_world = grid.transform; + capture_info_buf_.irradiance_accum_solid_angle = 0.0f; + /* Divide by twice the sample count because each ray is evaluated in both directions. */ + capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) / + (2 * inst_.sampling.sample_count()); + + eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | + GPU_TEXTURE_USAGE_HOST_READ; + + /* 32bit float is needed here otherwise we loose too much energy from rounding error during the + * accumulation when the sample count is above 500. */ + irradiance_L0_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage); + irradiance_L0_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage); + irradiance_L0_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage); + irradiance_L0_L1_a_tx_.clear(float4(0.0f)); + irradiance_L0_L1_b_tx_.clear(float4(0.0f)); + irradiance_L0_L1_c_tx_.clear(float4(0.0f)); + const float4x4 transform(grid.transform); /* Extract bounding box. Order is arbitrary as it is not important for our usage. */ @@ -367,6 +417,7 @@ void IrradianceBake::propagate_light_sample() GPU_storagebuf_clear(list_start_buf_, -1); inst_.manager->submit(surfel_light_propagate_ps_, ray_view); + inst_.manager->submit(irradiance_capture_ps_, ray_view); } void IrradianceBake::accumulate_bounce() @@ -376,6 +427,18 @@ void IrradianceBake::accumulate_bounce() void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) { + auto read_texture = [&](LightCacheTexture &cache_texture, draw::Texture &texture) { + MEM_SAFE_FREE(cache_texture.data); + cache_texture.data = (char *)texture.read(GPU_DATA_FLOAT); + copy_v3_v3_int(cache_texture.tex_size, light_cache_grid.resolution); + cache_texture.data_type = LIGHTCACHETEX_FLOAT; + cache_texture.components = 4; + }; + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + read_texture(light_cache_grid.irradiance_L0_L1_a, irradiance_L0_L1_a_tx_); + read_texture(light_cache_grid.irradiance_L0_L1_b, irradiance_L0_L1_b_tx_); + read_texture(light_cache_grid.irradiance_L0_L1_c, irradiance_L0_L1_c_tx_); + switch (inst_.debug_mode) { case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 4c89ece5576..2f465003580 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -69,6 +69,13 @@ class IrradianceBake { int3 dispatch_per_surfel_ = int3(1); /* Dispatch size for per surfel list workload. */ int3 dispatch_per_list_ = int3(1); + /* Dispatch size for per grid sample workload. */ + int3 dispatch_per_grid_sample_ = int3(1); + + /** Irradiance textures for baking. Only represents one grid in there. */ + Texture irradiance_L0_L1_a_tx_ = {"irradiance_L0_L1_a_tx_"}; + Texture irradiance_L0_L1_b_tx_ = {"irradiance_L0_L1_b_tx_"}; + Texture irradiance_L0_L1_c_tx_ = {"irradiance_L0_L1_c_tx_"}; /* Surfel per unit distance. */ float surfel_density_ = 2.0f; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index c9915c7265c..90a4a75493b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -339,6 +339,9 @@ void EEVEE_NEXT_lightcache_free(LightCache *light_cache) MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_a.data); MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_b.data); MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_c.data); + DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_a.tex); + DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_b.tex); + DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_c.tex); } MEM_SAFE_FREE(light_cache->grids); } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 8185c9b1147..0e0cca5e7a5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -148,6 +148,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_light_culling_tile"; case LIGHT_CULLING_ZBIN: return "eevee_light_culling_zbin"; + case LIGHTPROBE_IRRADIANCE_RAY: + return "eevee_lightprobe_irradiance_ray"; case SHADOW_CLIPMAP_CLEAR: return "eevee_shadow_clipmap_clear"; case SHADOW_DEBUG: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 04599976679..054ff6c5234 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -63,6 +63,8 @@ enum eShaderType { LIGHT_CULLING_TILE, LIGHT_CULLING_ZBIN, + LIGHTPROBE_IRRADIANCE_RAY, + MOTION_BLUR_GATHER, MOTION_BLUR_TILE_DILATE, MOTION_BLUR_TILE_FLATTEN_RENDER, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index a643710da9f..2f70d7b64c1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -862,13 +862,20 @@ struct Surfel { BLI_STATIC_ASSERT_ALIGN(Surfel, 16) struct CaptureInfoData { + /** Number of surfels inside the surfel buffer or the needed len. */ + int3 irradiance_grid_size; /** True if the surface shader needs to write the surfel data. */ bool1 do_surfel_output; /** True if the surface shader needs to increment the surfel_len. */ bool1 do_surfel_count; /** Number of surfels inside the surfel buffer or the needed len. */ uint surfel_len; - int _pad0; + /** Solid angle subtended by a single ray sample. Equal to `4 * pi / sample_count`. */ + float irradiance_sample_solid_angle; + /** Accumulated solid angle. Should reach `4 * pi` at the end of the accumulation. */ + float irradiance_accum_solid_angle; + /** Transform of the lightprobe object. */ + float4x4 irradiance_grid_local_to_world; }; BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl index e3b43d0e744..d462839d20d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl @@ -1,4 +1,5 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) void main() { @@ -10,7 +11,13 @@ void main() return; } + SphericalHarmonicL1 sh = spherical_harmonics_unpack(texelFetch(irradiance_a_tx, cell, 0), + texelFetch(irradiance_b_tx, cell, 0), + texelFetch(irradiance_c_tx, cell, 0)); + vec3 vN = vec3(lP, sqrt(max(0.0, 1.0 - dist_sqr))); - vec3 P = mat3(ViewMatrixInverse) * vN; - out_color = vec4(normal_view_to_world(vN) * 0.5 + 0.5, 1.0); + vec3 N = normal_view_to_world(vN); + + vec3 irradiance = spherical_harmonics_evaluate_lambert(N, sh); + out_color = vec4(irradiance, 0.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl index e2174b876fc..18b15cbbb1d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl @@ -1,5 +1,6 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) void main() { @@ -14,21 +15,21 @@ void main() vec2(-1.0, 1.0)); lP = pos[gl_VertexID % 6]; - cell_index = gl_VertexID / 6; + int cell_index = gl_VertexID / 6; - /* Keep in sync with update_irradiance_probe. */ - ivec3 cell = ivec3(cell_index / (grid_resolution.z * grid_resolution.y), - (cell_index / grid_resolution.z) % grid_resolution.y, - cell_index % grid_resolution.z); + ivec3 grid_resolution = textureSize(irradiance_a_tx, 0); - vec3 ls_cell_pos = (vec3(cell) + vec3(0.5)) / vec3(grid_resolution); - ls_cell_pos = ls_cell_pos * 2.0 - 1.0; /* Remap to (-1 ... +1). */ + cell = ivec3(cell_index / (grid_resolution.z * grid_resolution.y), + (cell_index / grid_resolution.z) % grid_resolution.y, + cell_index % grid_resolution.z); - vec3 ws_cell_pos = (grid_to_world * vec4(ls_cell_pos, 1.0)).xyz; + vec3 ws_cell_pos = lightprobe_irradiance_grid_sample_position( + grid_to_world, grid_resolution, cell); vec3 vs_offset = vec3(lP, 0.0) * sphere_radius; vec3 vP = (ViewMatrix * vec4(ws_cell_pos, 1.0)).xyz + vs_offset; gl_Position = ProjectionMatrix * vec4(vP, 1.0); - gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting. */ + /* Small bias to let the icon draw without zfighting. */ + gl_Position.z += 0.0001; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl new file mode 100644 index 00000000000..a169593f489 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -0,0 +1,88 @@ + +/** + * For every irradiance probe sample, compute the incomming radiance from both side. + * This is the same as the surfel ray but we do not actually transport the light, we only capture + * the irradiance as spherical harmonic coefficients. + * + * Dispatched as 1 thread per irradiance probe sample. + */ + +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surfel_list_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh) +{ + spherical_harmonics_encode_signal_sample( + L, irradiance * capture_info_buf.irradiance_sample_solid_angle, sh); +} + +void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh) +{ + vec3 L = safe_normalize(surfel_emitter.position - P); + bool facing = dot(-L, surfel_emitter.normal) > 0.0; + vec3 irradiance = facing ? surfel_emitter.radiance_front : surfel_emitter.radiance_back; + + irradiance_capture(L, irradiance, sh); +} + +void main() +{ + if (any(greaterThanEqual(gl_GlobalInvocationID, capture_info_buf.irradiance_grid_size))) { + return; + } + + ivec3 grid_coord = ivec3(gl_GlobalInvocationID); + + vec3 P = lightprobe_irradiance_grid_sample_position( + capture_info_buf.irradiance_grid_local_to_world, + capture_info_buf.irradiance_grid_size, + grid_coord); + + /* Project to get ray linked list. */ + float irradiance_sample_ray_distance; + int list_index = surfel_list_index_get(P, irradiance_sample_ray_distance); + + /* Walk the ray to get which surfels the irradiance sample is between. */ + int surfel_prev = -1; + int surfel_next = list_start_buf[list_index]; + for (; surfel_next > -1; surfel_next = surfel_buf[surfel_next].next) { + /* Reminder: List is sorted with highest value first. */ + if (surfel_buf[surfel_next].ray_distance < irradiance_sample_ray_distance) { + break; + } + surfel_prev = surfel_next; + } + + vec3 sky_L = cameraVec(P); + + SphericalHarmonicL1 sh = spherical_harmonics_unpack( + imageLoad(irradiance_L0_L1_a_img, grid_coord), + imageLoad(irradiance_L0_L1_b_img, grid_coord), + imageLoad(irradiance_L0_L1_c_img, grid_coord)); + + if (surfel_next > -1) { + irradiance_capture(surfel_buf[surfel_next], P, sh); + } + else { + /* TODO(fclem): Sky radiance. */ + irradiance_capture(sky_L, vec3(0.0), sh); + } + + if (surfel_prev > -1) { + irradiance_capture(surfel_buf[surfel_prev], P, sh); + } + else { + /* TODO(fclem): Sky radiance. */ + irradiance_capture(-sky_L, vec3(0.0), sh); + } + + vec4 sh_a, sh_b, sh_c; + spherical_harmonics_pack(sh, sh_a, sh_b, sh_c); + imageStore(irradiance_L0_L1_a_img, grid_coord, sh_a); + imageStore(irradiance_L0_L1_b_img, grid_coord, sh_b); + imageStore(irradiance_L0_L1_c_img, grid_coord, sh_c); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl new file mode 100644 index 00000000000..0b25464ce2f --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl @@ -0,0 +1,10 @@ + +vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world, + ivec3 grid_resolution, + ivec3 cell_coord) +{ + vec3 ls_cell_pos = (vec3(cell_coord) + vec3(0.5)) / vec3(grid_resolution); + ls_cell_pos = ls_cell_pos * 2.0 - 1.0; + vec3 ws_cell_pos = (grid_local_to_world * vec4(ls_cell_pos, 1.0)).xyz; + return ws_cell_pos; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index ed907207193..a935f168217 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -102,6 +102,7 @@ struct SphericalHarmonicL2 { /** \name Encode * * Decompose an input signal into spherical harmonic coefficients. + * Note that `amplitude` need to be scaled by solid angle. * \{ */ void spherical_harmonics_L0_encode_signal_sample(vec3 direction, @@ -213,3 +214,34 @@ vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Load/Store + * + * This section define the compression scheme of spherical harmonic data. + * \{ */ + +SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a, vec4 L0_L1_b, vec4 L0_L1_c) +{ + SphericalHarmonicL1 sh; + sh.L0.M0 = L0_L1_a.xyz; + sh.L1.Mn1 = L0_L1_b.xyz; + sh.L1.M0 = L0_L1_c.xyz; + sh.L1.Mp1 = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w); + return sh; +} + +void spherical_harmonics_pack(SphericalHarmonicL1 sh, + out vec4 L0_L1_a, + out vec4 L0_L1_b, + out vec4 L0_L1_c) +{ + L0_L1_a.xyz = sh.L0.M0; + L0_L1_b.xyz = sh.L1.Mn1; + L0_L1_c.xyz = sh.L1.M0; + L0_L1_a.w = sh.L1.Mp1.x; + L0_L1_b.w = sh.L1.Mp1.y; + L0_L1_c.w = sh.L1.Mp1.z; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl index 982af7c5b0f..0ce97ca0b1d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl @@ -12,7 +12,7 @@ #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surfel_list_lib.glsl) void main() { @@ -21,16 +21,11 @@ void main() return; } - vec4 hP = point_world_to_ndc(surfel_buf[surfel_index].position); - - surfel_buf[surfel_index].ray_distance = -hP.z; - - vec2 ssP_surfel = hP.xy * 0.5 + 0.5; - ivec2 ray_coord_on_grid = clamp(ivec2(ssP_surfel * vec2(list_info_buf.ray_grid_size)), - ivec2(0), - list_info_buf.ray_grid_size - 1); - int list_index = ray_coord_on_grid.y * list_info_buf.ray_grid_size.x + ray_coord_on_grid.x; - + float ray_distance; + int list_index = surfel_list_index_get(surfel_buf[surfel_index].position, ray_distance); + /* Do separate assignement to avoid reference to buffer in arguments which is tricky to cross + * compile. */ + surfel_buf[surfel_index].ray_distance = ray_distance; /* NOTE: We only need to init the `list_start_buf` to -1 for the whole list to be valid since * every surfel will load its `next` value from the list head. */ surfel_buf[surfel_index].next = atomicExchange(list_start_buf[list_index], surfel_index); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl new file mode 100644 index 00000000000..11b6b9bdf53 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_lib.glsl @@ -0,0 +1,19 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +/** + * Return the coresponding list index in the `list_start_buf` for a given world position. + * It will clamp any coordinate outside valid bounds to nearest list. + * Also return the surfel sorting value as `r_ray_distance`. + */ +int surfel_list_index_get(vec3 P, out float r_ray_distance) +{ + vec4 hP = point_world_to_ndc(P); + r_ray_distance = -hP.z; + vec2 ssP = hP.xy * 0.5 + 0.5; + ivec2 ray_coord_on_grid = ivec2(ssP * vec2(list_info_buf.ray_grid_size)); + ray_coord_on_grid = clamp(ray_coord_on_grid, ivec2(0), list_info_buf.ray_grid_size - 1); + + int list_index = ray_coord_on_grid.y * list_info_buf.ray_grid_size.x + ray_coord_on_grid.x; + return list_index; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl index 1fab81fc859..b454ff4ec5e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl @@ -123,6 +123,9 @@ void main() } } + /* Update list start for irradiance sample capture. */ + list_start_buf[list_index] = sorted_list.first; + /* Now that we have a sorted list, try to avoid connection from coplanar surfels. * For that we disconnect them and link them to the first non-coplanar surfel. * Note that this changes the list to a tree, which doesn't affect the rest of the algorithm. diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index e1475cf3cd4..51a245f65d2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -63,7 +63,7 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray) GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "") .smooth(Type::VEC2, "lP") - .flat(Type::INT, "cell_index"); + .flat(Type::IVEC3, "cell"); GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) .additional_info("eevee_shared", "draw_view") @@ -74,4 +74,20 @@ GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) .push_constant(Type::FLOAT, "sphere_radius") .push_constant(Type::IVEC3, "grid_resolution") .push_constant(Type::MAT4, "grid_to_world") + .sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx") + .sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx") + .sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) + .local_group_size(IRRADIANCE_GRID_GROUP_SIZE, + IRRADIANCE_GRID_GROUP_SIZE, + IRRADIANCE_GRID_GROUP_SIZE) + .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") + .storage_buf(0, Qualifier::READ, "int", "list_start_buf[]") + .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_a_img") + .image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_b_img") + .image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_c_img") + .compute_source("eevee_lightprobe_irradiance_ray_comp.glsl") .do_static_compilation(true); -- 2.30.2 From a0610ef654fce6fe862da04fce8c9e8c87234cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 2 Apr 2023 20:09:20 +0200 Subject: [PATCH 28/58] EEVEE-Next: IrradianceBake: Fix shadow tagging & rendering --- .../draw/engines/eevee_next/eevee_camera.cc | 24 ++++++++++++++++++- .../draw/engines/eevee_next/eevee_instance.cc | 5 ++++ .../eevee_next/eevee_irradiance_cache.cc | 7 +++--- .../eevee_next/eevee_irradiance_cache.hh | 11 +++++---- .../draw/engines/eevee_next/eevee_shadow.cc | 9 ++++--- .../shaders/eevee_shadow_tag_usage_lib.glsl | 3 ++- .../eevee_shadow_tag_usage_surfels_comp.glsl | 7 +++++- .../shaders/infos/eevee_shadow_info.hh | 13 ++++++---- 8 files changed, 61 insertions(+), 18 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc index 2526f2875d0..e4e5c5920a6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_camera.cc +++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc @@ -78,7 +78,29 @@ void Camera::sync() CameraData &data = data_; - if (inst_.drw_view) { + /* WORKAROUND(fclem): This is an abomination that should be remove ASAP. */ + bool is_first_baking_sync = inst_.light_probes.grids.size() == 0; + if (inst_.is_baking() && !is_first_baking_sync) { + /* Any view so that shadows and light culling works during irradiance bake. */ + draw::View &view = inst_.irradiance_cache.bake.view_z_; + data.viewmat = view.viewmat(); + data.viewinv = view.viewinv(); + data.winmat = view.winmat(); + data.wininv = view.wininv(); + data.persmat = data.winmat * data.viewmat; + data.persinv = math::invert(data.persmat); + data.uv_scale = float2(1.0f); + data.uv_bias = float2(0.0f); + data.type = CAMERA_ORTHO; + + /* \note: Follow camera parameters where distances are positive in front of the camera. */ + data.clip_near = -view.far_clip(); + data.clip_far = -view.near_clip(); + data.fisheye_fov = data.fisheye_lens = -1.0f; + data.equirect_bias = float2(0.0f); + data.equirect_scale = float2(0.0f); + } + else if (inst_.drw_view) { DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false); DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true); DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false); diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 9a58ced7ae7..bbebf8ab4cc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -520,6 +520,7 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, }; /* Count probes. */ + /* TODO(fclem): Ideally, this should only iterate the despgraph and not do a full sync. */ custom_pipeline_wrapper([&]() { manager->begin_sync(); render_sync(); @@ -538,12 +539,16 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, for (auto i : light_probes.grids.index_range()) { custom_pipeline_wrapper([&]() { + GPU_debug_capture_begin(); + irradiance_cache.bake.surfel_raster_views_sync(light_probes.grids[i]); /* TODO: lightprobe visibility group option. */ manager->begin_sync(); render_sync(); manager->end_sync(); + irradiance_cache.bake.surfels_create(light_probes.grids[i]); irradiance_cache.bake.surfels_lights_eval(); + GPU_debug_capture_end(); }); int bounce_len = scene->eevee.gi_diffuse_bounces; diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 127b7126521..bef3a0070df 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -267,8 +267,6 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) */ using namespace blender::math; - surfel_raster_views_sync(grid); - dispatch_per_grid_sample_ = math::divide_ceil(grid.resolution, int3(IRRADIANCE_GRID_GROUP_SIZE)); capture_info_buf_.irradiance_grid_size = grid.resolution; capture_info_buf_.irradiance_grid_local_to_world = grid.transform; @@ -350,6 +348,9 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) empty_raster_fb_.ensure(transform_point(invert(basis_z_), grid_pixel_extent_).xy()); inst_.pipelines.capture.render(view_z_); + /* Sync with any other following pass using the surfel buffer. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + DRW_stats_group_end(); } @@ -363,7 +364,7 @@ void IrradianceBake::surfels_lights_eval() inst_.shadows.set_view(view_z_); inst_.render_buffers.release(); - inst_.manager->submit(surfel_light_eval_ps_); + inst_.manager->submit(surfel_light_eval_ps_, view_z_); } void IrradianceBake::propagate_light_sample() diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 1fa32ae0389..7e2fb57e75d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -17,18 +17,24 @@ namespace blender::eevee { class Instance; class CapturePipeline; +class ShadowModule; +class Camera; /** * Baking related pass and data. Not used at runtime. */ class IrradianceBake { friend CapturePipeline; + friend ShadowModule; + friend Camera; private: Instance &inst_; /** Light cache being baked. */ LightCache *light_cache_ = nullptr; + /** Surface elements that represent the scene. */ + SurfelBuf surfels_buf_; /** Capture state. */ CaptureInfoBuf capture_info_buf_; /** Framebuffer. */ @@ -81,13 +87,10 @@ class IrradianceBake { float3 grid_location_; /* Bounding box vertices of the irradiance grid being baked. In world space. */ Vector grid_bbox_vertices; - - public: - /** Surface elements that represent the scene. */ - SurfelBuf surfels_buf_; /* Surfel per unit distance. */ float surfel_density_ = 2.0f; + public: IrradianceBake(Instance &inst) : inst_(inst){}; void sync(); diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index 90f8e843499..298901cf4d3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -708,6 +708,7 @@ void ShadowModule::begin_sync() if (inst_.is_baking()) { SurfelBuf &surfels_buf = inst_.irradiance_cache.bake.surfels_buf_; + CaptureInfoBuf &capture_info_buf = inst_.irradiance_cache.bake.capture_info_buf_; float surfel_coverage_area = inst_.irradiance_cache.bake.surfel_density_; /* Directional shadows. */ @@ -720,13 +721,15 @@ void ShadowModule::begin_sync() sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_USAGE_SURFELS)); sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data); sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data); - sub.bind_ssbo("surfels_buf", &surfels_buf); + sub.bind_ssbo("surfel_buf", &surfels_buf); + sub.bind_ssbo("capture_info_buf", &capture_info_buf); sub.push_constant("directional_level", directional_level); sub.push_constant("tilemap_projection_ratio", projection_ratio); inst_.lights.bind_resources(&sub); - sub.dispatch(int3(surfels_buf.size(), 1, 1)); + sub.dispatch(&inst_.irradiance_cache.bake.dispatch_per_surfel_); - return; /* Skip opaque and transparent tagging for light baking. */ + /* Skip opaque and transparent tagging for light baking. */ + return; } { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index 97f0ab1b6c8..d2d4a6726a4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -198,7 +198,8 @@ void shadow_tag_usage_surfel(Surfel surfel, int directional_level) LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx) { - float dist_to_cam = 1; /* Set it to 1 to avoid changing footprint_ratio. */ + /* Set distance to camera to 1 to avoid changing footprint_ratio. */ + float dist_to_cam = 1.0; shadow_tag_usage_tilemap_punctual(l_idx, P, dist_to_cam, 0); } LIGHT_FOREACH_END diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl index efa387467aa..ed81b047cac 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_surfels_comp.glsl @@ -11,6 +11,11 @@ void main() { - Surfel surfel = surfels_buf[gl_GlobalInvocationID.x]; + int index = int(gl_GlobalInvocationID.x); + if (index >= capture_info_buf.surfel_len) { + return; + } + + Surfel surfel = surfel_buf[index]; shadow_tag_usage_surfel(surfel, directional_level); } diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh index aeee7ff9520..b833134d9e7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh @@ -62,13 +62,16 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque) GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_surfels) .do_static_compilation(true) - .local_group_size(1, 1, 1) - .storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]") - .storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]") - .storage_buf(7, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]") + .local_group_size(SURFEL_GROUP_SIZE) + .storage_buf(6, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]") + .storage_buf(7, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]") .push_constant(Type::INT, "directional_level") .push_constant(Type::FLOAT, "tilemap_projection_ratio") - .additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data") + .additional_info("eevee_shared", + "draw_view", + "draw_view_culling", + "eevee_light_data", + "eevee_surfel_common") .compute_source("eevee_shadow_tag_usage_surfels_comp.glsl"); GPU_SHADER_INTERFACE_INFO(eevee_shadow_tag_transparent_iface, "interp") -- 2.30.2 From d30ad0e381d55dccfcb2c53c71072610e442f08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 5 Apr 2023 12:47:24 +0200 Subject: [PATCH 29/58] Cleanup: EEVEE-Next: Avoid implicit type cast --- .../eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index d2d4a6726a4..680eeaff587 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -50,7 +50,7 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi vec3 lP = shadow_world_to_local(light, P); - if (radius == 0) { + if (radius == 0.0) { ShadowCoordinates coord = shadow_directional_coordinates(light, lP); shadow_tag_usage_tile(light, coord.tile_coord, 0, coord.tilemap_index); } @@ -62,9 +62,9 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi for (int level = min_level; level <= max_level; level++) { ShadowCoordinates coord_min = shadow_directional_coordinates_at_level( - light, lP - vec3(radius, radius, 0), level); + light, lP - vec3(radius, radius, 0.0), level); ShadowCoordinates coord_max = shadow_directional_coordinates_at_level( - light, lP + vec3(radius, radius, 0), level); + light, lP + vec3(radius, radius, 0.0), level); for (int x = coord_min.tile_coord.x; x <= coord_max.tile_coord.x; x++) { for (int y = coord_min.tile_coord.y; y <= coord_max.tile_coord.y; y++) { -- 2.30.2 From 892f7077bcf14d643ab23563f6fd7becaf6553be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 13 Apr 2023 11:51:59 +0200 Subject: [PATCH 30/58] Add new lightprobe cache Waiting for the commit to be in master. --- source/blender/blenkernel/BKE_lightprobe.h | 38 ++++ .../blender/blenkernel/intern/lightprobe.cc | 165 ++++++++++++++ source/blender/blenkernel/intern/object.cc | 24 ++ .../blender/editors/render/render_intern.hh | 3 + source/blender/editors/render/render_ops.cc | 3 + .../blender/editors/render/render_shading.cc | 213 ++++++++++++++++++ .../blender/makesdna/DNA_lightprobe_types.h | 126 ++++++++++- source/blender/makesdna/DNA_object_types.h | 6 + 8 files changed, 575 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_lightprobe.h b/source/blender/blenkernel/BKE_lightprobe.h index d7f9ad9a5bd..9ccc9f0f4fd 100644 --- a/source/blender/blenkernel/BKE_lightprobe.h +++ b/source/blender/blenkernel/BKE_lightprobe.h @@ -14,10 +14,48 @@ extern "C" { struct LightProbe; struct Main; +struct BlendWriter; +struct BlendDataReader; +struct LightProbeObjectCache; +struct LightProbeGridCacheFrame; +struct Object; void BKE_lightprobe_type_set(struct LightProbe *probe, short lightprobe_type); void *BKE_lightprobe_add(struct Main *bmain, const char *name); +void BKE_lightprobe_cache_blend_write(struct BlendWriter *writer, + struct LightProbeObjectCache *cache); + +void BKE_lightprobe_cache_blend_read(struct BlendDataReader *reader, + struct LightProbeObjectCache *cache); + +/** + * Create a single empty irradiance grid cache. + */ +struct LightProbeGridCacheFrame *BKE_lightprobe_grid_cache_frame_create(void); + +/** + * Free a single grid cache. + */ +void BKE_lightprobe_grid_cache_frame_free(struct LightProbeGridCacheFrame *cache); + +/** + * Create the grid cache list depending on the lightprobe baking settings. + * The list is left empty to be filled by the baking process. + */ +void BKE_lightprobe_cache_create(struct Object *object); + +/** + * Free all irradiance grids allocated for the given object. + */ +void BKE_lightprobe_cache_free(struct Object *object); + +/** + * Return the number of sample stored inside an irradiance cache. + * This depends on the light cache type. + */ +int64_t BKE_lightprobe_grid_cache_frame_sample_count(const struct LightProbeGridCacheFrame *cache); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/lightprobe.cc b/source/blender/blenkernel/intern/lightprobe.cc index 4ad560d8245..2b2676b3be6 100644 --- a/source/blender/blenkernel/intern/lightprobe.cc +++ b/source/blender/blenkernel/intern/lightprobe.cc @@ -12,6 +12,8 @@ #include "DNA_lightprobe_types.h" #include "DNA_object_types.h" +#include "BLI_math_base.h" +#include "BLI_span.hh" #include "BLI_utildefines.h" #include "BKE_anim_data.h" @@ -129,3 +131,166 @@ void *BKE_lightprobe_add(Main *bmain, const char *name) return probe; } + +static void lightprobe_grid_cache_frame_blend_write(BlendWriter *writer, + const LightProbeGridCacheFrame *cache) +{ + if (cache->block_infos != nullptr) { + BLO_write_struct_array(writer, LightProbeGridCacheFrame, cache->block_len, cache->block_infos); + } + + int64_t sample_count = BKE_lightprobe_grid_cache_frame_sample_count(cache); + + if (cache->irradiance.L0 != nullptr) { + BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L0); + } + if (cache->irradiance.L1_a != nullptr) { + BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_a); + } + if (cache->irradiance.L1_b != nullptr) { + BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_b); + } + if (cache->irradiance.L1_c != nullptr) { + BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_c); + } + + if (cache->visibility.L0 != nullptr) { + BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L0); + } + if (cache->visibility.L1_a != nullptr) { + BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L1_a); + } + if (cache->visibility.L1_b != nullptr) { + BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L1_b); + } + if (cache->visibility.L1_c != nullptr) { + BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L1_c); + } + + if (cache->connectivity.bitmask != nullptr) { + BLO_write_struct_array( + writer, LightProbeGridCacheFrame, sample_count, cache->connectivity.bitmask); + } +} + +static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader, + LightProbeGridCacheFrame *cache) +{ + if (cache->block_infos != nullptr) { + BLO_read_data_address(reader, &cache->block_infos); + } + + int64_t sample_count = BKE_lightprobe_grid_cache_frame_sample_count(cache); + + /* Baking data is not stored. */ + cache->baking.L0 = nullptr; + cache->baking.L1_a = nullptr; + cache->baking.L1_b = nullptr; + cache->baking.L1_c = nullptr; + + if (cache->irradiance.L0 != nullptr) { + BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L0); + } + if (cache->irradiance.L1_a != nullptr) { + BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_a); + } + if (cache->irradiance.L1_b != nullptr) { + BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_b); + } + if (cache->irradiance.L1_c != nullptr) { + BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_c); + } + + if (cache->visibility.L0 != nullptr) { + BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L0); + } + if (cache->visibility.L1_a != nullptr) { + BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L1_a); + } + if (cache->visibility.L1_b != nullptr) { + BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L1_b); + } + if (cache->visibility.L1_c != nullptr) { + BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L1_c); + } + + if (cache->connectivity.bitmask != nullptr) { + BLO_read_data_address(reader, &cache->connectivity.bitmask); + } +} + +void BKE_lightprobe_cache_blend_write(BlendWriter *writer, LightProbeObjectCache *cache) +{ + if (cache->grid_static_cache != nullptr) { + BLO_write_struct(writer, LightProbeGridCacheFrame, cache->grid_static_cache); + lightprobe_grid_cache_frame_blend_write(writer, cache->grid_static_cache); + } +} + +void BKE_lightprobe_cache_blend_read(BlendDataReader *reader, LightProbeObjectCache *cache) +{ + if (cache->grid_static_cache != nullptr) { + BLO_read_data_address(reader, &cache->grid_static_cache); + lightprobe_grid_cache_frame_blend_read(reader, cache->grid_static_cache); + } +} + +template static void spherical_harmonic_free(T &data) +{ + MEM_SAFE_FREE(data.L0); + MEM_SAFE_FREE(data.L1_a); + MEM_SAFE_FREE(data.L1_b); + MEM_SAFE_FREE(data.L1_c); +} + +LightProbeGridCacheFrame *BKE_lightprobe_grid_cache_frame_create() +{ + LightProbeGridCacheFrame *cache = static_cast( + MEM_callocN(sizeof(LightProbeGridCacheFrame), "LightProbeGridCacheFrame")); + return cache; +} + +void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache) +{ + MEM_SAFE_FREE(cache->block_infos); + spherical_harmonic_free(cache->baking); + spherical_harmonic_free(cache->irradiance); + spherical_harmonic_free(cache->visibility); + MEM_SAFE_FREE(cache->connectivity.bitmask); + + MEM_SAFE_FREE(cache); +} + +void BKE_lightprobe_cache_create(Object *object) +{ + BLI_assert(object->lightprobe_cache == nullptr); + + object->lightprobe_cache = static_cast( + MEM_callocN(sizeof(LightProbeObjectCache), "LightProbeObjectCache")); +} + +void BKE_lightprobe_cache_free(Object *object) +{ + if (object->lightprobe_cache == nullptr) { + return; + } + + LightProbeObjectCache *cache = object->lightprobe_cache; + + if (cache->shared == false) { + if (cache->grid_static_cache != nullptr) { + BKE_lightprobe_grid_cache_frame_free(cache->grid_static_cache); + } + } + + MEM_SAFE_FREE(object->lightprobe_cache); +} + +int64_t BKE_lightprobe_grid_cache_frame_sample_count(const LightProbeGridCacheFrame *cache) +{ + if (cache->data_layout == LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION) { + return cache->block_len * cube_i(cache->block_size); + } + /* LIGHTPROBE_CACHE_UNIFORM_GRID */ + return cache->size[0] * cache->size[1] * cache->size[2]; +} diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index ace45ee4e95..c05722ceda9 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -267,6 +267,18 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in if (ob_src->lightgroup) { ob_dst->lightgroup = (LightgroupMembership *)MEM_dupallocN(ob_src->lightgroup); } + + if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) != 0) { + if (ob_src->lightprobe_cache) { + /* Reference the original object data. */ + ob_dst->lightprobe_cache = (LightProbeObjectCache *)MEM_dupallocN(ob_src->lightprobe_cache); + ob_dst->lightprobe_cache->shared = true; + } + } + else { + /* Do not copy lightprobe's cache. */ + ob_dst->lightprobe_cache = nullptr; + } } static void object_free_data(ID *id) @@ -319,6 +331,8 @@ static void object_free_data(ID *id) BKE_previewimg_free(&ob->preview); MEM_SAFE_FREE(ob->lightgroup); + + BKE_lightprobe_cache_free(ob); } static void library_foreach_modifiersForeachIDLink(void *user_data, @@ -597,6 +611,11 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre if (ob->lightgroup) { BLO_write_struct(writer, LightgroupMembership, ob->lightgroup); } + + if (ob->lightprobe_cache) { + BLO_write_struct(writer, LightProbeObjectCache, ob->lightprobe_cache); + BKE_lightprobe_cache_blend_write(writer, ob->lightprobe_cache); + } } /* XXX deprecated - old animation system */ @@ -815,6 +834,11 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id) BKE_previewimg_blend_read(reader, ob->preview); BLO_read_data_address(reader, &ob->lightgroup); + + BLO_read_data_address(reader, &ob->lightprobe_cache); + if (ob->lightprobe_cache) { + BKE_lightprobe_cache_blend_read(reader, ob->lightprobe_cache); + } } /* XXX deprecated - old animation system */ diff --git a/source/blender/editors/render/render_intern.hh b/source/blender/editors/render/render_intern.hh index e9adf760ee7..2497c59ab4c 100644 --- a/source/blender/editors/render/render_intern.hh +++ b/source/blender/editors/render/render_intern.hh @@ -41,6 +41,9 @@ void SCENE_OT_view_layer_remove_unused_lightgroups(struct wmOperatorType *ot); void SCENE_OT_light_cache_bake(struct wmOperatorType *ot); void SCENE_OT_light_cache_free(struct wmOperatorType *ot); +void OBJECT_OT_lightprobe_cache_bake(struct wmOperatorType *ot); +void OBJECT_OT_lightprobe_cache_free(struct wmOperatorType *ot); + void SCENE_OT_render_view_add(struct wmOperatorType *ot); void SCENE_OT_render_view_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/render/render_ops.cc b/source/blender/editors/render/render_ops.cc index 933a4d0cc88..84ed29fade7 100644 --- a/source/blender/editors/render/render_ops.cc +++ b/source/blender/editors/render/render_ops.cc @@ -28,6 +28,9 @@ void ED_operatortypes_render() WM_operatortype_append(OBJECT_OT_material_slot_move); WM_operatortype_append(OBJECT_OT_material_slot_remove_unused); + WM_operatortype_append(OBJECT_OT_lightprobe_cache_bake); + WM_operatortype_append(OBJECT_OT_lightprobe_cache_free); + WM_operatortype_append(MATERIAL_OT_new); WM_operatortype_append(TEXTURE_OT_new); WM_operatortype_append(WORLD_OT_new); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index ed5c80663d0..4262453b43a 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -37,6 +37,7 @@ #include "BKE_image.h" #include "BKE_layer.h" #include "BKE_lib_id.h" +#include "BKE_lightprobe.h" #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_material.h" @@ -53,6 +54,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #ifdef WITH_FREESTYLE # include "BKE_freestyle.h" @@ -1333,6 +1335,8 @@ enum { LIGHTCACHE_SUBSET_ALL = 0, LIGHTCACHE_SUBSET_DIRTY, LIGHTCACHE_SUBSET_CUBE, + LIGHTCACHE_SUBSET_SELECTED, + LIGHTCACHE_SUBSET_ACTIVE, }; static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op) @@ -1349,6 +1353,9 @@ static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op) case LIGHTCACHE_SUBSET_DIRTY: /* Leave tag untouched. */ break; + default: + BLI_assert_unreachable(); + break; } } } @@ -1505,6 +1512,169 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } +/* NOTE: New version destined to replace the old lightcache bake operator. */ + +static void lightprobe_cache_bake_start(bContext *C, wmOperator *op) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + + auto is_irradiance_volume = [](Object *ob) -> bool { + return ob->type == OB_LIGHTPROBE && + static_cast(ob->data)->type == LIGHTPROBE_TYPE_GRID; + }; + + auto irradiance_volume_setup = [](Object *ob) { + BKE_lightprobe_cache_free(ob); + BKE_lightprobe_cache_create(ob); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + }; + + int subset = RNA_enum_get(op->ptr, "subset"); + switch (subset) { + case LIGHTCACHE_SUBSET_ALL: { + FOREACH_OBJECT_BEGIN (scene, view_layer, ob) { + if (is_irradiance_volume(ob)) { + irradiance_volume_setup(ob); + } + } + FOREACH_OBJECT_END; + break; + } + case LIGHTCACHE_SUBSET_DIRTY: { + FOREACH_OBJECT_BEGIN (scene, view_layer, ob) { + if (is_irradiance_volume(ob) && ob->lightprobe_cache && ob->lightprobe_cache->dirty) { + irradiance_volume_setup(ob); + } + } + FOREACH_OBJECT_END; + break; + } + case LIGHTCACHE_SUBSET_SELECTED: { + uint objects_len = 0; + ObjectsInViewLayerParams parameters; + parameters.filter_fn = nullptr; + parameters.no_dup_data = true; + Object **objects = BKE_view_layer_array_selected_objects_params( + view_layer, nullptr, &objects_len, ¶meters); + for (Object *ob : blender::MutableSpan(objects, objects_len)) { + if (is_irradiance_volume(ob)) { + irradiance_volume_setup(ob); + } + } + MEM_freeN(objects); + break; + } + case LIGHTCACHE_SUBSET_ACTIVE: { + Object *active_ob = CTX_data_active_object(C); + if (is_irradiance_volume(active_ob)) { + irradiance_volume_setup(active_ob); + } + break; + } + default: + BLI_assert_unreachable(); + break; + } +} + +static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + Scene *scene = CTX_data_scene(C); + + lightprobe_cache_bake_start(C, op); + + WM_event_add_modal_handler(C, op); + + /* store actual owner of job, so modal operator could check for it, + * the reason of this is that active scene could change when rendering + * several layers from compositor #31800. */ + op->customdata = scene; + + WM_cursor_wait(false); + + return OPERATOR_RUNNING_MODAL; +} + +static int lightprobe_cache_bake_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Scene *scene = (Scene *)op->customdata; + + /* No running bake, remove handler and pass through. */ + if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LIGHT_BAKE)) { + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + } + + /* Running bake. */ + switch (event->type) { + case EVT_ESCKEY: + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_PASS_THROUGH; +} + +static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = (Scene *)op->customdata; + + /* Kill on cancel, because job is using op->reports. */ + WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE); +} + +/* Executes blocking bake. */ +static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op) +{ + lightprobe_cache_bake_start(C, op); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_lightprobe_cache_bake(wmOperatorType *ot) +{ + static const EnumPropertyItem light_cache_subset_items[] = { + {LIGHTCACHE_SUBSET_ALL, "ALL", 0, "All Light Probes", "Bake all light probes"}, + {LIGHTCACHE_SUBSET_DIRTY, + "DIRTY", + 0, + "Dirty Only", + "Only bake light probes that are marked as dirty"}, + {LIGHTCACHE_SUBSET_SELECTED, + "SELECTED", + 0, + "Selected Only", + "Only bake selected light probes"}, + {LIGHTCACHE_SUBSET_ACTIVE, "ACTIVE", 0, "Active Only", "Only bake the active light probe"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + /* identifiers */ + ot->name = "Bake Light Cache"; + ot->idname = "OBJECT_OT_lightprobe_cache_bake"; + ot->description = "Bake the active view layer lighting"; + + /* api callbacks */ + ot->invoke = lightprobe_cache_bake_invoke; + ot->modal = lightprobe_cache_bake_modal; + ot->cancel = lightprobe_cache_bake_cancel; + ot->exec = lightprobe_cache_bake_exec; + + ot->prop = RNA_def_int(ot->srna, + "delay", + 0, + 0, + 2000, + "Delay", + "Delay in millisecond before baking starts", + 0, + 2000); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); + + ot->prop = RNA_def_enum( + ot->srna, "subset", light_cache_subset_items, 0, "Subset", "Subset of probes to update"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1558,6 +1728,49 @@ void SCENE_OT_light_cache_free(wmOperatorType *ot) ot->poll = light_cache_free_poll; } +/* NOTE: New version destined to replace the old lightcache bake operator. */ + +static bool lightprobe_cache_free_poll(bContext *C) +{ + Object *object = CTX_data_active_object(C); + + return object && object->lightprobe_cache != nullptr; +} + +static int lightprobe_cache_free_exec(bContext *C, wmOperator * /*op*/) +{ + Scene *scene = CTX_data_scene(C); + Object *object = CTX_data_active_object(C); + + /* Kill potential bake job first (see #57011). */ + wmWindowManager *wm = CTX_wm_manager(C); + WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE); + + if (object->lightprobe_cache == nullptr) { + return OPERATOR_CANCELLED; + } + + BKE_lightprobe_cache_free(object); + + DEG_id_tag_update(&object->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, scene); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_lightprobe_cache_free(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Light Cache"; + ot->idname = "OBJECT_OT_lightprobe_cache_free"; + ot->description = "Delete cached indirect lighting"; + + /* api callbacks */ + ot->exec = lightprobe_cache_free_exec; + ot->poll = lightprobe_cache_free_poll; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 94ad46be693..1aacf407af5 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -61,9 +61,6 @@ typedef struct LightProbe { struct Image *image; /** Object visibility group, inclusive or exclusive. */ struct Collection *visibility_grp; - - /* Runtime display data */ - float distfalloff, distgridinf; } LightProbe; /* Probe->type */ @@ -223,6 +220,129 @@ enum { LIGHTCACHETEX_UINT = (1 << 2), }; +/* -------------------------------------------------------------------- */ +/** \name Irradiance grid data storage + * + * Each spherical harmonic band is stored separately. This allow loading only a specific band. + * The layout of each array is set by the #LightProbeGridType. + * Any unavailable data is be set to nullptr. + * \{ */ + +/** + * Irradiance data (RGB) stored along visibility (A). + * This is the format used during baking and is used for visualizing the baking process. + */ +typedef struct LightProbeBakingData { + float (*L0)[4]; + float (*L1_a)[4]; + float (*L1_b)[4]; + float (*L1_c)[4]; +} LightProbeBakingData; + +/** + * Irradiance stored as RGB triple using scene referred color space. + */ +typedef struct LightProbeIrradianceData { + float (*L0)[3]; + float (*L1_a)[3]; + float (*L1_b)[3]; + float (*L1_c)[3]; +} LightProbeIrradianceData; + +/** + * Normalized visibility of distant light. Used for compositing grids together. + */ +typedef struct LightProbeVisibilityData { + uint8_t *L0; + uint8_t *L1_a; + uint8_t *L1_b; + uint8_t *L1_c; +} LightProbeVisibilityData; + +/** + * Used to avoid light leaks. Validate visibility between each grid sample. + */ +typedef struct LightProbeConnectivityData { + /** Stores a bitmask of valid connections within a cell. */ + uint8_t *bitmask; +} LightProbeConnectivityData; + +/** + * Defines one block of data inside the grid cache data arrays. + * The block size if the same for all the blocks. + */ +typedef struct LightProbeBlockData { + /* Offset inside the level-of-detail this block starts. */ + int offset[3]; + /* Level-of-detail this block is from. */ + int level; +} LightProbeBlockData; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightProbeGridCacheFrame + * + * \{ */ + +/** + * A frame worth of baked lighting data. + */ +typedef struct LightProbeGridCacheFrame { + /** Number of samples in the highest level of detail. */ + int size[3]; + /** Spatial layout type of the data stored inside the data arrays. */ + int data_layout; + + /** Sparse or adaptive layout only: number of blocks inside data arrays. */ + int block_len; + /** Sparse or adaptive layout only: size of a block in samples. All 3 dimensions are equal. */ + int block_size; + /** Sparse or adaptive layout only: specify the blocks positions. */ + LightProbeBlockData *block_infos; + + /** Baked data. */ + LightProbeBakingData baking; + LightProbeIrradianceData irradiance; + LightProbeVisibilityData visibility; + LightProbeConnectivityData connectivity; +} LightProbeGridCacheFrame; + +/** #LightProbeGridCacheFrame.data_layout (int) */ +enum { + /** Simple uniform grid. Raw output from GPU. Used during the baking process. */ + LIGHTPROBE_CACHE_UNIFORM_GRID = 0, + /** Fills the space with different level of resolution. More efficient storage. */ + LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION = 1, +}; + +/** + * Per object container of baked data. + * Should be called #LightProbeCache but name is already taken. + */ +typedef struct LightProbeObjectCache { + /** Allow correct versioning / different types of data for the same layout. */ + int cache_type; + /** True if this cache references the original object's cache. */ + char shared; + /** True if the cache has been tagged for automatic baking. */ + char dirty; + + char _pad0[2]; + + struct LightProbeGridCacheFrame *grid_static_cache; +} LightProbeObjectCache; + +/** #LightProbeObjectCache.type (int) */ +enum { + /** Light cache was just created and is not yet baked. Keep as 0 for default value. */ + LIGHTPROBE_CACHE_TYPE_NONE = 0, + /** Light cache is baked for one specific frame and capture all indirect lighting. */ + LIGHTPROBE_CACHE_TYPE_STATIC = 1, +}; + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index ad84ab716c0..388e7e806c3 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -34,6 +34,7 @@ struct FluidsimSettings; struct GeometrySet; struct Ipo; struct LightgroupMembership; +struct LightProbeGridCacheFrame; struct Material; struct Mesh; struct Object; @@ -448,6 +449,11 @@ typedef struct Object { /** Lightgroup membership information. */ struct LightgroupMembership *lightgroup; + /** Irradiance caches baked for this object (light-probes only). */ + struct LightProbeObjectCache *lightprobe_cache; + + void *_pad9; + /** Runtime evaluation data (keep last). */ Object_Runtime runtime; } Object; -- 2.30.2 From a5310b77205fd80dc25c7f4e891af4a4d0e44cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 13 Apr 2023 20:12:32 +0200 Subject: [PATCH 31/58] EEVEE-Next: Adapt code to new irradiance cache --- .../blender/blenkernel/intern/lightprobe.cc | 3 + source/blender/blenkernel/intern/scene.cc | 6 +- .../draw/engines/eevee/eevee_lightcache.c | 14 - .../draw/engines/eevee_next/eevee_camera.cc | 4 +- .../draw/engines/eevee_next/eevee_instance.cc | 136 ++----- .../draw/engines/eevee_next/eevee_instance.hh | 12 +- .../eevee_next/eevee_irradiance_cache.cc | 333 +++++++++++------- .../eevee_next/eevee_irradiance_cache.hh | 29 +- .../engines/eevee_next/eevee_lightcache.cc | 272 ++++---------- ...eevee_lightcache.h => eevee_lightcache.hh} | 51 +-- .../engines/eevee_next/eevee_lightprobe.cc | 38 +- .../engines/eevee_next/eevee_lightprobe.hh | 33 +- .../eevee_display_probe_grid_frag.glsl | 12 +- .../eevee_lightprobe_irradiance_ray_comp.glsl | 20 +- .../eevee_spherical_harmonics_lib.glsl | 69 ++-- .../infos/eevee_irradiance_cache_info.hh | 8 +- .../blender/editors/render/render_shading.cc | 66 ++-- .../blender/makesdna/DNA_lightprobe_types.h | 29 +- 18 files changed, 478 insertions(+), 657 deletions(-) rename source/blender/draw/engines/eevee_next/{eevee_lightcache.h => eevee_lightcache.hh} (69%) diff --git a/source/blender/blenkernel/intern/lightprobe.cc b/source/blender/blenkernel/intern/lightprobe.cc index 2b2676b3be6..c1d7a1ed33a 100644 --- a/source/blender/blenkernel/intern/lightprobe.cc +++ b/source/blender/blenkernel/intern/lightprobe.cc @@ -187,6 +187,8 @@ static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader, cache->baking.L1_a = nullptr; cache->baking.L1_b = nullptr; cache->baking.L1_c = nullptr; + cache->surfels = nullptr; + cache->surfels_len = 0; if (cache->irradiance.L0 != nullptr) { BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L0); @@ -257,6 +259,7 @@ void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache) spherical_harmonic_free(cache->irradiance); spherical_harmonic_free(cache->visibility); MEM_SAFE_FREE(cache->connectivity.bitmask); + MEM_SAFE_FREE(cache->surfels); MEM_SAFE_FREE(cache); } diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 3e3225148c5..5d575ccccf8 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -107,7 +107,6 @@ #include "BLO_read_write.h" #include "engines/eevee/eevee_lightcache.h" -#include "engines/eevee_next/eevee_lightcache.h" #include "PIL_time.h" @@ -1444,10 +1443,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) } } - RenderEngineType *engine_type = RE_engines_find(sce->r.engine); - bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); - - (use_eevee_next ? EEVEE_NEXT_lightcache_info_update : EEVEE_lightcache_info_update)(&sce->eevee); + EEVEE_lightcache_info_update(&sce->eevee); BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading); diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 8afa63a484b..83a978ccff6 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -24,7 +24,6 @@ #include "PIL_time.h" -#include "../eevee_next/eevee_lightcache.h" #include "eevee_lightcache.h" #include "eevee_private.h" @@ -502,11 +501,6 @@ static void eevee_lightbake_readback_reflections(LightCache *lcache) void EEVEE_lightcache_free(LightCache *lcache) { - if (lcache->version == LIGHTCACHE_NEXT_STATIC_VERSION) { - EEVEE_NEXT_lightcache_free(lcache); - return; - } - DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex); MEM_SAFE_FREE(lcache->cube_tx.data); DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex); @@ -545,10 +539,6 @@ static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex void EEVEE_lightcache_blend_write(BlendWriter *writer, LightCache *cache) { - if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) { - EEVEE_NEXT_lightcache_blend_write(writer, cache); - } - write_lightcache_texture(writer, &cache->grid_tx); write_lightcache_texture(writer, &cache->cube_tx); @@ -589,10 +579,6 @@ static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTe void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache) { - if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) { - EEVEE_NEXT_lightcache_blend_read_data(reader, cache); - } - cache->flag &= ~LIGHTCACHE_NOT_USABLE; direct_link_lightcache_texture(reader, &cache->cube_tx); direct_link_lightcache_texture(reader, &cache->grid_tx); diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc index e4e5c5920a6..1ee45427fba 100644 --- a/source/blender/draw/engines/eevee_next/eevee_camera.cc +++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc @@ -78,9 +78,7 @@ void Camera::sync() CameraData &data = data_; - /* WORKAROUND(fclem): This is an abomination that should be remove ASAP. */ - bool is_first_baking_sync = inst_.light_probes.grids.size() == 0; - if (inst_.is_baking() && !is_first_baking_sync) { + if (inst_.is_baking()) { /* Any view so that shadows and light culling works during irradiance bake. */ draw::View &view = inst_.irradiance_cache.bake.view_z_; data.viewmat = view.viewmat(); diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index bbebf8ab4cc..43b9d90421d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -21,7 +21,6 @@ #include "eevee_engine.h" #include "eevee_instance.hh" -#include "eevee_lightcache.h" namespace blender::eevee { @@ -333,51 +332,6 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Light Bake - * \{ */ - -LightCache *Instance::light_cache_create(Vector grids, - Vector cubes) -{ - LightCache *light_cache = EEVEE_NEXT_lightcache_create(); - light_cache->flag = LIGHTCACHE_BAKING; - - light_cache->grid_len = grids.size(); - light_cache->grids = MEM_cnew_array(grids.size(), - "LightCacheIrradianceGrid"); - - for (const auto i : grids.index_range()) { - const IrradianceGrid &grid = grids[i]; - size_t sample_count = grid.resolution.x * grid.resolution.y * grid.resolution.z; - size_t grid_texture_sample_size = sizeof(uint16_t) * 4; - if (sample_count * grid_texture_sample_size > INT_MAX) { - /* The size of the texture doesn't fit on a 32bit system. */ - light_cache->flag |= LIGHTCACHE_INVALID; - info = "Scene contains an irradiance grid with too many samples points"; - return nullptr; - } - LightCacheIrradianceGrid &irradiance_grid = light_cache->grids[i]; - float4x4 world_to_grid = math::invert(grid.transform); - copy_m4_m4(irradiance_grid.world_to_grid, world_to_grid.ptr()); - irradiance_grid.resolution[0] = grid.resolution.x; - irradiance_grid.resolution[1] = grid.resolution.y; - irradiance_grid.resolution[2] = grid.resolution.z; - } - - light_cache->cube_len = cubes.size(); - // light_cache->cubes = MEM_cnew_array(cubes.size(), - // "LightCacheIrradianceGrid"); - - // for (const auto i : cubes.index_range()) { - /* TODO */ - // } - - return light_cache; -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Interface * \{ */ @@ -498,12 +452,12 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL); } -void Instance::light_bake_irradiance(LightCache *&r_light_cache, +void Instance::light_bake_irradiance(Object &probe, std::function context_enable, - std::function context_disable) + std::function context_disable, + std::function result_update) { BLI_assert(is_baking()); - BLI_assert(r_light_cache == nullptr); auto custom_pipeline_wrapper = [&](std::function callback) { context_enable(); @@ -519,67 +473,47 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache, context_disable(); }; - /* Count probes. */ - /* TODO(fclem): Ideally, this should only iterate the despgraph and not do a full sync. */ custom_pipeline_wrapper([&]() { + GPU_debug_capture_begin(); + irradiance_cache.bake.surfel_raster_views_sync(probe); + /* TODO: lightprobe visibility group option. */ manager->begin_sync(); render_sync(); manager->end_sync(); + + irradiance_cache.bake.surfels_create(probe); + irradiance_cache.bake.surfels_lights_eval(); + GPU_debug_capture_end(); }); - /* Allocate CPU storage. */ - r_light_cache = this->light_cache_create(light_probes.grids, light_probes.cubes); - if (r_light_cache->flag & LIGHTCACHE_INVALID) { - /* Something happened and the light cache couldn't be created. */ - // stop = true; - // do_update = true; - r_light_cache->flag &= ~LIGHTCACHE_BAKING; - return; - } + int bounce_len = scene->eevee.gi_diffuse_bounces; + /* Start at bounce 1 as 0 bounce is no indirect lighting. */ + for (int bounce = 1; bounce <= bounce_len; bounce++) { + sampling.init(scene); + while (!sampling.finished()) { + context_wrapper([&]() { + /* Batch ray cast by pack of 16 to avoid too much overhead of + * the update function & context switch. */ + for (int i = 0; i < 16 && !sampling.finished(); i++) { + sampling.step(); + irradiance_cache.bake.propagate_light_sample(); + } + if (sampling.finished()) { + irradiance_cache.bake.accumulate_bounce(); + } - for (auto i : light_probes.grids.index_range()) { - custom_pipeline_wrapper([&]() { - GPU_debug_capture_begin(); - irradiance_cache.bake.surfel_raster_views_sync(light_probes.grids[i]); - /* TODO: lightprobe visibility group option. */ - manager->begin_sync(); - render_sync(); - manager->end_sync(); - - irradiance_cache.bake.surfels_create(light_probes.grids[i]); - irradiance_cache.bake.surfels_lights_eval(); - GPU_debug_capture_end(); - }); - - int bounce_len = scene->eevee.gi_diffuse_bounces; - /* Start at bounce 1 as 0 bounce is no indirect lighting. */ - for (int bounce = 1; bounce <= bounce_len; bounce++) { - sampling.init(scene); - while (!sampling.finished()) { - context_wrapper([&]() { - /* Batch ray cast by pack of 16 to avoid too much overhead of - * the update function & context switch. */ - for (int i = 0; i < 16 && !sampling.finished(); i++) { - sampling.step(); - irradiance_cache.bake.propagate_light_sample(); - } - if (sampling.finished()) { - irradiance_cache.bake.accumulate_bounce(); - } - irradiance_cache.bake.read_result(r_light_cache->grids[i]); - }); - // do_update = true; - } - } - - if (bounce_len == 0) { - /* Still read result for debugging surfel direct lighting. */ - context_wrapper([&]() { irradiance_cache.bake.read_result(r_light_cache->grids[i]); }); + LightProbeGridCacheFrame *cache_frame; + if (bounce != bounce_len) { + /* TODO(fclem): Only do this read-back if needed. But it might be tricky to know when. */ + cache_frame = irradiance_cache.bake.read_result_unpacked(); + } + else { + cache_frame = irradiance_cache.bake.read_result_packed(); + } + result_update(cache_frame); + }); } } - - r_light_cache->flag |= LIGHTCACHE_BAKED; - r_light_cache->flag &= ~LIGHTCACHE_BAKING; } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index f94f5624a7c..bcb1ace04f5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -146,9 +146,10 @@ class Instance { /* Light bake. */ void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager); - void light_bake_irradiance(LightCache *&r_light_cache, + void light_bake_irradiance(Object &probe, std::function context_enable, - std::function context_disable); + std::function context_disable, + std::function result_update); static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer); @@ -196,13 +197,6 @@ class Instance { void scene_sync(); void mesh_sync(Object *ob, ObjectHandle &ob_handle); - /** - * Create a light cache big enough to fit all light-probes inside. - * IMPORTANT: Can return nullptr on failure, in which case, the `Instance::info` will be set - * to an error message. - */ - LightCache *light_cache_create(Vector grids, Vector cubes); - void update_eval_members(); void set_time(float time); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index bef3a0070df..f3a4b62b877 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -2,6 +2,8 @@ #include "DNA_lightprobe_types.h" +#include "BKE_lightprobe.h" + #include "GPU_capabilities.h" #include "GPU_debug.h" @@ -17,15 +19,15 @@ namespace blender::eevee { /** \name Interface * \{ */ -void IrradianceCache::init() {} +void IrradianceCache::init() +{ + display_grids_enabled_ = DRW_state_draw_support() && + inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE; +} void IrradianceCache::sync() { - if (!inst_.is_baking()) { - debug_pass_sync(); - display_pass_sync(); - } - else { + if (inst_.is_baking()) { bake.sync(); } } @@ -38,42 +40,6 @@ void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb) } } -void IrradianceCache::debug_pass_sync() -{ - if (!ELEM(inst_.debug_mode, - eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL, - eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) { - return; - } - - LightCache *light_cache = inst_.scene->eevee.light_cache_data; - if (light_cache == nullptr || light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION || - light_cache->grids == nullptr || light_cache->grid_len == 0) { - return; - } - - debug_surfels_ps_.init(); - debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS_EQUAL); - debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); - debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f); - debug_surfels_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); - - surfels_buf_.clear(); - for (auto i : IndexRange(light_cache->grid_len)) { - LightCacheIrradianceGrid &grid = light_cache->grids[i]; - if (grid.surfels_len > 0 && grid.surfels != nullptr) { - Span grid_surfels(static_cast(grid.surfels), grid.surfels_len); - for (const Surfel &surfel : grid_surfels) { - surfels_buf_.append(surfel); - } - } - } - surfels_buf_.push_update(); - debug_surfels_ps_.bind_ssbo("surfels_buf", surfels_buf_); - debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, surfels_buf_.size(), 4); -} - void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) { switch (inst_.debug_mode) { @@ -88,66 +54,105 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) return; } - GPU_framebuffer_bind(view_fb); - inst_.manager->submit(debug_surfels_ps_, view); -} - -void IrradianceCache::display_pass_sync() -{ - LightCache *light_cache = inst_.scene->eevee.light_cache_data; - - display_grids_enabled_ = light_cache && light_cache->grid_len > 0 && light_cache->grids && - DRW_state_draw_support() && - inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE; - if (!display_grids_enabled_) { - return; - } - - display_grids_ps_.init(); - display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK); - display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID)); - - for (auto i : IndexRange(light_cache->grid_len)) { - LightCacheIrradianceGrid &grid = light_cache->grids[i]; - - if (grid.irradiance_L0_L1_a.data == nullptr) { + for (const IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) { + if (grid.cache == nullptr) { continue; } - auto load_texture = [&](const char *name, LightCacheTexture &cache_texture) { - if ((light_cache->flag & LIGHTCACHE_BAKED) && cache_texture.tex != nullptr) { - return; - } - if (light_cache->flag & LIGHTCACHE_BAKING) { - GPU_TEXTURE_FREE_SAFE(cache_texture.tex); - } - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; - cache_texture.tex = GPU_texture_create_3d( - name, UNPACK3(cache_texture.tex_size), 1, GPU_RGBA16F, usage, cache_texture.data); - }; - load_texture("grid.irradiance_L0_L1_a", grid.irradiance_L0_L1_a); - load_texture("grid.irradiance_L0_L1_b", grid.irradiance_L0_L1_b); - load_texture("grid.irradiance_L0_L1_c", grid.irradiance_L0_L1_c); + LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache; - display_grids_ps_.push_constant("sphere_radius", 0.3f); - display_grids_ps_.push_constant("grid_resolution", int3(grid.resolution)); - float4x4 grid_to_world = math::invert(float4x4(grid.world_to_grid)); - display_grids_ps_.push_constant("grid_to_world", grid_to_world); - display_grids_ps_.bind_texture("irradiance_a_tx", grid.irradiance_L0_L1_a.tex); - display_grids_ps_.bind_texture("irradiance_b_tx", grid.irradiance_L0_L1_b.tex); - display_grids_ps_.bind_texture("irradiance_c_tx", grid.irradiance_L0_L1_c.tex); + if (cache->surfels == nullptr || cache->surfels_len == 0) { + continue; + } - int cell_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; - display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, cell_count * 3 * 2); + debug_surfels_ps_.init(); + debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_LESS_EQUAL); + display_grids_ps_.framebuffer_set(&view_fb); + debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS)); + debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f); + debug_surfels_ps_.push_constant("debug_mode", static_cast(inst_.debug_mode)); + + debug_surfels_buf_.resize(cache->surfels_len); + /* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an input + * data. */ + Span grid_surfels(static_cast(cache->surfels), cache->surfels_len); + MutableSpan(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels); + debug_surfels_buf_.push_update(); + + debug_surfels_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_); + debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4); + + inst_.manager->submit(debug_surfels_ps_, view); } } void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb) { - if (display_grids_enabled_) { - GPU_framebuffer_bind(view_fb); + if (!display_grids_enabled_) { + return; + } + + for (const IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) { + if (grid.cache == nullptr) { + continue; + } + + LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache; + + if (cache == nullptr) { + continue; + } + + /* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */ + draw::Texture irradiance_a_tx_ = {"irradiance_a_tx"}; + draw::Texture irradiance_b_tx_ = {"irradiance_b_tx"}; + draw::Texture irradiance_c_tx_ = {"irradiance_c_tx"}; + draw::Texture irradiance_d_tx_ = {"irradiance_d_tx"}; + + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; + int3 grid_size = int3(cache->size); + if (cache->baking.L0) { + irradiance_a_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0); + irradiance_b_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a); + irradiance_c_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b); + irradiance_d_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c); + } + else if (cache->irradiance.L0) { + irradiance_a_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0); + irradiance_b_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a); + irradiance_c_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b); + irradiance_d_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c); + } + else { + continue; + } + + display_grids_ps_.init(); + display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK); + display_grids_ps_.framebuffer_set(&view_fb); + display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID)); + + display_grids_ps_.push_constant("sphere_radius", 0.3f); /* TODO property */ + display_grids_ps_.push_constant("grid_resolution", grid_size); + display_grids_ps_.push_constant("grid_to_world", grid.object_to_world); + + display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx_); + display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx_); + display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx_); + display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx_); + + int sample_count = static_cast(BKE_lightprobe_grid_cache_frame_sample_count(cache)); + int triangle_count = sample_count * 2; + display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, triangle_count * 3); + inst_.manager->submit(display_grids_ps_, view); + + irradiance_a_tx_.free(); + irradiance_b_tx_.free(); + irradiance_c_tx_.free(); + irradiance_d_tx_.free(); } } @@ -215,9 +220,10 @@ void IrradianceBake::sync() pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); pass.bind_ssbo("list_start_buf", &list_start_buf_); pass.bind_ssbo("list_info_buf", &list_info_buf_); - pass.bind_image("irradiance_L0_L1_a_img", &irradiance_L0_L1_a_tx_); - pass.bind_image("irradiance_L0_L1_b_img", &irradiance_L0_L1_b_tx_); - pass.bind_image("irradiance_L0_L1_c_img", &irradiance_L0_L1_c_tx_); + pass.bind_image("irradiance_L0_img", &irradiance_L0_tx_); + pass.bind_image("irradiance_L1_a_img", &irradiance_L1_a_tx_); + pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_); + pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_); pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS); pass.dispatch(&dispatch_per_grid_sample_); } @@ -232,10 +238,10 @@ void IrradianceBake::sync() } } -void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid) +void IrradianceBake::surfel_raster_views_sync(const Object &probe_object) { using namespace blender::math; - const float4x4 transform(grid.transform); + const float4x4 transform(probe_object.object_to_world); float3 scale; math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale); @@ -258,7 +264,7 @@ void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid) sync_view(view_z_, basis_z_); } -void IrradianceBake::surfels_create(const IrradianceGrid &grid) +void IrradianceBake::surfels_create(const Object &probe_object) { /** * We rasterize the scene along the 3 axes. Each generated fragment will write a surface element @@ -267,9 +273,14 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) */ using namespace blender::math; - dispatch_per_grid_sample_ = math::divide_ceil(grid.resolution, int3(IRRADIANCE_GRID_GROUP_SIZE)); - capture_info_buf_.irradiance_grid_size = grid.resolution; - capture_info_buf_.irradiance_grid_local_to_world = grid.transform; + const ::LightProbe *lightprobe = static_cast<::LightProbe *>(probe_object.data); + + int3 grid_resolution = int3(&lightprobe->grid_resolution_x); + float4x4 grid_local_to_world = invert(float4x4(probe_object.world_to_object)); + + dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE)); + capture_info_buf_.irradiance_grid_size = grid_resolution; + capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world; capture_info_buf_.irradiance_accum_solid_angle = 0.0f; /* Divide by twice the sample count because each ray is evaluated in both directions. */ capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) / @@ -280,14 +291,14 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) /* 32bit float is needed here otherwise we loose too much energy from rounding error during the * accumulation when the sample count is above 500. */ - irradiance_L0_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage); - irradiance_L0_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage); - irradiance_L0_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage); - irradiance_L0_L1_a_tx_.clear(float4(0.0f)); - irradiance_L0_L1_b_tx_.clear(float4(0.0f)); - irradiance_L0_L1_c_tx_.clear(float4(0.0f)); - - const float4x4 transform(grid.transform); + irradiance_L0_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage); + irradiance_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage); + irradiance_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage); + irradiance_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage); + irradiance_L0_tx_.clear(float4(0.0f)); + irradiance_L1_a_tx_.clear(float4(0.0f)); + irradiance_L1_b_tx_.clear(float4(0.0f)); + irradiance_L1_c_tx_.clear(float4(0.0f)); /* Extract bounding box. Order is arbitrary as it is not important for our usage. */ const std::array bbox_corners({float3{+1, +1, +1}, @@ -300,7 +311,7 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid) float3{-1, -1, -1}}); grid_bbox_vertices.clear(); for (const float3 &point : bbox_corners) { - grid_bbox_vertices.append(transform_point(transform, point)); + grid_bbox_vertices.append(transform_point(grid_local_to_world, point)); } DRW_stats_group_start("IrradianceBake.SurfelsCount"); @@ -428,35 +439,85 @@ void IrradianceBake::accumulate_bounce() inst_.manager->submit(surfel_light_bounce_ps_); } -void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid) +void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame) { - auto read_texture = [&](LightCacheTexture &cache_texture, draw::Texture &texture) { - MEM_SAFE_FREE(cache_texture.data); - cache_texture.data = (char *)texture.read(GPU_DATA_FLOAT); - copy_v3_v3_int(cache_texture.tex_size, light_cache_grid.resolution); - cache_texture.data_type = LIGHTCACHETEX_FLOAT; - cache_texture.components = 4; - }; - GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); - read_texture(light_cache_grid.irradiance_L0_L1_a, irradiance_L0_L1_a_tx_); - read_texture(light_cache_grid.irradiance_L0_L1_b, irradiance_L0_L1_b_tx_); - read_texture(light_cache_grid.irradiance_L0_L1_c, irradiance_L0_L1_c_tx_); - - switch (inst_.debug_mode) { - case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL: - case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: - GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); - capture_info_buf_.read(); - surfels_buf_.read(); - light_cache_grid.surfels_len = capture_info_buf_.surfel_len; - /* TODO(fclem): This isn't threadsafe. */ - MEM_SAFE_FREE(light_cache_grid.surfels); - light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data()); - break; - default: - /* Nothing to display. */ - return; + if (!ELEM(inst_.debug_mode, + eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL, + eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) { + return; } + + GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); + capture_info_buf_.read(); + surfels_buf_.read(); + + cache_frame->surfels_len = capture_info_buf_.surfel_len; + cache_frame->surfels = MEM_malloc_arrayN(cache_frame->surfels_len, sizeof(Surfel), __func__); + + MutableSpan surfels_dst((Surfel *)cache_frame->surfels, cache_frame->surfels_len); + Span surfels_src(surfels_buf_.data(), cache_frame->surfels_len); + surfels_dst.copy_from(surfels_src); +} + +LightProbeGridCacheFrame *IrradianceBake::read_result_unpacked() +{ + LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create(); + + read_surfels(cache_frame); + + cache_frame->size[0] = irradiance_L0_tx_.width(); + cache_frame->size[1] = irradiance_L0_tx_.height(); + cache_frame->size[2] = irradiance_L0_tx_.depth(); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read(GPU_DATA_FLOAT); + cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read(GPU_DATA_FLOAT); + cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read(GPU_DATA_FLOAT); + cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read(GPU_DATA_FLOAT); + + /* TODO(fclem): Connectivity. */ + // cache_frame->connectivity.bitmask = connectivity_tx_.read(GPU_DATA_FLOAT); + + return cache_frame; +} + +LightProbeGridCacheFrame *IrradianceBake::read_result_packed() +{ + LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create(); + + read_surfels(cache_frame); + + cache_frame->size[0] = irradiance_L0_tx_.width(); + cache_frame->size[1] = irradiance_L0_tx_.height(); + cache_frame->size[2] = irradiance_L0_tx_.depth(); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + /* TODO(fclem): Temp. */ + cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read(GPU_DATA_FLOAT); + cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read(GPU_DATA_FLOAT); + cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read(GPU_DATA_FLOAT); + cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read(GPU_DATA_FLOAT); + + /* TODO(fclem): Create packed format texture and swizzle on gpu. */ + // cache_frame->irradiance.L0 = (float(*)[4])irradiance_only_L0_tx_.read(GPU_DATA_FLOAT); + // cache_frame->irradiance.L1_a = + // (float(*)[4])irradiance_only_L1_a_tx_.read(GPU_DATA_FLOAT); + // cache_frame->irradiance.L1_b = + // (float(*)[4])irradiance_only_L1_b_tx_.read(GPU_DATA_FLOAT); + // cache_frame->irradiance.L1_c = + // (float(*)[4])irradiance_only_L1_c_tx_.read(GPU_DATA_FLOAT); + + // cache_frame->visibility.L0 = irradiance_only_L0_tx_.read(GPU_DATA_UBYTE); + // cache_frame->visibility.L1_a = irradiance_only_L1_a_tx_.read(GPU_DATA_UBYTE); + // cache_frame->visibility.L1_b = irradiance_only_L1_b_tx_.read(GPU_DATA_UBYTE); + // cache_frame->visibility.L1_c = irradiance_only_L1_c_tx_.read(GPU_DATA_UBYTE); + + /* TODO(fclem): Connectivity. */ + // cache_frame->connectivity.bitmask = connectivity_tx_.read(GPU_DATA_FLOAT); + + return cache_frame; } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 7e2fb57e75d..c0563cccb14 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -77,9 +77,10 @@ class IrradianceBake { int3 dispatch_per_grid_sample_ = int3(1); /** Irradiance textures for baking. Only represents one grid in there. */ - Texture irradiance_L0_L1_a_tx_ = {"irradiance_L0_L1_a_tx_"}; - Texture irradiance_L0_L1_b_tx_ = {"irradiance_L0_L1_b_tx_"}; - Texture irradiance_L0_L1_c_tx_ = {"irradiance_L0_L1_c_tx_"}; + Texture irradiance_L0_tx_ = {"irradiance_L0_tx_"}; + Texture irradiance_L1_a_tx_ = {"irradiance_L1_a_tx_"}; + Texture irradiance_L1_b_tx_ = {"irradiance_L1_b_tx_"}; + Texture irradiance_L1_c_tx_ = {"irradiance_L1_c_tx_"}; /* Orientation of the irradiance grid being baked. */ math::Quaternion grid_orientation_; @@ -96,9 +97,9 @@ class IrradianceBake { void sync(); /** Create the views used to rasterize the scene into surfel representation. */ - void surfel_raster_views_sync(const IrradianceGrid &grid); - /** Create a surfel representation of the scene from the \a grid using the capture pipeline. */ - void surfels_create(const IrradianceGrid &grid); + void surfel_raster_views_sync(const Object &probe_object); + /** Create a surfel representation of the scene from the probe using the capture pipeline. */ + void surfels_create(const Object &probe_object); /** Evaluate direct lighting (and also clear the surfels radiance). */ void surfels_lights_eval(); /** Propagate light from surfel to surfel in a random direction over the sphere. */ @@ -106,8 +107,14 @@ class IrradianceBake { /** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */ void accumulate_bounce(); - /** Read grid final irradiance back to CPU into \a light_cache_grid . */ - void read_result(LightCacheIrradianceGrid &light_cache_grid); + /** Read grid unpacked irradiance back to CPU and returns as a #LightProbeGridCacheFrame. */ + LightProbeGridCacheFrame *read_result_unpacked(); + /** Read grid packed irradiance back to CPU and returns as a #LightProbeGridCacheFrame. */ + LightProbeGridCacheFrame *read_result_packed(); + + private: + /** Read surfel data back to CPU into \a cache_frame . */ + void read_surfels(LightProbeGridCacheFrame *cache_frame); }; /** @@ -121,10 +128,12 @@ class IrradianceCache { private: Instance &inst_; + /** Display surfel debug data. */ PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"}; /** Debug surfel elements copied from the light cache. */ - draw::StorageVectorBuffer surfels_buf_; + draw::StorageArrayBuffer debug_surfels_buf_; + /** Display grid cache data. */ bool display_grids_enabled_ = false; PassSimple display_grids_ps_ = {"IrradianceCache.Display Grids"}; @@ -137,9 +146,7 @@ class IrradianceCache { void viewport_draw(View &view, GPUFrameBuffer *view_fb); private: - void debug_pass_sync(); void debug_pass_draw(View &view, GPUFrameBuffer *view_fb); - void display_pass_sync(); void display_pass_draw(View &view, GPUFrameBuffer *view_fb); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index 90a4a75493b..388d0e0eb38 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -9,8 +9,10 @@ #include "DRW_render.h" #include "BKE_global.h" +#include "BKE_lightprobe.h" + +#include "DNA_lightprobe_types.h" -#include "BLI_endian_switch.h" #include "BLI_threads.h" #include "DEG_depsgraph_build.h" @@ -24,14 +26,12 @@ #include "WM_api.h" #include "WM_types.h" -#include "BLO_read_write.h" - #include "wm_window.h" #include "eevee_engine.h" #include "eevee_instance.hh" -#include "eevee_lightcache.h" +#include "eevee_lightcache.hh" /* -------------------------------------------------------------------- */ /** \name Light Probe Baking @@ -64,18 +64,29 @@ class LightBake { /** Manager used for command submission. Created and freed in the worker thread. */ draw::Manager *manager_ = nullptr; + /** Lightprobe original objects to bake. */ + Vector original_probes_; + /** Frame to copy to original objects during update. This is needed to avoid race conditions. */ + Vector bake_result_; + std::mutex result_mutex_; + public: LightBake(struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, + Span probes, bool run_as_job, int frame, int delay_ms = 0) : depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)), frame_(frame), - delay_ms_(delay_ms) + delay_ms_(delay_ms), + original_probes_(probes) { BLI_assert(BLI_thread_is_main()); + bake_result_.resize(probes.size()); + bake_result_.fill(nullptr); + if (run_as_job && !GPU_use_main_context_workaround()) { /* This needs to happen in main thread. */ gl_context_ = WM_opengl_context_create(); @@ -98,20 +109,28 @@ class LightBake { void update() { BLI_assert(BLI_thread_is_main()); - Scene *original_scene = DEG_get_input_scene(depsgraph_); - LightCache *&scene_light_cache = original_scene->eevee.light_cache_data; - if (scene_light_cache != light_cache_) { - if (scene_light_cache != nullptr) { - /* Delete old data if existing. */ - EEVEE_NEXT_lightcache_free(scene_light_cache); + for (auto i : bake_result_.index_range()) { + if (bake_result_[i] == nullptr) { + continue; } - scene_light_cache = light_cache_; + Object *orig_ob = original_probes_[i]; + + { + std::scoped_lock lock(result_mutex_); + + LightProbeObjectCache *cache = orig_ob->lightprobe_cache; + /* Delete any existing cache. */ + if (cache->grid_static_cache != nullptr) { + BKE_lightprobe_grid_cache_frame_free(cache->grid_static_cache); + } + /* Pass ownership to original object. */ + cache->grid_static_cache = bake_result_[i]; + bake_result_[i] = nullptr; + } + /* Propagate the cache to evaluated object. */ + DEG_id_tag_update(&orig_ob->id, ID_RECALC_COPY_ON_WRITE); } - - EEVEE_NEXT_lightcache_info_update(&original_scene->eevee); - - DEG_id_tag_update(&original_scene->id, ID_RECALC_COPY_ON_WRITE); } /** @@ -124,18 +143,36 @@ class LightBake { DEG_graph_relations_update(depsgraph_); DEG_evaluate_on_framechange(depsgraph_, frame_); + if (delay_ms_ > 0) { + PIL_sleep_ms(delay_ms_); + } + context_enable(); manager_ = new draw::Manager(); instance_ = new eevee::Instance(); instance_->init_light_bake(depsgraph_, manager_); context_disable(); - if (delay_ms_ > 0) { - PIL_sleep_ms(delay_ms_); - } + for (auto i : original_probes_.index_range()) { + Object *eval_ob = DEG_get_evaluated_object(depsgraph_, original_probes_[i]); - instance_->light_bake_irradiance( - light_cache_, [this]() { context_enable(); }, [this]() { context_disable(); }); + instance_->light_bake_irradiance( + *eval_ob, + [this]() { context_enable(); }, + [this]() { context_disable(); }, + [&](LightProbeGridCacheFrame *cache_frame) { + { + std::scoped_lock lock(result_mutex_); + /* Delete any existing cache that wasn't transferred to the original object. */ + if (bake_result_[i] != nullptr) { + BKE_lightprobe_grid_cache_frame_free(bake_result_[i]); + } + bake_result_[i] = cache_frame; + } + *do_update = true; + /* TODO: Update progress. */ + }); + } delete_resources(); } @@ -226,8 +263,6 @@ class LightBake { } // namespace blender::eevee -extern "C" { - using namespace blender::eevee; /* -------------------------------------------------------------------- */ @@ -239,6 +274,7 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, + blender::Vector original_probes, int delay_ms, int frame) { @@ -257,7 +293,8 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, WM_JOB_TYPE_LIGHT_BAKE); - LightBake *bake = new LightBake(bmain, view_layer, scene, true, frame, delay_ms); + LightBake *bake = new LightBake( + bmain, view_layer, scene, original_probes, true, frame, delay_ms); WM_jobs_customdata_set(wm_job, bake, EEVEE_NEXT_lightbake_job_data_free); WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0); @@ -275,12 +312,10 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, - bool run_as_job, + blender::Vector original_probes, int frame) { - /* This should only be used for exec job. Eventually, remove `run_as_job` parameter later. */ - BLI_assert(run_as_job == false); - LightBake *bake = new LightBake(bmain, view_layer, scene, run_as_job, frame); + LightBake *bake = new LightBake(bmain, view_layer, scene, original_probes, false, frame); /* TODO(fclem): Can remove this cast once we remove the previous EEVEE light cache. */ return reinterpret_cast(bake); } @@ -301,184 +336,3 @@ void EEVEE_NEXT_lightbake_job(void *job_data, bool *stop, bool *do_update, float } /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Light Cache Create / Delete - * \{ */ - -LightCache *EEVEE_NEXT_lightcache_create() -{ - LightCache *light_cache = (LightCache *)MEM_callocN(sizeof(LightCache), "LightCache"); - - light_cache->version = LIGHTCACHE_NEXT_STATIC_VERSION; - light_cache->type = LIGHTCACHE_TYPE_STATIC; - - return light_cache; -} - -void EEVEE_NEXT_lightcache_free(LightCache *light_cache) -{ - if (light_cache->version < LIGHTCACHE_NEXT_STATIC_VERSION) { - /* Allow deleting old EEVEE cache. */ - DRW_TEXTURE_FREE_SAFE(light_cache->cube_tx.tex); - MEM_SAFE_FREE(light_cache->cube_tx.data); - DRW_TEXTURE_FREE_SAFE(light_cache->grid_tx.tex); - MEM_SAFE_FREE(light_cache->grid_tx.data); - if (light_cache->cube_mips) { - for (int i = 0; i < light_cache->mips_len; i++) { - MEM_SAFE_FREE(light_cache->cube_mips[i].data); - } - MEM_SAFE_FREE(light_cache->cube_mips); - } - MEM_SAFE_FREE(light_cache->cube_data); - MEM_SAFE_FREE(light_cache->grid_data); - } - else { - for (int i = 0; i < light_cache->grid_len; i++) { - MEM_SAFE_FREE(light_cache->grids[i].surfels); - MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_a.data); - MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_b.data); - MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_c.data); - DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_a.tex); - DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_b.tex); - DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_c.tex); - } - MEM_SAFE_FREE(light_cache->grids); - } - MEM_freeN(light_cache); -} - -void EEVEE_NEXT_lightcache_info_update(SceneEEVEE *eevee) -{ - LightCache *light_cache = eevee->light_cache_data; - - if (light_cache == nullptr) { - BLI_strncpy(eevee->light_cache_info, - TIP_("No light cache in this scene"), - sizeof(eevee->light_cache_info)); - return; - } - - if (light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION) { - BLI_strncpy(eevee->light_cache_info, - TIP_("Incompatible Light cache version, please bake again"), - sizeof(eevee->light_cache_info)); - return; - } - - if (light_cache->flag & LIGHTCACHE_INVALID) { - BLI_strncpy(eevee->light_cache_info, - TIP_("Error: Light cache dimensions not supported by the GPU"), - sizeof(eevee->light_cache_info)); - return; - } - - if (light_cache->flag & LIGHTCACHE_BAKING) { - BLI_strncpy( - eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info)); - return; - } - - int irradiance_sample_len = 0; - for (const LightCacheIrradianceGrid &grid : - blender::Span(light_cache->grids, light_cache->grid_len)) { - irradiance_sample_len += grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; - } - - size_t size_in_bytes = irradiance_sample_len * sizeof(uint16_t) * 4 * 3; - - char formatted_mem[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE]; - BLI_str_format_byte_unit(formatted_mem, size_in_bytes, false); - - BLI_snprintf(eevee->light_cache_info, - sizeof(eevee->light_cache_info), - TIP_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"), - light_cache->cube_len, - irradiance_sample_len, - formatted_mem); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Light Cache Read/Write to file - * \{ */ - -void EEVEE_NEXT_lightcache_blend_write(BlendWriter *writer, LightCache *light_cache) -{ - auto write_lightcache_texture = [&](LightCacheTexture &tex) { - if (tex.data) { - size_t data_size = tex.components * tex.tex_size[0] * tex.tex_size[1] * tex.tex_size[2]; - if (tex.data_type == LIGHTCACHETEX_FLOAT) { - data_size *= sizeof(float); - } - else if (tex.data_type == LIGHTCACHETEX_UINT) { - data_size *= sizeof(uint); - } - - /* FIXME: We can't save more than what 32bit systems can handle. - * The solution would be to split the texture but it is too late for 2.90. (see #78529) */ - if (data_size < INT_MAX) { - BLO_write_raw(writer, data_size, tex.data); - } - } - }; - - BLO_write_struct_array( - writer, LightCacheIrradianceGrid, light_cache->grid_len, light_cache->grids); - - for (int i = 0; i < light_cache->grid_len; i++) { - LightCacheIrradianceGrid &grid = light_cache->grids[i]; - /* Surfels are runtime data. Not stored in the blend file. */ - write_lightcache_texture(grid.irradiance_L0_L1_a); - write_lightcache_texture(grid.irradiance_L0_L1_b); - write_lightcache_texture(grid.irradiance_L0_L1_c); - } -} - -void EEVEE_NEXT_lightcache_blend_read_data(BlendDataReader *reader, LightCache *light_cache) -{ - - if (light_cache->grids) { - BLO_read_data_address(reader, &light_cache->grids); - - auto direct_link_lightcache_texture = [&](LightCacheTexture &lctex) { - /* Runtime data. Not stored in the blend file. */ - lctex.tex = nullptr; - - if (lctex.data) { - BLO_read_data_address(reader, &lctex.data); - if (lctex.data && BLO_read_requires_endian_switch(reader)) { - int data_size = lctex.components * lctex.tex_size[0] * lctex.tex_size[1] * - lctex.tex_size[2]; - - if (lctex.data_type == LIGHTCACHETEX_FLOAT) { - BLI_endian_switch_float_array((float *)lctex.data, data_size * sizeof(float)); - } - else if (lctex.data_type == LIGHTCACHETEX_UINT) { - BLI_endian_switch_uint32_array((uint *)lctex.data, data_size * sizeof(uint)); - } - } - } - - if (lctex.data == nullptr) { - zero_v3_int(lctex.tex_size); - } - }; - - for (int i = 0; i < light_cache->grid_len; i++) { - LightCacheIrradianceGrid &grid = light_cache->grids[i]; - /* Runtime data. Not stored in the blend file. */ - grid.surfels_len = 0; - grid.surfels = nullptr; - direct_link_lightcache_texture(grid.irradiance_L0_L1_a); - direct_link_lightcache_texture(grid.irradiance_L0_L1_b); - direct_link_lightcache_texture(grid.irradiance_L0_L1_c); - } - } -} - -/** \} */ -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.h b/source/blender/draw/engines/eevee_next/eevee_lightcache.hh similarity index 69% rename from source/blender/draw/engines/eevee_next/eevee_lightcache.h rename to source/blender/draw/engines/eevee_next/eevee_lightcache.hh index c96b944e292..144177ab619 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.h +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.hh @@ -6,9 +6,15 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include "BLI_vector.hh" + +struct wmWindowManager; +struct wmWindow; +struct Main; +struct ViewLayer; +struct Scene; +struct Object; +struct wmJob; /** Opaque type hiding eevee::LightBake. */ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake; @@ -21,7 +27,6 @@ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake; * Create the job description. * This is called for async (modal) bake operator. * The actual work will be done by `EEVEE_NEXT_lightbake_job()`. - * Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()`. * IMPORTANT: Must run on the main thread because of potential GPUContext creation. */ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, @@ -29,6 +34,7 @@ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, + blender::Vector original_probes, int delay_ms, int frame); @@ -43,7 +49,7 @@ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm, void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, - bool run_as_job, + blender::Vector original_probes, int frame); /** @@ -69,38 +75,3 @@ void EEVEE_NEXT_lightbake_job(void *job_data /* EEVEE_NEXT_LightBake */, float *progress); /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Light Cache Create / Delete - * \{ */ - -/** - * Create an empty light-cache. - */ -struct LightCache *EEVEE_NEXT_lightcache_create(void); - -/** - * Free a light-cache and its associated data. - */ -void EEVEE_NEXT_lightcache_free(struct LightCache *lcache); - -/** - * Update the UI message in the render panel about the state of the cache. - */ -void EEVEE_NEXT_lightcache_info_update(struct SceneEEVEE *eevee); - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Light Cache Read/Write to file - * \{ */ - -void EEVEE_NEXT_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *light_cache); -void EEVEE_NEXT_lightcache_blend_read_data(struct BlendDataReader *reader, - struct LightCache *light_cache); - -/** \} */ - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 3d6ad935162..1da2d23a2a8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -19,40 +19,28 @@ void LightProbeModule::begin_sync() { auto_bake_enabled_ = inst_.is_viewport() && (inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0; - grid_update_ = false; - cube_update_ = false; - - grids.clear(); - cubes.clear(); } void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle) { - LightProbe &grid = grid_map_.lookup_or_add_default(handle.object_key); + IrradianceGrid &grid = grid_map_.lookup_or_add_default(handle.object_key); grid.used = true; if (handle.recalc != 0 || grid.initialized == false) { grid.initialized = true; - grid_update_ = true; - } - - if (inst_.is_baking()) { - const ::LightProbe *light_probe = (const ::LightProbe *)ob->data; - grids.append({float4x4(ob->object_to_world), &light_probe->grid_resolution_x}); + grid.updated = true; + grid.object_to_world = float4x4(ob->object_to_world); + grid.cache = ob->lightprobe_cache; } } void LightProbeModule::sync_cube(ObjectHandle &handle) { - LightProbe &cube = cube_map_.lookup_or_add_default(handle.object_key); + ReflectionCube &cube = cube_map_.lookup_or_add_default(handle.object_key); cube.used = true; if (handle.recalc != 0 || cube.initialized == false) { cube.initialized = true; cube_update_ = true; } - - if (inst_.is_baking()) { - cubes.append({}); - } } void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle) @@ -75,10 +63,14 @@ void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle) void LightProbeModule::end_sync() { { - /* Check for deleted grid. */ + /* Check for deleted or updated grid. */ + grid_update_ = false; auto it_end = grid_map_.items().end(); for (auto it = grid_map_.items().begin(); it != it_end; ++it) { - LightProbe &grid = (*it).value; + IrradianceGrid &grid = (*it).value; + if (grid.updated) { + grid_update_ = true; + } if (!grid.used) { grid_map_.remove(it); grid_update_ = true; @@ -89,10 +81,14 @@ void LightProbeModule::end_sync() } } { - /* Check for deleted cube. */ + /* Check for deleted or updated cube. */ + cube_update_ = false; auto it_end = cube_map_.items().end(); for (auto it = cube_map_.items().begin(); it != it_end; ++it) { - LightProbe &cube = (*it).value; + ReflectionCube &cube = (*it).value; + if (cube.updated) { + cube_update_ = true; + } if (!cube.used) { cube_map_.remove(it); cube_update_ = true; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index cfbae98f908..db08f78821d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -16,30 +16,37 @@ namespace blender::eevee { class Instance; - -struct IrradianceGrid { - float4x4 transform; - int3 resolution; -}; - -struct ReflectionCube {}; +class IrradianceCache; struct LightProbe { bool used = false; bool initialized = false; + bool updated = false; }; +struct IrradianceGrid : public LightProbe { + /** Reference to the light-cache data. Should be refreshed every sync. */ + float4x4 object_to_world; + /** + * Reference to the light-cache data. + * Do not try to dereference it before LightProbeModule::end_sync() as the grid could + * already have been freed (along with its cache). It is only safe to dereference after the + * pruning have been done. + */ + const struct LightProbeObjectCache *cache = nullptr; +}; + +struct ReflectionCube : public LightProbe {}; + class LightProbeModule { - public: - /** Synced probe data. Only valid if the `eevee::Instance` is a baking instance. */ - Vector grids; - Vector cubes; + friend class IrradianceCache; private: Instance &inst_; - /** Light Probe map to detect deletion. */ - Map grid_map_, cube_map_; + /** Light Probe map to detect deletion and store associated data. */ + Map grid_map_; + Map cube_map_; /** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */ bool grid_update_; /** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl index d462839d20d..d0e2e696f17 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl @@ -11,13 +11,15 @@ void main() return; } - SphericalHarmonicL1 sh = spherical_harmonics_unpack(texelFetch(irradiance_a_tx, cell, 0), - texelFetch(irradiance_b_tx, cell, 0), - texelFetch(irradiance_c_tx, cell, 0)); + SphericalHarmonicL1 sh; + sh.L0.M0 = texelFetch(irradiance_a_tx, cell, 0); + sh.L1.Mn1 = texelFetch(irradiance_b_tx, cell, 0); + sh.L1.M0 = texelFetch(irradiance_c_tx, cell, 0); + sh.L1.Mp1 = texelFetch(irradiance_d_tx, cell, 0); vec3 vN = vec3(lP, sqrt(max(0.0, 1.0 - dist_sqr))); vec3 N = normal_view_to_world(vN); - vec3 irradiance = spherical_harmonics_evaluate_lambert(N, sh); - out_color = vec4(irradiance, 0.0); + vec4 irradiance = spherical_harmonics_evaluate_lambert(N, sh); + out_color = vec4(irradiance.rgb, 0.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl index a169593f489..fbd3f063bff 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -17,7 +17,7 @@ void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh) { spherical_harmonics_encode_signal_sample( - L, irradiance * capture_info_buf.irradiance_sample_solid_angle, sh); + L, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh); } void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh) @@ -59,10 +59,11 @@ void main() vec3 sky_L = cameraVec(P); - SphericalHarmonicL1 sh = spherical_harmonics_unpack( - imageLoad(irradiance_L0_L1_a_img, grid_coord), - imageLoad(irradiance_L0_L1_b_img, grid_coord), - imageLoad(irradiance_L0_L1_c_img, grid_coord)); + SphericalHarmonicL1 sh; + sh.L0.M0 = imageLoad(irradiance_L0_img, grid_coord); + sh.L1.Mn1 = imageLoad(irradiance_L1_a_img, grid_coord); + sh.L1.M0 = imageLoad(irradiance_L1_b_img, grid_coord); + sh.L1.Mp1 = imageLoad(irradiance_L1_c_img, grid_coord); if (surfel_next > -1) { irradiance_capture(surfel_buf[surfel_next], P, sh); @@ -80,9 +81,8 @@ void main() irradiance_capture(-sky_L, vec3(0.0), sh); } - vec4 sh_a, sh_b, sh_c; - spherical_harmonics_pack(sh, sh_a, sh_b, sh_c); - imageStore(irradiance_L0_L1_a_img, grid_coord, sh_a); - imageStore(irradiance_L0_L1_b_img, grid_coord, sh_b); - imageStore(irradiance_L0_L1_c_img, grid_coord, sh_c); + imageStore(irradiance_L0_img, grid_coord, sh.L0.M0); + imageStore(irradiance_L1_a_img, grid_coord, sh.L1.Mn1); + imageStore(irradiance_L1_b_img, grid_coord, sh.L1.M0); + imageStore(irradiance_L1_c_img, grid_coord, sh.L1.Mp1); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index a935f168217..c3706f1654a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -64,21 +64,21 @@ float spherical_harmonics_L2_Mp2(vec3 v) * \{ */ struct SphericalHarmonicBandL0 { - vec3 M0; + vec4 M0; }; struct SphericalHarmonicBandL1 { - vec3 Mn1; - vec3 M0; - vec3 Mp1; + vec4 Mn1; + vec4 M0; + vec4 Mp1; }; struct SphericalHarmonicBandL2 { - vec3 Mn2; - vec3 Mn1; - vec3 M0; - vec3 Mp1; - vec3 Mp2; + vec4 Mn2; + vec4 Mn1; + vec4 M0; + vec4 Mp1; + vec4 Mp2; }; struct SphericalHarmonicL0 { @@ -106,14 +106,14 @@ struct SphericalHarmonicL2 { * \{ */ void spherical_harmonics_L0_encode_signal_sample(vec3 direction, - vec3 amplitude, + vec4 amplitude, inout SphericalHarmonicBandL0 r_L0) { r_L0.M0 += spherical_harmonics_L0_M0(direction) * amplitude; } void spherical_harmonics_L1_encode_signal_sample(vec3 direction, - vec3 amplitude, + vec4 amplitude, inout SphericalHarmonicBandL1 r_L1) { r_L1.Mn1 += spherical_harmonics_L1_Mn1(direction) * amplitude; @@ -122,7 +122,7 @@ void spherical_harmonics_L1_encode_signal_sample(vec3 direction, } void spherical_harmonics_L2_encode_signal_sample(vec3 direction, - vec3 amplitude, + vec4 amplitude, inout SphericalHarmonicBandL2 r_L2) { r_L2.Mn2 += spherical_harmonics_L2_Mn2(direction) * amplitude; @@ -133,14 +133,14 @@ void spherical_harmonics_L2_encode_signal_sample(vec3 direction, } void spherical_harmonics_encode_signal_sample(vec3 direction, - vec3 amplitude, + vec4 amplitude, inout SphericalHarmonicL0 sh) { spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0); } void spherical_harmonics_encode_signal_sample(vec3 direction, - vec3 amplitude, + vec4 amplitude, inout SphericalHarmonicL1 sh) { spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0); @@ -148,7 +148,7 @@ void spherical_harmonics_encode_signal_sample(vec3 direction, } void spherical_harmonics_encode_signal_sample(vec3 direction, - vec3 amplitude, + vec4 amplitude, inout SphericalHarmonicL2 sh) { spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0); @@ -164,19 +164,19 @@ void spherical_harmonics_encode_signal_sample(vec3 direction, * Evaluate an encoded signal in a given unit vector direction. * \{ */ -vec3 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0) +vec4 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0) { return spherical_harmonics_L0_M0(direction) * L0.M0; } -vec3 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1) +vec4 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1) { return spherical_harmonics_L1_Mn1(direction) * L1.Mn1 + spherical_harmonics_L1_M0(direction) * L1.M0 + spherical_harmonics_L1_Mp1(direction) * L1.Mp1; } -vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2) +vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2) { return spherical_harmonics_L2_Mn2(direction) * L2.Mn2 + spherical_harmonics_L2_Mn1(direction) * L2.Mn1 + @@ -197,16 +197,16 @@ vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2) * 2/3 and 1/4. See this reference for more explanation: * https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */ -vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh) +vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh) { return spherical_harmonics_L0_evaluate(N, sh.L0); } -vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh) +vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh) { return spherical_harmonics_L0_evaluate(N, sh.L0) + spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0); } -vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh) +vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh) { return spherical_harmonics_L0_evaluate(N, sh.L0) + spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) + @@ -221,27 +221,36 @@ vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh) * This section define the compression scheme of spherical harmonic data. * \{ */ -SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a, vec4 L0_L1_b, vec4 L0_L1_c) +SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a, + vec4 L0_L1_b, + vec4 L0_L1_c, + vec4 L0_L1_vis) { SphericalHarmonicL1 sh; - sh.L0.M0 = L0_L1_a.xyz; - sh.L1.Mn1 = L0_L1_b.xyz; - sh.L1.M0 = L0_L1_c.xyz; - sh.L1.Mp1 = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w); + sh.L0.M0.xyz = L0_L1_a.xyz; + sh.L1.Mn1.xyz = L0_L1_b.xyz; + sh.L1.M0.xyz = L0_L1_c.xyz; + sh.L1.Mp1.xyz = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w); + sh.L0.M0.w = L0_L1_vis.x; + sh.L1.Mn1.w = L0_L1_vis.y; + sh.L1.M0.w = L0_L1_vis.z; + sh.L1.Mp1.w = L0_L1_vis.w; return sh; } void spherical_harmonics_pack(SphericalHarmonicL1 sh, out vec4 L0_L1_a, out vec4 L0_L1_b, - out vec4 L0_L1_c) + out vec4 L0_L1_c, + out vec4 L0_L1_vis) { - L0_L1_a.xyz = sh.L0.M0; - L0_L1_b.xyz = sh.L1.Mn1; - L0_L1_c.xyz = sh.L1.M0; + L0_L1_a.xyz = sh.L0.M0.xyz; + L0_L1_b.xyz = sh.L1.Mn1.xyz; + L0_L1_c.xyz = sh.L1.M0.xyz; L0_L1_a.w = sh.L1.Mp1.x; L0_L1_b.w = sh.L1.Mp1.y; L0_L1_c.w = sh.L1.Mp1.z; + L0_L1_vis = vec4(sh.L0.M0.w, sh.L1.Mn1.w, sh.L1.M0.w, sh.L1.Mp1.w); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 51a245f65d2..5239ff904e4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -77,6 +77,7 @@ GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) .sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx") .sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx") .sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx") + .sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx") .do_static_compilation(true); GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) @@ -86,8 +87,9 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") .storage_buf(0, Qualifier::READ, "int", "list_start_buf[]") .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") - .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_a_img") - .image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_b_img") - .image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_c_img") + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_img") + .image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_a_img") + .image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_b_img") + .image(3, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_c_img") .compute_source("eevee_lightprobe_irradiance_ray_comp.glsl") .do_static_compilation(true); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index 4262453b43a..31506444b8b 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -85,7 +85,7 @@ #include "RE_pipeline.h" #include "engines/eevee/eevee_lightcache.h" -#include "engines/eevee_next/eevee_lightcache.h" +#include "engines/eevee_next/eevee_lightcache.hh" #include "render_intern.hh" /* own include */ @@ -1401,23 +1401,17 @@ static int light_cache_bake_exec(bContext *C, wmOperator *op) G.is_break = false; - RenderEngineType *engine_type = RE_engines_find(scene->r.engine); - bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); - /* TODO: abort if selected engine is not eevee. */ - void *rj = ((use_eevee_next) ? - EEVEE_NEXT_lightbake_job_data_alloc : - EEVEE_lightbake_job_data_alloc)(bmain, view_layer, scene, false, scene->r.cfra); + void *rj = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false, scene->r.cfra); light_cache_bake_tag_cache(scene, op); bool stop = false, do_update; float progress; /* Not actually used. */ /* Do the job. */ - ((use_eevee_next) ? EEVEE_NEXT_lightbake_job : - EEVEE_lightbake_job)(rj, &stop, &do_update, &progress); + EEVEE_lightbake_job(rj, &stop, &do_update, &progress); /* Free baking data. Result is already stored in the scene data. */ - ((use_eevee_next) ? EEVEE_NEXT_lightbake_job_data_free : EEVEE_lightbake_job_data_free)(rj); + EEVEE_lightbake_job_data_free(rj); /* No redraw needed, we leave state as we entered it. */ ED_update_for_newframe(bmain, CTX_data_depsgraph_pointer(C)); @@ -1436,11 +1430,7 @@ static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * Scene *scene = CTX_data_scene(C); int delay = RNA_int_get(op->ptr, "delay"); - RenderEngineType *engine_type = RE_engines_find(scene->r.engine); - bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); - - wmJob *wm_job = ((use_eevee_next) ? EEVEE_NEXT_lightbake_job_create : - EEVEE_lightbake_job_create)( + wmJob *wm_job = EEVEE_lightbake_job_create( wm, win, bmain, view_layer, scene, delay, scene->r.cfra); if (!wm_job) { @@ -1514,7 +1504,7 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot) /* NOTE: New version destined to replace the old lightcache bake operator. */ -static void lightprobe_cache_bake_start(bContext *C, wmOperator *op) +static blender::Vector lightprobe_cache_bake_start(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); Scene *scene = CTX_data_scene(C); @@ -1524,10 +1514,13 @@ static void lightprobe_cache_bake_start(bContext *C, wmOperator *op) static_cast(ob->data)->type == LIGHTPROBE_TYPE_GRID; }; - auto irradiance_volume_setup = [](Object *ob) { + blender::Vector probes; + + auto irradiance_volume_setup = [&](Object *ob) { BKE_lightprobe_cache_free(ob); BKE_lightprobe_cache_create(ob); DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + probes.append(ob); }; int subset = RNA_enum_get(op->ptr, "subset"); @@ -1576,21 +1569,37 @@ static void lightprobe_cache_bake_start(bContext *C, wmOperator *op) BLI_assert_unreachable(); break; } + + return probes; } static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + int delay = RNA_int_get(op->ptr, "delay"); - lightprobe_cache_bake_start(C, op); + blender::Vector probes = lightprobe_cache_bake_start(C, op); + + if (probes.is_empty()) { + return OPERATOR_CANCELLED; + } + + wmJob *wm_job = EEVEE_NEXT_lightbake_job_create( + wm, win, bmain, view_layer, scene, probes, scene->r.cfra, delay); WM_event_add_modal_handler(C, op); - /* store actual owner of job, so modal operator could check for it, + /* Store actual owner of job, so modal operator could check for it, * the reason of this is that active scene could change when rendering * several layers from compositor #31800. */ op->customdata = scene; + WM_jobs_start(wm, wm_job); + WM_cursor_wait(false); return OPERATOR_RUNNING_MODAL; @@ -1625,7 +1634,18 @@ static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op) /* Executes blocking bake. */ static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op) { - lightprobe_cache_bake_start(C, op); + ViewLayer *view_layer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + blender::Vector probes = lightprobe_cache_bake_start(C, op); + + /* TODO: abort if selected engine is not eevee. */ + void *rj = EEVEE_NEXT_lightbake_job_data_alloc(bmain, view_layer, scene, probes, scene->r.cfra); + /* Do the job. */ + EEVEE_NEXT_lightbake_job(rj, nullptr, nullptr, nullptr); + /* Free baking data. Result is already stored in the scene data. */ + EEVEE_NEXT_lightbake_job_data_free(rj); return OPERATOR_FINISHED; } @@ -1703,11 +1723,7 @@ static int light_cache_free_exec(bContext *C, wmOperator * /*op*/) EEVEE_lightcache_free(scene->eevee.light_cache_data); scene->eevee.light_cache_data = nullptr; - RenderEngineType *engine_type = RE_engines_find(scene->r.engine); - bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT"); - - ((use_eevee_next) ? EEVEE_NEXT_lightcache_info_update : - EEVEE_lightcache_info_update)(&scene->eevee); + EEVEE_lightcache_info_update(&scene->eevee); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 1aacf407af5..c84abcf0e7a 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -139,21 +139,6 @@ typedef struct LightCacheTexture { char _pad[2]; } LightCacheTexture; -typedef struct LightCacheIrradianceGrid { - /** Transform to local space [0..resolution-1] range. */ - float world_to_grid[4][4]; - /** Number of samples in each dimension. */ - int resolution[3]; - /** Number of surfels in the cache. */ - int surfels_len; - /** Currently only used at runtime for debugging the baking process. Not written to file. */ - void *surfels; - /** Irradiance Data. Stored as spherical harmonic. */ - LightCacheTexture irradiance_L0_L1_a; - LightCacheTexture irradiance_L0_L1_b; - LightCacheTexture irradiance_L0_L1_c; -} LightCacheIrradianceGrid; - typedef struct LightCache { int flag; /** Version number to know if the cache data is compatible with this version of blender. */ @@ -179,17 +164,10 @@ typedef struct LightCache { /* All lightprobes data contained in the cache. */ LightProbeCache *cube_data; LightGridCache *grid_data; - - /* ---- EEVEE-Next ---- */ - LightCacheIrradianceGrid *grids; - // LightCacheReflectionCube *cubes; } LightCache; /* Bump the version number for lightcache data structure changes. */ #define LIGHTCACHE_STATIC_VERSION 2 -/* Cache generated by EEVEE-Next. Should be removed and be replaced by bumped - * LIGHTCACHE_STATIC_VERSION once EEVEE-Next is made default. */ -#define LIGHTCACHE_NEXT_STATIC_VERSION 3 /* LightCache->type */ enum { @@ -306,6 +284,13 @@ typedef struct LightProbeGridCacheFrame { LightProbeIrradianceData irradiance; LightProbeVisibilityData visibility; LightProbeConnectivityData connectivity; + + char _pad[4]; + + /** Number of debug surfels. */ + int surfels_len; + /** Debug surfels used to visualize the baking process. */ + void *surfels; } LightProbeGridCacheFrame; /** #LightProbeGridCacheFrame.data_layout (int) */ -- 2.30.2 From d3e761cda3d1e3786465ddd1690a21a338b80581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 16 Apr 2023 22:00:56 +0200 Subject: [PATCH 32/58] EEVEE-Next: Make Spherical harmonic follow object transform This make the baking step output local space spherical harmonics which need to be rotated back to world before being evaluated. In practice we rotate the evaluated direction. --- .../blender/draw/engines/eevee_next/eevee_irradiance_cache.cc | 3 +++ source/blender/draw/engines/eevee_next/eevee_lightprobe.cc | 2 ++ source/blender/draw/engines/eevee_next/eevee_lightprobe.hh | 4 +++- source/blender/draw/engines/eevee_next/eevee_shader_shared.hh | 3 +++ .../eevee_next/shaders/eevee_display_probe_grid_frag.glsl | 4 +++- .../shaders/eevee_lightprobe_irradiance_ray_comp.glsl | 4 +++- .../eevee_next/shaders/infos/eevee_irradiance_cache_info.hh | 1 + 7 files changed, 18 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index f3a4b62b877..2c7e4d27418 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -137,6 +137,7 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb) display_grids_ps_.push_constant("sphere_radius", 0.3f); /* TODO property */ display_grids_ps_.push_constant("grid_resolution", grid_size); display_grids_ps_.push_constant("grid_to_world", grid.object_to_world); + display_grids_ps_.push_constant("world_to_grid", grid.world_to_object); display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx_); display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx_); @@ -281,6 +282,8 @@ void IrradianceBake::surfels_create(const Object &probe_object) dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE)); capture_info_buf_.irradiance_grid_size = grid_resolution; capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world; + capture_info_buf_.irradiance_grid_world_to_local_rotation = float4x4( + (invert(normalize(float3x3(grid_local_to_world))))); capture_info_buf_.irradiance_accum_solid_angle = 0.0f; /* Divide by twice the sample count because each ray is evaluated in both directions. */ capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) / diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 1da2d23a2a8..0bd327d82b2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -29,6 +29,8 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle) grid.initialized = true; grid.updated = true; grid.object_to_world = float4x4(ob->object_to_world); + grid.world_to_object = float4x4( + math::normalize(math::transpose(float3x3(grid.object_to_world)))); grid.cache = ob->lightprobe_cache; } } diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index db08f78821d..ab1c5091d73 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -25,8 +25,10 @@ struct LightProbe { }; struct IrradianceGrid : public LightProbe { - /** Reference to the light-cache data. Should be refreshed every sync. */ + /** Copy of the transform matrix. */ float4x4 object_to_world; + /** Precomputed inverse transform with normalized axes. No position. Used for rotating SH. */ + float4x4 world_to_object; /** * Reference to the light-cache data. * Do not try to dereference it before LightProbeModule::end_sync() as the grid could diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 382fd0f3898..99cd4af1229 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -876,6 +876,9 @@ struct CaptureInfoData { float irradiance_accum_solid_angle; /** Transform of the lightprobe object. */ float4x4 irradiance_grid_local_to_world; + /** Transform vectors from world space to local space. Does not have location component. */ + /** TODO(fclem): This could be a float3x4 or a float3x3 if padded correctly. */ + float4x4 irradiance_grid_world_to_local_rotation; }; BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl index d0e2e696f17..8fd276efbaa 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl @@ -1,4 +1,5 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) void main() @@ -19,7 +20,8 @@ void main() vec3 vN = vec3(lP, sqrt(max(0.0, 1.0 - dist_sqr))); vec3 N = normal_view_to_world(vN); + vec3 lN = transform_direction(world_to_grid, N); - vec4 irradiance = spherical_harmonics_evaluate_lambert(N, sh); + vec4 irradiance = spherical_harmonics_evaluate_lambert(lN, sh); out_color = vec4(irradiance.rgb, 0.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl index fbd3f063bff..c82033c4db4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -16,8 +16,10 @@ void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh) { + vec3 lL = transform_direction(capture_info_buf.irradiance_grid_world_to_local_rotation, L); + spherical_harmonics_encode_signal_sample( - L, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh); + lL, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh); } void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh) diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 5239ff904e4..4107cbc38cd 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -74,6 +74,7 @@ GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) .push_constant(Type::FLOAT, "sphere_radius") .push_constant(Type::IVEC3, "grid_resolution") .push_constant(Type::MAT4, "grid_to_world") + .push_constant(Type::MAT4, "world_to_grid") .sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx") .sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx") .sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx") -- 2.30.2 From f14c7e804c44ba424b9b38b45323b996c1e967a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 22 Apr 2023 18:08:27 +0200 Subject: [PATCH 33/58] EEVEE-Next: Irradiance Cache: Load irradiance in atlas This approach allow streaming of visible grids and has a controlled memory budget. --- source/blender/draw/CMakeLists.txt | 2 + .../draw/engines/eevee_next/eevee_defines.hh | 12 +- .../draw/engines/eevee_next/eevee_instance.cc | 2 - .../eevee_next/eevee_irradiance_cache.cc | 238 ++++++++++++++++-- .../eevee_next/eevee_irradiance_cache.hh | 23 ++ .../engines/eevee_next/eevee_lightprobe.cc | 10 + .../engines/eevee_next/eevee_lightprobe.hh | 6 +- .../draw/engines/eevee_next/eevee_pipeline.cc | 3 + .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 1 + .../engines/eevee_next/eevee_shader_shared.hh | 33 +++ .../shaders/eevee_deferred_light_frag.glsl | 3 + .../eevee_display_probe_grid_frag.glsl | 4 +- .../shaders/eevee_lightprobe_eval_lib.glsl | 80 ++++++ ...eevee_lightprobe_irradiance_load_comp.glsl | 51 ++++ .../shaders/eevee_lightprobe_lib.glsl | 27 ++ .../shaders/eevee_lightprobe_test.glsl | 71 ++++++ .../eevee_spherical_harmonics_lib.glsl | 55 +++- .../shaders/infos/eevee_deferred_info.hh | 1 + .../shaders/infos/eevee_hiz_info.hh | 4 +- .../infos/eevee_irradiance_cache_info.hh | 82 ++++-- source/blender/draw/intern/DRW_gpu_wrapper.hh | 25 +- .../shaders/common_debug_print_lib.glsl | 9 + source/blender/gpu/GPU_shader_shared_utils.h | 3 + 24 files changed, 684 insertions(+), 63 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_test.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 4e7ced585cb..c3df9bb6eb8 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -465,7 +465,9 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_light_eval_lib.glsl engines/eevee_next/shaders/eevee_light_iter_lib.glsl engines/eevee_next/shaders/eevee_light_lib.glsl + engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl + engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_lib.glsl engines/eevee_next/shaders/eevee_ltc_lib.glsl engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index b6c6686ed11..6d644d4dd7d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -71,6 +71,10 @@ #define MOTION_BLUR_GROUP_SIZE 32 #define MOTION_BLUR_DILATE_GROUP_SIZE 512 +/* Irradiance Cache. */ +/** Maximum number of entities inside the cache. */ +#define IRRADIANCE_GRID_MAX 64 + /* Depth Of Field. */ #define DOF_TILES_SIZE 8 #define DOF_TILES_FLATTEN_GROUP_SIZE DOF_TILES_SIZE @@ -89,17 +93,20 @@ /* IrradianceBake. */ #define SURFEL_GROUP_SIZE 256 #define SURFEL_LIST_GROUP_SIZE 256 -#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, do 4x4x4 workgroup size. */ +#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, so 4x4x4 workgroup size. */ +#define IRRADIANCE_GRID_BRICK_SIZE 4 /* In each dimension, so 4x4x4 brick size. */ /* Resource bindings. */ /* Texture. */ +#define IRRADIANCE_ATLAS_TEX_SLOT 11 #define SHADOW_TILEMAPS_TEX_SLOT 12 /* Only during surface shading. */ #define SHADOW_ATLAS_TEX_SLOT 13 /* Only during shadow rendering. */ #define SHADOW_RENDER_MAP_SLOT 13 #define RBUFS_UTILITY_TEX_SLOT 14 +#define HIZ_TEX_SLOT 15 /* Images. */ #define RBUFS_NORMAL_SLOT 0 @@ -115,6 +122,8 @@ #define GBUF_COLOR_SLOT RBUFS_DIFF_COLOR_SLOT /* Uniform Buffers. */ +#define IRRADIANCE_GRID_BUF_SLOT 3 +#define HIZ_BUF_SLOT 5 /* Only during pre-pass. */ #define VELOCITY_CAMERA_PREV_BUF 3 #define VELOCITY_CAMERA_CURR_BUF 4 @@ -127,6 +136,7 @@ #define LIGHT_BUF_SLOT 1 #define LIGHT_ZBIN_BUF_SLOT 2 #define LIGHT_TILE_BUF_SLOT 3 +#define IRRADIANCE_BRICK_BUF_SLOT 4 /* Only during surface capture. */ #define SURFEL_BUF_SLOT 4 /* Only during surface capture. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 43b9d90421d..e4d5b82e6b1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -474,7 +474,6 @@ void Instance::light_bake_irradiance(Object &probe, }; custom_pipeline_wrapper([&]() { - GPU_debug_capture_begin(); irradiance_cache.bake.surfel_raster_views_sync(probe); /* TODO: lightprobe visibility group option. */ manager->begin_sync(); @@ -483,7 +482,6 @@ void Instance::light_bake_irradiance(Object &probe, irradiance_cache.bake.surfels_create(probe); irradiance_cache.bake.surfels_lights_eval(); - GPU_debug_capture_end(); }); int bounce_len = scene->eevee.gi_diffuse_bounces; diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 2c7e4d27418..fa0c8260d6f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -22,7 +22,44 @@ namespace blender::eevee { void IrradianceCache::init() { display_grids_enabled_ = DRW_state_draw_support() && - inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE; + (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE); + + /* TODO option. */ + int atlas_byte_size = 1024 * 1024 * 16; + /* This might become an option in the future. */ + bool use_l2_band = false; + int sh_coef_len = use_l2_band ? 9 : 4; + int texel_byte_size = 8; /* Assumes GPU_RGBA16F. */ + int3 atlas_extent(IRRADIANCE_GRID_BRICK_SIZE); + atlas_extent.z *= sh_coef_len; + int atlas_col_count = 256; + atlas_extent.x *= atlas_col_count; + /* Determine the row count depending on the scene settings. */ + int row_byte_size = atlas_extent.x * atlas_extent.y * atlas_extent.z * texel_byte_size; + int atlas_row_count = divide_ceil_u(atlas_byte_size, row_byte_size); + atlas_extent.y *= atlas_row_count; + + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ; + do_full_update_ = irradiance_atlas_tx_.ensure_3d(GPU_RGBA16F, atlas_extent, usage); + + if (do_full_update_) { + /* Delete all references to existing bricks. */ + for (IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) { + grid.bricks.clear(); + } + brick_pool_.clear(); + /* Fill with all the available bricks. */ + for (auto i : IndexRange(atlas_row_count * atlas_col_count)) { + IrradianceBrick brick; + brick.atlas_coord = uint2(i % atlas_col_count, i / atlas_col_count) * + IRRADIANCE_GRID_BRICK_SIZE; + brick_pool_.append(irradiance_brick_pack(brick)); + } + } + + if (irradiance_atlas_tx_.is_valid() == false) { + inst_.info = "Irradiance Atlas texture could not be created"; + } } void IrradianceCache::sync() @@ -32,6 +69,165 @@ void IrradianceCache::sync() } } +Vector IrradianceCache::bricks_alloc(int brick_len) +{ + if (brick_pool_.size() < brick_len) { + /* Fail allocation. Not enough brick in the atlas. */ + return {}; + } + Vector allocated; + allocated.resize(brick_len); + /* Copy bricks to return vector. */ + allocated.as_mutable_span().copy_from(brick_pool_.as_span().take_back(brick_len)); + /* Remove bricks from the pool. */ + brick_pool_.resize(brick_pool_.size() - brick_len); + + return allocated; +} + +void IrradianceCache::bricks_free(Vector &bricks) +{ + brick_pool_.extend(bricks.as_span()); + bricks.clear(); +} + +void IrradianceCache::set_view(View & /*view*/) +{ + Vector grid_updates; + + /* First allocate the needed bricks and populate the brick buffer. */ + int grids_len = 0; + bricks_infos_buf_.clear(); + for (IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) { + LightProbeGridCacheFrame *cache = grid.cache ? grid.cache->grid_static_cache : nullptr; + if (cache == nullptr) { + continue; + } + + if (cache->baking.L0 == nullptr && cache->irradiance.L0 == nullptr) { + /* No data. */ + continue; + } + + int3 grid_size = int3(cache->size); + if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) { + inst_.info = "Error: Malformed irradiance grid data"; + continue; + } + + /* TODO frustum cull and only load visible grids. */ + + if (grids_len >= IRRADIANCE_GRID_MAX) { + inst_.info = "Error: Too many grid visible"; + continue; + } + + if (grid.bricks.is_empty()) { + int3 grid_size_in_bricks = math::divide_ceil(grid_size, + int3(IRRADIANCE_GRID_BRICK_SIZE - 1)); + int brick_len = grid_size_in_bricks.x * grid_size_in_bricks.y * grid_size_in_bricks.z; + grid.bricks = bricks_alloc(brick_len); + + if (grid.bricks.is_empty()) { + inst_.info = "Error: Irradiance grid allocation failed"; + continue; + } + + grid_updates.append(&grid); + } + + grid.brick_offset = bricks_infos_buf_.size(); + bricks_infos_buf_.extend(grid.bricks); + + if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) { + inst_.info = "Error: Malformed irradiance grid data"; + continue; + } + + float4x4 grid_to_world = grid.object_to_world * math::from_location(float3(-1.0f)) * + math::from_scale(float3(2.0f / float3(grid_size))) * + math::from_location(float3(0.0f)); + + grid.world_to_grid_transposed = float3x4(math::transpose(math::invert(grid_to_world))); + grid.grid_size = grid_size; + grid.grid_index = grids_len; + grids_infos_buf_[grids_len++] = grid; + } + + /* TODO(fclem): Insert world grid here. */ + + if (grids_len < IRRADIANCE_GRID_MAX) { + /* Tag last grid as invalid to stop the iteration. */ + grids_infos_buf_[grids_len].grid_size = int3(-1); + } + + bricks_infos_buf_.push_update(); + grids_infos_buf_.push_update(); + + /* Upload data for each grid that need to be inserted in the atlas. */ + for (IrradianceGrid *grid : grid_updates) { + LightProbeGridCacheFrame *cache = grid->cache->grid_static_cache; + + /* Staging textures are recreated for each light grid to avoid increasing VRAM usage. */ + draw::Texture irradiance_a_tx = {"irradiance_a_tx"}; + draw::Texture irradiance_b_tx = {"irradiance_b_tx"}; + draw::Texture irradiance_c_tx = {"irradiance_c_tx"}; + draw::Texture irradiance_d_tx = {"irradiance_d_tx"}; + + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; + int3 grid_size = int3(cache->size); + if (cache->baking.L0) { + irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0); + irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a); + irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b); + irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c); + } + else if (cache->irradiance.L0) { + irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0); + irradiance_b_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a); + irradiance_c_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b); + irradiance_d_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c); + } + else { + continue; + } + + if (irradiance_a_tx.is_valid() == false) { + inst_.info = "Error: Could not allocate irradiance staging texture"; + /* Avoid undefined behavior with uninitialized values. */ + irradiance_a_tx.clear(float4(0.0f)); + irradiance_b_tx.clear(float4(0.0f)); + irradiance_c_tx.clear(float4(0.0f)); + irradiance_d_tx.clear(float4(0.0f)); + } + + grid_upload_ps_.init(); + grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD)); + + grid_upload_ps_.push_constant("grid_index", grid->grid_index); + grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_); + grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_); + grid_upload_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx); + grid_upload_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx); + grid_upload_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx); + grid_upload_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx); + grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_); + + /* Note that we take into account the padding border of each brick. */ + int3 grid_size_in_bricks = math::divide_ceil(grid_size, int3(IRRADIANCE_GRID_BRICK_SIZE - 1)); + grid_upload_ps_.dispatch(grid_size_in_bricks); + + inst_.manager->submit(grid_upload_ps_); + + irradiance_a_tx.free(); + irradiance_b_tx.free(); + irradiance_c_tx.free(); + irradiance_d_tx.free(); + } + + do_full_update_ = false; +} + void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb) { if (!inst_.is_baking()) { @@ -105,24 +301,24 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb) } /* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */ - draw::Texture irradiance_a_tx_ = {"irradiance_a_tx"}; - draw::Texture irradiance_b_tx_ = {"irradiance_b_tx"}; - draw::Texture irradiance_c_tx_ = {"irradiance_c_tx"}; - draw::Texture irradiance_d_tx_ = {"irradiance_d_tx"}; + draw::Texture irradiance_a_tx = {"irradiance_a_tx"}; + draw::Texture irradiance_b_tx = {"irradiance_b_tx"}; + draw::Texture irradiance_c_tx = {"irradiance_c_tx"}; + draw::Texture irradiance_d_tx = {"irradiance_d_tx"}; eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; int3 grid_size = int3(cache->size); if (cache->baking.L0) { - irradiance_a_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0); - irradiance_b_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a); - irradiance_c_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b); - irradiance_d_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c); + irradiance_a_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0); + irradiance_b_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a); + irradiance_c_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b); + irradiance_d_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c); } else if (cache->irradiance.L0) { - irradiance_a_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0); - irradiance_b_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a); - irradiance_c_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b); - irradiance_d_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c); + irradiance_a_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0); + irradiance_b_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a); + irradiance_c_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b); + irradiance_d_tx.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c); } else { continue; @@ -139,10 +335,10 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb) display_grids_ps_.push_constant("grid_to_world", grid.object_to_world); display_grids_ps_.push_constant("world_to_grid", grid.world_to_object); - display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx_); - display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx_); - display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx_); - display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx_); + display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx); + display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx); + display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx); + display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx); int sample_count = static_cast(BKE_lightprobe_grid_cache_frame_sample_count(cache)); int triangle_count = sample_count * 2; @@ -150,10 +346,10 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb) inst_.manager->submit(display_grids_ps_, view); - irradiance_a_tx_.free(); - irradiance_b_tx_.free(); - irradiance_c_tx_.free(); - irradiance_d_tx_.free(); + irradiance_a_tx.free(); + irradiance_b_tx.free(); + irradiance_c_tx.free(); + irradiance_d_tx.free(); } } diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index c0563cccb14..621b5509f17 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -128,6 +128,18 @@ class IrradianceCache { private: Instance &inst_; + /** Atlas 3D texture containing all loaded grid data. */ + Texture irradiance_atlas_tx_ = {"irradiance_atlas_tx_"}; + /** Data structure used to index irradiance cache pages inside the atlas. */ + IrradianceGridDataBuf grids_infos_buf_ = {"grids_infos_buf_"}; + IrradianceBrickBuf bricks_infos_buf_ = {"bricks_infos_buf_"}; + /** Pool of atlas regions to allocate to different grids. */ + Vector brick_pool_; + /** Stream data into the irradiance atlas texture. */ + PassSimple grid_upload_ps_ = {"IrradianceCache.Upload"}; + /** If true, will trigger the reupload of all grid data instead of just streaming new ones. */ + bool do_full_update_ = true; + /** Display surfel debug data. */ PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"}; /** Debug surfel elements copied from the light cache. */ @@ -143,8 +155,19 @@ class IrradianceCache { void init(); void sync(); + void set_view(View &view); void viewport_draw(View &view, GPUFrameBuffer *view_fb); + Vector bricks_alloc(int brick_len); + void bricks_free(Vector &bricks); + + template void bind_resources(draw::detail::PassBase *pass) + { + pass->bind_ubo(IRRADIANCE_GRID_BUF_SLOT, &grids_infos_buf_); + pass->bind_ssbo(IRRADIANCE_BRICK_BUF_SLOT, &bricks_infos_buf_); + pass->bind_texture(IRRADIANCE_ATLAS_TEX_SLOT, &irradiance_atlas_tx_); + } + private: void debug_pass_draw(View &view, GPUFrameBuffer *view_fb); void display_pass_draw(View &view, GPUFrameBuffer *view_fb); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index 0bd327d82b2..05509fa4d7b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -13,6 +13,8 @@ #include "eevee_instance.hh" #include "eevee_lightprobe.hh" +#include "draw_debug.hh" + namespace blender::eevee { void LightProbeModule::begin_sync() @@ -31,7 +33,10 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle) grid.object_to_world = float4x4(ob->object_to_world); grid.world_to_object = float4x4( math::normalize(math::transpose(float3x3(grid.object_to_world)))); + grid.cache = ob->lightprobe_cache; + /* Force reupload. */ + inst_.irradiance_cache.bricks_free(grid.bricks); } } @@ -71,9 +76,11 @@ void LightProbeModule::end_sync() for (auto it = grid_map_.items().begin(); it != it_end; ++it) { IrradianceGrid &grid = (*it).value; if (grid.updated) { + grid.updated = false; grid_update_ = true; } if (!grid.used) { + inst_.irradiance_cache.bricks_free(grid.bricks); grid_map_.remove(it); grid_update_ = true; continue; @@ -89,6 +96,7 @@ void LightProbeModule::end_sync() for (auto it = cube_map_.items().begin(); it != it_end; ++it) { ReflectionCube &cube = (*it).value; if (cube.updated) { + cube.updated = false; cube_update_ = true; } if (!cube.used) { @@ -101,6 +109,7 @@ void LightProbeModule::end_sync() } } +#if 0 /* TODO make this work with new per object light cache. */ /* If light-cache auto-update is enable we tag the relevant part * of the cache to update and fire up a baking job. */ if (auto_bake_enabled_ && (grid_update_ || cube_update_)) { @@ -123,6 +132,7 @@ void LightProbeModule::end_sync() WM_event_add_notifier(DRW_context_state_get()->evil_C, NC_LIGHTPROBE, original_scene); } } +#endif } } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index ab1c5091d73..4e500b7abd5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -24,7 +24,7 @@ struct LightProbe { bool updated = false; }; -struct IrradianceGrid : public LightProbe { +struct IrradianceGrid : public LightProbe, IrradianceGridData { /** Copy of the transform matrix. */ float4x4 object_to_world; /** Precomputed inverse transform with normalized axes. No position. Used for rotating SH. */ @@ -36,6 +36,10 @@ struct IrradianceGrid : public LightProbe { * pruning have been done. */ const struct LightProbeObjectCache *cache = nullptr; + /** List of associated atlas bricks that are used by this grid. */ + Vector bricks; + /** Index of the grid inside the grid UBO. */ + int grid_index; }; struct ReflectionCube : public LightProbe {}; diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 5d4ec4288ca..6c51b1c6720 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -263,6 +263,7 @@ void ForwardPipeline::render(View &view, // } inst_.shadows.set_view(view); + inst_.irradiance_cache.set_view(view); GPU_framebuffer_bind(combined_fb); inst_.manager->submit(opaque_ps_, view); @@ -385,6 +386,7 @@ void DeferredLayer::end_sync() inst_.shadows.bind_resources(&eval_light_ps_); inst_.sampling.bind_resources(&eval_light_ps_); inst_.hiz_buffer.bind_resources(&eval_light_ps_); + inst_.irradiance_cache.bind_resources(&eval_light_ps_); eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); @@ -425,6 +427,7 @@ void DeferredLayer::render(View &view, inst_.hiz_buffer.set_dirty(); inst_.shadows.set_view(view); + inst_.irradiance_cache.set_view(view); inst_.gbuffer.acquire(extent, closure_bits_); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index ebf5851fb10..41189897379 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -150,6 +150,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_light_culling_zbin"; case LIGHTPROBE_IRRADIANCE_RAY: return "eevee_lightprobe_irradiance_ray"; + case LIGHTPROBE_IRRADIANCE_LOAD: + return "eevee_lightprobe_irradiance_load"; case SHADOW_CLIPMAP_CLEAR: return "eevee_shadow_clipmap_clear"; case SHADOW_DEBUG: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index f6c27ab6c55..2775d15f7d1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -64,6 +64,7 @@ enum eShaderType { LIGHT_CULLING_ZBIN, LIGHTPROBE_IRRADIANCE_RAY, + LIGHTPROBE_IRRADIANCE_LOAD, MOTION_BLUR_GATHER, MOTION_BLUR_TILE_DILATE, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index e77d262e908..8df488d8aed 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -892,6 +892,37 @@ struct SurfelListInfoData { }; BLI_STATIC_ASSERT_ALIGN(SurfelListInfoData, 16) +struct IrradianceGridData { + /** World to non-normalized local grid space [0..size-1]. Stored transposed for compactness. */ + float3x4 world_to_grid_transposed; + /** Number of bricks for this grid. */ + int3 grid_size; + /** Index in brick descriptor list of the first brick of this grid. */ + int brick_offset; +}; +BLI_STATIC_ASSERT_ALIGN(IrradianceGridData, 16) + +struct IrradianceBrick { + /* Offset in pixel to the start of the data inside the atlas texture. */ + uint2 atlas_coord; +}; +/** \note Stored packed as a uint. */ +#define IrradianceBrickPacked uint + +static inline IrradianceBrickPacked irradiance_brick_pack(IrradianceBrick brick) +{ + uint2 data = (uint2(brick.atlas_coord) & 0xFFFFu) << uint2(0u, 16u); + IrradianceBrickPacked brick_packed = data.x | data.y; + return brick_packed; +} + +static inline IrradianceBrick irradiance_brick_unpack(IrradianceBrickPacked brick_packed) +{ + IrradianceBrick brick; + brick.atlas_coord = (uint2(brick_packed) >> uint2(0u, 16u)) & uint2(0xFFFFu); + return brick; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1002,6 +1033,8 @@ using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer; using FilmDataBuf = draw::UniformBuffer; using HiZDataBuf = draw::UniformBuffer; +using IrradianceGridDataBuf = draw::UniformArrayBuffer; +using IrradianceBrickBuf = draw::StorageVectorBuffer; using LightCullingDataBuf = draw::StorageBuffer; using LightCullingKeyBuf = draw::StorageArrayBuffer; using LightCullingTileBuf = draw::StorageArrayBuffer; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index 80dcaaf67cb..7d5d34d82f5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -9,6 +9,7 @@ #pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) void main() { @@ -52,6 +53,8 @@ void main() vec3 diffuse_light = vec3(0.0); vec3 reflection_light = vec3(0.0); + lightprobe_eval(diffuse_data, reflection_data, P, Ng, V, diffuse_light, reflection_light); + light_eval( diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl index 8fd276efbaa..fbaca7ad04f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl @@ -22,6 +22,6 @@ void main() vec3 N = normal_view_to_world(vN); vec3 lN = transform_direction(world_to_grid, N); - vec4 irradiance = spherical_harmonics_evaluate_lambert(lN, sh); - out_color = vec4(irradiance.rgb, 0.0); + vec3 irradiance = spherical_harmonics_evaluate_lambert(lN, sh); + out_color = vec4(irradiance, 0.0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl new file mode 100644 index 00000000000..901ddb44601 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl @@ -0,0 +1,80 @@ + +/** + * The resources expected to be defined are: + * - grids_infos_buf + * - bricks_infos_buf + * - irradiance_atlas_tx + */ + +#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) + +/** + * Return sample coordinates of the first SH coef in unormalized texture space. + */ +vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 lP) +{ + ivec3 brick_coord = ivec3((lP - 0.5) / float(IRRADIANCE_GRID_BRICK_SIZE - 1)); + /* Avoid sampling adjacent bricks. */ + brick_coord = max(brick_coord, ivec3(0)); + /* Avoid sampling adjacent bricks. */ + lP = max(lP, vec3(0.5)); + /* Local position inside the brick (still in grid sample spacing unit). */ + vec3 brick_lP = lP - vec3(brick_coord) * float(IRRADIANCE_GRID_BRICK_SIZE - 1); + + int brick_index = lightprobe_irradiance_grid_brick_index_get(grid_data, brick_coord); + + IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]); + vec3 output_coord = vec3(brick.atlas_coord, 0.0) + brick_lP; + + return output_coord; +} + +vec4 textureUnormalizedCoord(sampler3D tx, vec3 co) +{ + return texture(tx, co / vec3(textureSize(tx, 0))); +} + +SphericalHarmonicL1 lightprobe_irradiance_sample(sampler3D atlas_tx, vec3 P) +{ + vec3 lP; + int grid_index; + for (grid_index = 0; grid_index < IRRADIANCE_GRID_MAX; grid_index++) { + /* Last grid is tagged as invalid to stop the iteration. */ + if (grids_infos_buf[grid_index].grid_size.x == -1) { + /* Sample the last grid instead. */ + /* TODO: Avoid this by using a default world grid. */ + grid_index -= 1; + break; + } + /* If. */ + if (lightprobe_irradiance_grid_local_coord(grids_infos_buf[grid_index], P, lP)) { + break; + } + } + + vec3 atlas_coord = lightprobe_irradiance_grid_atlas_coord(grids_infos_buf[grid_index], lP); + + SphericalHarmonicL1 sh; + sh.L0.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord); + atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE); + sh.L1.Mn1 = textureUnormalizedCoord(atlas_tx, atlas_coord); + atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE); + sh.L1.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord); + atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE); + sh.L1.Mp1 = textureUnormalizedCoord(atlas_tx, atlas_coord); + return sh; +} + +void lightprobe_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 Ng, + vec3 V, + inout vec3 out_diffuse, + inout vec3 out_specular) +{ + SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(irradiance_atlas_tx, P); + + out_diffuse += spherical_harmonics_evaluate_lambert(diffuse.N, irradiance); +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl new file mode 100644 index 00000000000..32156bb37d3 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl @@ -0,0 +1,51 @@ + +/** + * Load an input lightgrid cache texture into the atlas. + * + * Each thread group will load a brick worth of data and add the needed padding texels. + */ + +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) + +void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer) +{ + imageStore(irradiance_atlas_img, + ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + ivec3(gl_LocalInvocationID), + sh_coefficient); +} + +void main() +{ + int brick_index = lightprobe_irradiance_grid_brick_index_get(grids_infos_buf[grid_index], + ivec3(gl_WorkGroupID)); + /* Brick coordinate in the source grid. */ + ivec3 brick_coord = ivec3(gl_WorkGroupID); + /* Add padding border to allow bilinear filtering. */ + ivec3 texel_coord = brick_coord * (IRRADIANCE_GRID_BRICK_SIZE - 1) + ivec3(gl_LocalInvocationID); + ivec3 input_coord = min(texel_coord, textureSize(irradiance_a_tx, 0) - 1); + + /* Brick coordinate in the destination atlas. */ + IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]); + ivec2 output_coord = ivec2(brick.atlas_coord); + + SphericalHarmonicL1 sh; + sh.L0.M0 = texelFetch(irradiance_a_tx, input_coord, 0); + sh.L1.Mn1 = texelFetch(irradiance_b_tx, input_coord, 0); + sh.L1.M0 = texelFetch(irradiance_c_tx, input_coord, 0); + sh.L1.Mp1 = texelFetch(irradiance_d_tx, input_coord, 0); + + /* Rotate Spherical Harmonic into world space. */ + mat3 world_to_grid_transposed = mat3(grids_infos_buf[grid_index].world_to_grid_transposed); + mat3 rotation = normalize(world_to_grid_transposed); + spherical_harmonics_L1_rotate(rotation, sh.L1); + + atlas_store(sh.L0.M0, output_coord, 0); + atlas_store(sh.L1.Mn1, output_coord, 1); + atlas_store(sh.L1.M0, output_coord, 2); + atlas_store(sh.L1.Mp1, output_coord, 3); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl index 0b25464ce2f..ccfaa025fb8 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) + vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world, ivec3 grid_resolution, ivec3 cell_coord) @@ -8,3 +10,28 @@ vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world, vec3 ws_cell_pos = (grid_local_to_world * vec4(ls_cell_pos, 1.0)).xyz; return ws_cell_pos; } + +/** + * Return true if sample position is valid. + * \a r_lP is the local position in grid units [0..grid_size). + */ +bool lightprobe_irradiance_grid_local_coord(IrradianceGridData grid_data, vec3 P, out vec3 r_lP) +{ + /* Position in cell units. */ + /* NOTE: The vector-matrix multiplication swapped on purpose to cancel the matrix transpose. */ + vec3 lP = (vec4(P, 1.0) * grid_data.world_to_grid_transposed).xyz; + r_lP = clamp(lP, vec3(0.0), vec3(grid_data.grid_size) - 1e-5); + /* Sample is valid if position wasn't clamped. */ + return all(equal(lP, r_lP)); +} + +int lightprobe_irradiance_grid_brick_index_get(IrradianceGridData grid_data, ivec3 brick_coord) +{ + int3 grid_size_in_bricks = divide_ceil(grid_data.grid_size, + int3(IRRADIANCE_GRID_BRICK_SIZE - 1)); + int brick_index = grid_data.brick_offset; + brick_index += brick_coord.x; + brick_index += brick_coord.y * grid_size_in_bricks.x; + brick_index += brick_coord.z * grid_size_in_bricks.x * grid_size_in_bricks.y; + return brick_index; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_test.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_test.glsl new file mode 100644 index 00000000000..8db0abc1236 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_test.glsl @@ -0,0 +1,71 @@ +/* Directive for resetting the line numbering so the failing tests lines can be printed. + * This conflict with the shader compiler error logging scheme. + * Comment out for correct compilation error line. */ +#line 5 + +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_test_lib.glsl) + +#define TEST(a, b) if (true) + +void main() +{ + TEST(eevee_lightprobe, IrradianceBrickIndex) + { + float near = 0.5, far = 1.0; + mat4 pers_mat = projection_perspective(-near, near, -near, near, near, far); + mat4 normal_mat = invert(transpose(pers_mat)); + + LightData light; + light.clip_near = floatBitsToInt(near); + light.clip_far = floatBitsToInt(far); + light.influence_radius_max = far; + light.type = LIGHT_SPOT; + light.normal_mat_packed.x = normal_mat[3][2]; + light.normal_mat_packed.y = normal_mat[3][3]; + + vec2 atlas_size = vec2(SHADOW_TILEMAP_RES); + { + /* Simulate a "2D" plane crossing the frustum diagonaly. */ + vec3 lP0 = vec3(-1.0, 0.0, -1.0); + vec3 lP1 = vec3(0.5, 0.0, -0.5); + vec3 lTg = normalize(lP1 - lP0); + vec3 lNg = vec3(-lTg.z, 0.0, lTg.x); + + float expect = 1.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES); + EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4); + EXPECT_NEAR( + shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4); + EXPECT_NEAR( + shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4); + } + { + /* Simulate a "2D" plane crossing the near plane at the center diagonaly. */ + vec3 lP0 = vec3(-1.0, 0.0, -1.0); + vec3 lP1 = vec3(0.0, 0.0, -0.5); + vec3 lTg = normalize(lP1 - lP0); + vec3 lNg = vec3(-lTg.z, 0.0, lTg.x); + + float expect = 2.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES); + EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4); + EXPECT_NEAR( + shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4); + EXPECT_NEAR( + shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4); + } + { + /* Simulate a "2D" plane parallel to near clip plane. */ + vec3 lP0 = vec3(-1.0, 0.0, -0.75); + vec3 lP1 = vec3(0.0, 0.0, -0.75); + vec3 lTg = normalize(lP1 - lP0); + vec3 lNg = vec3(-lTg.z, 0.0, lTg.x); + + EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), 0.0, 1e-4); + EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), 0.0, 1e-4); + EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), 0.0, 1e-4); + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index c3706f1654a..0f82b34a366 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -187,6 +187,40 @@ vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Rotation + * \{ */ + +void spherical_harmonics_L0_rotate(mat3x3 rotation, inout SphericalHarmonicBandL0 L0) +{ + /* L0 band being a constant function (i.e: there is no directionallity) there is nothing to + * rotate. This is a no-op. */ +} + +void spherical_harmonics_L1_rotate(mat3x3 rotation, inout SphericalHarmonicBandL1 L1) +{ + /* Convert L1 coefficients to per channel column. + * Note the component shuffle to match blender coordinate system. */ + mat4x3 per_channel = transpose(mat3x4(L1.Mp1, L1.Mn1, -L1.M0)); + /* Rotate each channel. */ + per_channel[0] = rotation * per_channel[0]; + per_channel[1] = rotation * per_channel[1]; + per_channel[2] = rotation * per_channel[2]; + /* Convert back to L1 coefficients to per channel column. + * Note the component shuffle to match blender coordinate system. */ + mat3x4 per_coef = transpose(per_channel); + L1.Mn1 = per_coef[1]; + L1.M0 = -per_coef[2]; + L1.Mp1 = per_coef[0]; +} + +void spherical_harmonics_L2_rotate(mat3x3 rotation, inout SphericalHarmonicBandL2 L2) +{ + /* TODO */ +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Evaluation * \{ */ @@ -197,20 +231,23 @@ vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2) * 2/3 and 1/4. See this reference for more explanation: * https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */ -vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh) +vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh) { - return spherical_harmonics_L0_evaluate(N, sh.L0); + vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb; + return radiance; } -vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh) +vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh) { - return spherical_harmonics_L0_evaluate(N, sh.L0) + - spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0); + vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb + + spherical_harmonics_L1_evaluate(N, sh.L1).rgb * (2.0 / 3.0); + return radiance; } -vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh) +vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh) { - return spherical_harmonics_L0_evaluate(N, sh.L0) + - spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) + - spherical_harmonics_L2_evaluate(N, sh.L2) * (1.0 / 4.0); + vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb + + spherical_harmonics_L1_evaluate(N, sh.L1).rgb * (2.0 / 3.0) + + spherical_harmonics_L2_evaluate(N, sh.L2).rgb * (1.0 / 4.0); + return radiance; } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh index f5bc967e5b6..9dfdc52e7b7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -37,6 +37,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light) .additional_info("eevee_shared", "eevee_utility_texture", "eevee_light_data", + "eevee_lightprobe_data", "eevee_shadow_data", "eevee_deferred_base", "eevee_hiz_data", diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh index 5e32631a8f8..f3ec6e9d043 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh @@ -4,8 +4,8 @@ #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(eevee_hiz_data) - .sampler(15, ImageType::FLOAT_2D, "hiz_tx") - .uniform_buf(5, "HiZData", "hiz_buf"); + .sampler(HIZ_TEX_SLOT, ImageType::FLOAT_2D, "hiz_tx") + .uniform_buf(HIZ_BUF_SLOT, "HiZData", "hiz_buf"); GPU_SHADER_CREATE_INFO(eevee_hiz_update) .do_static_compilation(true) diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 4107cbc38cd..321ac377d8a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -3,6 +3,10 @@ #include "eevee_defines.hh" #include "gpu_shader_create_info.hh" +/* -------------------------------------------------------------------- */ +/** \name Display + * \{ */ + GPU_SHADER_INTERFACE_INFO(eevee_debug_surfel_iface, "") .smooth(Type::VEC3, "P") .flat(Type::INT, "surfel_index"); @@ -18,6 +22,32 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .push_constant(Type::INT, "debug_mode") .do_static_compilation(true); +GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "") + .smooth(Type::VEC2, "lP") + .flat(Type::IVEC3, "cell"); + +GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) + .additional_info("eevee_shared", "draw_view") + .vertex_source("eevee_display_probe_grid_vert.glsl") + .vertex_out(eevee_display_probe_grid_iface) + .fragment_source("eevee_display_probe_grid_frag.glsl") + .fragment_out(0, Type::VEC4, "out_color") + .push_constant(Type::FLOAT, "sphere_radius") + .push_constant(Type::IVEC3, "grid_resolution") + .push_constant(Type::MAT4, "grid_to_world") + .push_constant(Type::MAT4, "world_to_grid") + .sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx") + .sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx") + .sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx") + .sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx") + .do_static_compilation(true); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Baking + * \{ */ + GPU_SHADER_CREATE_INFO(eevee_surfel_common) .storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]") .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf"); @@ -61,26 +91,6 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray) .compute_source("eevee_surfel_ray_comp.glsl") .do_static_compilation(true); -GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "") - .smooth(Type::VEC2, "lP") - .flat(Type::IVEC3, "cell"); - -GPU_SHADER_CREATE_INFO(eevee_display_probe_grid) - .additional_info("eevee_shared", "draw_view") - .vertex_source("eevee_display_probe_grid_vert.glsl") - .vertex_out(eevee_display_probe_grid_iface) - .fragment_source("eevee_display_probe_grid_frag.glsl") - .fragment_out(0, Type::VEC4, "out_color") - .push_constant(Type::FLOAT, "sphere_radius") - .push_constant(Type::IVEC3, "grid_resolution") - .push_constant(Type::MAT4, "grid_to_world") - .push_constant(Type::MAT4, "world_to_grid") - .sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx") - .sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx") - .sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx") - .sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx") - .do_static_compilation(true); - GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) .local_group_size(IRRADIANCE_GRID_GROUP_SIZE, IRRADIANCE_GRID_GROUP_SIZE, @@ -94,3 +104,35 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) .image(3, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_c_img") .compute_source("eevee_lightprobe_irradiance_ray_comp.glsl") .do_static_compilation(true); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Runtime + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load) + .local_group_size(IRRADIANCE_GRID_BRICK_SIZE, + IRRADIANCE_GRID_BRICK_SIZE, + IRRADIANCE_GRID_BRICK_SIZE) + .additional_info("eevee_shared") + .push_constant(Type::INT, "grid_index") + .uniform_buf(0, "IrradianceGridData", "grids_infos_buf[IRRADIANCE_GRID_MAX]") + .storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]") + .sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx") + .sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx") + .sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx") + .sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx") + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img") + .compute_source("eevee_lightprobe_irradiance_load_comp.glsl") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(eevee_lightprobe_data) + .uniform_buf(IRRADIANCE_GRID_BUF_SLOT, + "IrradianceGridData", + "grids_infos_buf[IRRADIANCE_GRID_MAX]") + /* NOTE: Use uint instead of IrradianceBrickPacked because Metal need to know the exact type.*/ + .storage_buf(IRRADIANCE_BRICK_BUF_SLOT, Qualifier::READ, "uint", "bricks_infos_buf[]") + .sampler(IRRADIANCE_ATLAS_TEX_SLOT, ImageType::FLOAT_3D, "irradiance_atlas_tx"); + +/** \} */ diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 82288a5fe86..90abecc2a42 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -176,8 +176,11 @@ class UniformCommon : public DataBuffer, NonMovable, NonCopyable #endif public: - UniformCommon() + UniformCommon(const char *name = nullptr) { + if (name) { + name_ = name; + } ubo_ = GPU_uniformbuf_create_ex(sizeof(T) * len, nullptr, name_); } @@ -276,7 +279,7 @@ template< /* bool device_only = false */> class UniformArrayBuffer : public detail::UniformCommon { public: - UniformArrayBuffer() + UniformArrayBuffer(const char *name = nullptr) : detail::UniformCommon(name) { /* TODO(@fclem): We should map memory instead. */ this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_); @@ -295,7 +298,7 @@ template< /* bool device_only = false */> class UniformBuffer : public T, public detail::UniformCommon { public: - UniformBuffer() + UniformBuffer(const char *name = nullptr) : detail::UniformCommon(name) { /* TODO(@fclem): How could we map this? */ this->data_ = static_cast(this); @@ -367,6 +370,11 @@ class StorageArrayBuffer : public detail::StorageCommon { return this->len_; } + MutableSpan as_span() const + { + return {this->data_, this->len_}; + } + static void swap(StorageArrayBuffer &a, StorageArrayBuffer &b) { SWAP(T *, a.data_, b.data_); @@ -422,6 +430,14 @@ class StorageVectorBuffer : public StorageArrayBuffer { new (ptr) T(std::forward(value)...); } + void extend(const Span &values) + { + /* TODO(fclem): Optimize to a single memcpy. */ + for (auto v : values) { + this->append(v); + } + } + int64_t size() const { return item_len_; @@ -1032,8 +1048,7 @@ class TextureRef : public Texture { * Dummy type to bind texture as image. * It is just a GPUTexture in disguise. */ -class Image { -}; +class Image {}; static inline Image *as_image(GPUTexture *tex) { diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl index 36ff3a90622..4e5623abe98 100644 --- a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl +++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl @@ -396,3 +396,12 @@ void drw_print_value(mat4 value) drw_print(" ", value[3]); drw_print(")"); } + +void drw_print_value(mat3 value) +{ + drw_print("mat3x3("); + drw_print(" ", value[0]); + drw_print(" ", value[1]); + drw_print(" ", value[2]); + drw_print(")"); +} diff --git a/source/blender/gpu/GPU_shader_shared_utils.h b/source/blender/gpu/GPU_shader_shared_utils.h index 8771c04d6e3..16b7c9b8e5b 100644 --- a/source/blender/gpu/GPU_shader_shared_utils.h +++ b/source/blender/gpu/GPU_shader_shared_utils.h @@ -48,6 +48,7 @@ # ifndef GPU_METAL # define float2 vec2 # define float3 vec3 +# define float3x4 mat3x4 # define float4 vec4 # define float4x4 mat4 # define int2 ivec2 @@ -72,6 +73,7 @@ # include "BLI_math_vector_types.hh" using blender::float2; using blender::float3; +using blender::float3x4; using blender::float4; using blender::float4x4; using blender::int2; @@ -91,6 +93,7 @@ typedef float float2[2]; typedef float float3[3]; typedef float float4[4]; typedef float float4x4[4][4]; +typedef float float3x4[3][4]; typedef int int2[2]; typedef int int3[2]; typedef int int4[4]; -- 2.30.2 From 0388f93e790883c7c7d6c389d7e63af7d38776f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 27 Apr 2023 19:59:11 +0200 Subject: [PATCH 34/58] EEVEE-Next: Irradiance Cache: Store final format --- .../eevee_next/eevee_irradiance_cache.cc | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index fa0c8260d6f..8b201d8734b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -693,20 +693,30 @@ LightProbeGridCacheFrame *IrradianceBake::read_result_packed() GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); - /* TODO(fclem): Temp. */ cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read(GPU_DATA_FLOAT); cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read(GPU_DATA_FLOAT); cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read(GPU_DATA_FLOAT); cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read(GPU_DATA_FLOAT); - /* TODO(fclem): Create packed format texture and swizzle on gpu. */ - // cache_frame->irradiance.L0 = (float(*)[4])irradiance_only_L0_tx_.read(GPU_DATA_FLOAT); - // cache_frame->irradiance.L1_a = - // (float(*)[4])irradiance_only_L1_a_tx_.read(GPU_DATA_FLOAT); - // cache_frame->irradiance.L1_b = - // (float(*)[4])irradiance_only_L1_b_tx_.read(GPU_DATA_FLOAT); - // cache_frame->irradiance.L1_c = - // (float(*)[4])irradiance_only_L1_c_tx_.read(GPU_DATA_FLOAT); + int64_t sample_count = irradiance_L0_tx_.width() * irradiance_L0_tx_.height() * + irradiance_L0_tx_.depth(); + size_t coefficient_texture_size = sizeof(*cache_frame->irradiance.L0) * sample_count; + cache_frame->irradiance.L0 = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__); + cache_frame->irradiance.L1_a = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__); + cache_frame->irradiance.L1_b = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__); + cache_frame->irradiance.L1_c = (float(*)[3])MEM_mallocN(coefficient_texture_size, __func__); + + for (auto i : IndexRange(sample_count)) { + copy_v3_v3(cache_frame->irradiance.L0[i], cache_frame->baking.L0[i]); + copy_v3_v3(cache_frame->irradiance.L1_a[i], cache_frame->baking.L1_a[i]); + copy_v3_v3(cache_frame->irradiance.L1_b[i], cache_frame->baking.L1_b[i]); + copy_v3_v3(cache_frame->irradiance.L1_c[i], cache_frame->baking.L1_c[i]); + } + + MEM_SAFE_FREE(cache_frame->baking.L0); + MEM_SAFE_FREE(cache_frame->baking.L1_a); + MEM_SAFE_FREE(cache_frame->baking.L1_b); + MEM_SAFE_FREE(cache_frame->baking.L1_c); // cache_frame->visibility.L0 = irradiance_only_L0_tx_.read(GPU_DATA_UBYTE); // cache_frame->visibility.L1_a = irradiance_only_L1_a_tx_.read(GPU_DATA_UBYTE); -- 2.30.2 From 12095b8e16b8bba779ebfe37d8a2c7732d351b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 30 Apr 2023 21:10:55 +0200 Subject: [PATCH 35/58] EEVEE-Next: Irradiance Cache: Use scene bounds for surfel cache This remove the per irradiance grid approach. The idea is to do the light transport only once for each visibility collection and store the irradiance for all irradiance grid using this visibility mask. This removes the need for the lightprobe near/far clip parameters. --- .../bl_ui/properties_data_lightprobe.py | 34 +++++- source/blender/draw/CMakeLists.txt | 1 + .../draw/engines/eevee_next/eevee_defines.hh | 1 + .../draw/engines/eevee_next/eevee_instance.cc | 1 - .../eevee_next/eevee_irradiance_cache.cc | 110 ++++++++++++------ .../eevee_next/eevee_irradiance_cache.hh | 10 +- .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 1 + .../engines/eevee_next/eevee_shader_shared.hh | 10 ++ ...vee_lightprobe_irradiance_bounds_comp.glsl | 48 ++++++++ .../infos/eevee_irradiance_cache_info.hh | 10 ++ .../shaders/infos/eevee_material_info.hh | 1 + source/blender/draw/intern/draw_manager.hh | 8 ++ 13 files changed, 196 insertions(+), 41 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl diff --git a/scripts/startup/bl_ui/properties_data_lightprobe.py b/scripts/startup/bl_ui/properties_data_lightprobe.py index 7ef9243653d..72a0a07e1d3 100644 --- a/scripts/startup/bl_ui/properties_data_lightprobe.py +++ b/scripts/startup/bl_ui/properties_data_lightprobe.py @@ -33,7 +33,7 @@ class DATA_PT_context_lightprobe(DataButtonsPanel, Panel): class DATA_PT_lightprobe(DataButtonsPanel, Panel): bl_label = "Probe" - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER'} def draw(self, context): layout = self.layout @@ -80,6 +80,37 @@ class DATA_PT_lightprobe(DataButtonsPanel, Panel): sub.prop(probe, "clip_end", text="End") +class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel): + bl_label = "Probe" + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + probe = context.lightprobe + +# layout.prop(probe, "type") + + if probe.type == 'GRID': + col = layout.column() + + sub = col.column(align=True) + sub.prop(probe, "grid_resolution_x", text="Resolution X") + sub.prop(probe, "grid_resolution_y", text="Y") + sub.prop(probe, "grid_resolution_z", text="Z") + + col.operator("object.lightprobe_cache_bake").subset = "ACTIVE" + col.operator("object.lightprobe_cache_free") + + elif probe.type == 'PLANAR': + # Currently unsupported + pass + else: + # Currently unsupported + pass + + class DATA_PT_lightprobe_visibility(DataButtonsPanel, Panel): bl_label = "Visibility" bl_parent_id = "DATA_PT_lightprobe" @@ -166,6 +197,7 @@ class DATA_PT_lightprobe_display(DataButtonsPanel, Panel): classes = ( DATA_PT_context_lightprobe, DATA_PT_lightprobe, + DATA_PT_lightprobe_eevee_next, DATA_PT_lightprobe_visibility, DATA_PT_lightprobe_parallax, DATA_PT_lightprobe_display, diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c3df9bb6eb8..7fd1dcad092 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -466,6 +466,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_light_iter_lib.glsl engines/eevee_next/shaders/eevee_light_lib.glsl engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl + engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl engines/eevee_next/shaders/eevee_lightprobe_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 6d644d4dd7d..6f38f0b45a0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -95,6 +95,7 @@ #define SURFEL_LIST_GROUP_SIZE 256 #define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, so 4x4x4 workgroup size. */ #define IRRADIANCE_GRID_BRICK_SIZE 4 /* In each dimension, so 4x4x4 brick size. */ +#define IRRADIANCE_BOUNDS_GROUP_SIZE 64 /* Resource bindings. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index e4d5b82e6b1..4610826f439 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -474,7 +474,6 @@ void Instance::light_bake_irradiance(Object &probe, }; custom_pipeline_wrapper([&]() { - irradiance_cache.bake.surfel_raster_views_sync(probe); /* TODO: lightprobe visibility group option. */ manager->begin_sync(); render_sync(); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 8b201d8734b..371754aa58c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -435,24 +435,23 @@ void IrradianceBake::sync() } } -void IrradianceBake::surfel_raster_views_sync(const Object &probe_object) +void IrradianceBake::surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max) { using namespace blender::math; - const float4x4 transform(probe_object.object_to_world); - float3 scale; - math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale); + grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * (scene_max - scene_min))); - grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * 2.0f * scale)); + grid_pixel_extent_ = min(grid_pixel_extent_, int3(16384)); /* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to * make any difference. The bottleneck is still the light propagation loop. */ auto sync_view = [&](View &view, CartesianBasis basis) { - float3 extent = transform_point(invert(basis), scale); + float3 extent_min = transform_point(invert(basis), scene_min); + float3 extent_max = transform_point(invert(basis), scene_max); float4x4 winmat = projection::orthographic( - -extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z); - float4x4 viewinv = math::from_loc_rot( - grid_location_, grid_orientation_ * to_quaternion(basis)); + extent_min.x, extent_max.x, extent_min.y, extent_max.y, extent_min.z, extent_max.z); + float4x4 viewinv = from_rotation(to_quaternion(basis)); + view.visibility_test(false); view.sync(invert(viewinv), winmat); }; @@ -499,20 +498,73 @@ void IrradianceBake::surfels_create(const Object &probe_object) irradiance_L1_b_tx_.clear(float4(0.0f)); irradiance_L1_c_tx_.clear(float4(0.0f)); - /* Extract bounding box. Order is arbitrary as it is not important for our usage. */ - const std::array bbox_corners({float3{+1, +1, +1}, - float3{-1, +1, +1}, - float3{+1, -1, +1}, - float3{-1, -1, +1}, - float3{+1, +1, -1}, - float3{-1, +1, -1}, - float3{+1, -1, -1}, - float3{-1, -1, -1}}); - grid_bbox_vertices.clear(); - for (const float3 &point : bbox_corners) { - grid_bbox_vertices.append(transform_point(grid_local_to_world, point)); + DRW_stats_group_start("IrradianceBake.SceneBounds"); + + { + draw::Manager &manager = *inst_.manager; + PassSimple &pass = irradiance_bounds_ps_; + pass.init(); + pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_BOUNDS)); + pass.bind_ssbo("capture_info_buf", &capture_info_buf_); + pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current()); + pass.push_constant("resource_len", int(manager.resource_handle_count())); + pass.dispatch( + int3(divide_ceil_u(manager.resource_handle_count(), IRRADIANCE_BOUNDS_GROUP_SIZE), 1, 1)); } + /* Raster the scene to query the number of surfel needed. */ + capture_info_buf_.do_surfel_count = false; + capture_info_buf_.do_surfel_output = false; + + int neg_flt_max = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */ + int pos_flt_max = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */ + capture_info_buf_.scene_bound_x_min = pos_flt_max; + capture_info_buf_.scene_bound_y_min = pos_flt_max; + capture_info_buf_.scene_bound_z_min = pos_flt_max; + capture_info_buf_.scene_bound_x_max = neg_flt_max; + capture_info_buf_.scene_bound_y_max = neg_flt_max; + capture_info_buf_.scene_bound_z_max = neg_flt_max; + + capture_info_buf_.push_update(); + + inst_.manager->submit(irradiance_bounds_ps_); + + GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE); + capture_info_buf_.read(); + + auto ordered_int_bits_to_float = [](int32_t int_value) -> float { + int32_t float_bits = (int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value; + return *reinterpret_cast(&float_bits); + }; + + float3 scene_min = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_min), + ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_min), + ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_min)); + float3 scene_max = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_max), + ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_max), + ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_max)); + /* To avoid loosing any surface to the clipping planes, add some padding. */ + float epsilon = 1.0f / surfel_density_; + scene_min -= epsilon; + scene_max += epsilon; + surfel_raster_views_sync(scene_min, scene_max); + + /* Extract bounding box. Order is arbitrary as it is not important for our usage. */ + scene_bbox_vertices_.clear(); + scene_bbox_vertices_.append(float3{scene_max.x, scene_max.y, scene_max.z}); + scene_bbox_vertices_.append(float3{scene_min.x, scene_max.y, scene_max.z}); + scene_bbox_vertices_.append(float3{scene_max.x, scene_min.y, scene_max.z}); + scene_bbox_vertices_.append(float3{scene_min.x, scene_min.y, scene_max.z}); + scene_bbox_vertices_.append(float3{scene_max.x, scene_max.y, scene_min.z}); + scene_bbox_vertices_.append(float3{scene_min.x, scene_max.y, scene_min.z}); + scene_bbox_vertices_.append(float3{scene_max.x, scene_min.y, scene_min.z}); + scene_bbox_vertices_.append(float3{scene_min.x, scene_min.y, scene_min.z}); + + DRW_stats_group_end(); + + /* WORKAROUND: Sync camera with correct bounds for light culling. */ + inst_.camera.sync(); + DRW_stats_group_start("IrradianceBake.SurfelsCount"); /* Raster the scene to query the number of surfel needed. */ @@ -570,7 +622,6 @@ void IrradianceBake::surfels_lights_eval() /* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */ inst_.render_buffers.acquire(int2(1)); inst_.lights.set_view(view_z_, grid_pixel_extent_.xy()); - /* TODO: Instead of using the volume tagging we should tag using the surfels. */ inst_.shadows.set_view(view_z_); inst_.render_buffers.release(); @@ -584,21 +635,14 @@ void IrradianceBake::propagate_light_sample() float2 rand_uv = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_FILTER_U); const float3 ray_direction = inst_.sampling.sample_hemisphere(rand_uv); const float3 up = ray_direction; - /* Find the closest axis. */ - const float3 grid_local_ray_direction = transform_point(grid_orientation_, ray_direction); - Axis closest_grid_axis = Axis::from_int(dominant_axis(grid_local_ray_direction)); - /* Use one of the other 2 grid axes to get a reference right vector. */ - Axis right_axis = AxisSigned(closest_grid_axis).next_after().axis(); - const float3 grid_right = from_rotation(grid_orientation_)[right_axis.as_int()]; - /* Create a view around the grid position with the ray direction as up axis. - * The other axes are aligned to the grid local axes to avoid to allocate too many list start. */ - const float4x4 viewmat = invert( - from_orthonormal_axes(grid_location_, normalize(cross(up, grid_right)), up)); + const float3 forward = cross(up, normalize(orthogonal(up))); + const float4x4 viewinv = from_orthonormal_axes(float3(0.0f), forward, up); + const float4x4 viewmat = invert(viewinv); /* Compute projection bounds. */ float2 min, max; INIT_MINMAX2(min, max); - for (const float3 &point : grid_bbox_vertices) { + for (const float3 &point : scene_bbox_vertices_) { min_max(transform_point(viewmat, point).xy(), min, max); } diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 621b5509f17..73479ae323b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -47,6 +47,8 @@ class IrradianceBake { PassSimple surfel_light_bounce_ps_ = {"LightBounce"}; /** Capture surfel lighting to irradiance samples. */ PassSimple irradiance_capture_ps_ = {"IrradianceCapture"}; + /** Compute scene bounding box. */ + PassSimple irradiance_bounds_ps_ = {"IrradianceBounds"}; /** * Basis orientation for each baking projection. @@ -82,12 +84,8 @@ class IrradianceBake { Texture irradiance_L1_b_tx_ = {"irradiance_L1_b_tx_"}; Texture irradiance_L1_c_tx_ = {"irradiance_L1_c_tx_"}; - /* Orientation of the irradiance grid being baked. */ - math::Quaternion grid_orientation_; - /* Object center of the irradiance grid being baked. */ - float3 grid_location_; /* Bounding box vertices of the irradiance grid being baked. In world space. */ - Vector grid_bbox_vertices; + Vector scene_bbox_vertices_; /* Surfel per unit distance. */ float surfel_density_ = 2.0f; @@ -97,7 +95,7 @@ class IrradianceBake { void sync(); /** Create the views used to rasterize the scene into surfel representation. */ - void surfel_raster_views_sync(const Object &probe_object); + void surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max); /** Create a surfel representation of the scene from the probe using the capture pipeline. */ void surfels_create(const Object &probe_object); /** Evaluate direct lighting (and also clear the surfels radiance). */ diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 41189897379..e219c15bb33 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -148,6 +148,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_light_culling_tile"; case LIGHT_CULLING_ZBIN: return "eevee_light_culling_zbin"; + case LIGHTPROBE_IRRADIANCE_BOUNDS: + return "eevee_lightprobe_irradiance_bounds"; case LIGHTPROBE_IRRADIANCE_RAY: return "eevee_lightprobe_irradiance_ray"; case LIGHTPROBE_IRRADIANCE_LOAD: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 2775d15f7d1..367ba3764a2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -63,6 +63,7 @@ enum eShaderType { LIGHT_CULLING_TILE, LIGHT_CULLING_ZBIN, + LIGHTPROBE_IRRADIANCE_BOUNDS, LIGHTPROBE_IRRADIANCE_RAY, LIGHTPROBE_IRRADIANCE_LOAD, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 8df488d8aed..6179b4a1048 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -879,6 +879,16 @@ struct CaptureInfoData { /** Transform vectors from world space to local space. Does not have location component. */ /** TODO(fclem): This could be a float3x4 or a float3x3 if padded correctly. */ float4x4 irradiance_grid_world_to_local_rotation; + /** Scene bounds. Stored as min & max and as int for atomic operations. */ + int scene_bound_x_min; + int scene_bound_y_min; + int scene_bound_z_min; + int scene_bound_x_max; + int scene_bound_y_max; + int scene_bound_z_max; + int _pad0; + int _pad1; + int _pad2; }; BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl new file mode 100644 index 00000000000..03f18c36b05 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl @@ -0,0 +1,48 @@ + +/** + * Surface Capture: Output surface parameters to diverse storage. + * + * The resources expected to be defined are: + * - capture_info_buf + */ + +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl) + +void main() +{ + uint index = gl_GlobalInvocationID.x; + if (index >= resource_len) { + return; + } + + ObjectBounds bounds = bounds_buf[index]; + /* Bounds are not correct as culling is disabled for these. */ + if (bounds._inner_sphere_radius <= 0.0) { + return; + } + + IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz, + bounds.bounding_corners[1].xyz, + bounds.bounding_corners[2].xyz, + bounds.bounding_corners[3].xyz); + + vec3 local_min = vec3(FLT_MAX); + vec3 local_max = vec3(-FLT_MAX); + for (int i = 0; i < 8; i++) { + local_min = min(local_min, box.corners[i].xyz); + local_max = max(local_max, box.corners[i].xyz); + } + + atomicMin(capture_info_buf.scene_bound_x_min, floatBitsToOrderedInt(local_min.x)); + atomicMax(capture_info_buf.scene_bound_x_max, floatBitsToOrderedInt(local_max.x)); + + atomicMin(capture_info_buf.scene_bound_y_min, floatBitsToOrderedInt(local_min.y)); + atomicMax(capture_info_buf.scene_bound_y_max, floatBitsToOrderedInt(local_max.y)); + + atomicMin(capture_info_buf.scene_bound_z_min, floatBitsToOrderedInt(local_min.z)); + atomicMax(capture_info_buf.scene_bound_z_max, floatBitsToOrderedInt(local_max.z)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 321ac377d8a..6434b795c51 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -91,6 +91,16 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray) .compute_source("eevee_surfel_ray_comp.glsl") .do_static_compilation(true); +GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_bounds) + .do_static_compilation(true) + .local_group_size(IRRADIANCE_BOUNDS_GROUP_SIZE) + .storage_buf(0, Qualifier::READ_WRITE, "CaptureInfoData", "capture_info_buf") + .storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") + .push_constant(Type::INT, "resource_len") + .typedef_source("draw_shader_shared.h") + .additional_info("eevee_shared") + .compute_source("eevee_lightprobe_irradiance_bounds_comp.glsl"); + GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) .local_group_size(IRRADIANCE_GRID_GROUP_SIZE, IRRADIANCE_GRID_GROUP_SIZE, diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index b25caff4e9b..11b74c32375 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -149,6 +149,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) GPU_SHADER_CREATE_INFO(eevee_surf_capture) .vertex_out(eevee_surf_iface) + .define("MAT_CAPTURE") .storage_buf(SURFEL_BUF_SLOT, Qualifier::WRITE, "Surfel", "surfel_buf[]") .storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ_WRITE, "CaptureInfoData", "capture_info_buf") .fragment_source("eevee_surf_capture_frag.glsl") diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh index aa263d4d82a..4c44afb30de 100644 --- a/source/blender/draw/intern/draw_manager.hh +++ b/source/blender/draw/intern/draw_manager.hh @@ -178,6 +178,14 @@ class Manager { acquired_textures.append(texture); } + /** + * Return the number of resource handles allocated. + */ + uint resource_handle_count() const + { + return resource_len_; + } + /** TODO(fclem): The following should become private at some point. */ void begin_sync(); void end_sync(); -- 2.30.2 From 95b8b71908249eee1f8ccb633541147c040d840d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 30 Apr 2023 21:35:42 +0200 Subject: [PATCH 36/58] EEVEE-Next: Irradiance Cache: Reserve slot for world irradiance This doesn't implement the world irradiance extraction process, but only reserve the storage for it. --- .../eevee_next/eevee_irradiance_cache.cc | 32 +++++++++++++++---- .../eevee_next/eevee_irradiance_cache.hh | 2 ++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 371754aa58c..7a2ac8c7fdb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -50,10 +50,20 @@ void IrradianceCache::init() brick_pool_.clear(); /* Fill with all the available bricks. */ for (auto i : IndexRange(atlas_row_count * atlas_col_count)) { - IrradianceBrick brick; - brick.atlas_coord = uint2(i % atlas_col_count, i / atlas_col_count) * - IRRADIANCE_GRID_BRICK_SIZE; - brick_pool_.append(irradiance_brick_pack(brick)); + if (i == 0) { + /* Reserve one brick for the world. */ + world_brick_index_ = 0; + } + else { + IrradianceBrick brick; + brick.atlas_coord = uint2(i % atlas_col_count, i / atlas_col_count) * + IRRADIANCE_GRID_BRICK_SIZE; + brick_pool_.append(irradiance_brick_pack(brick)); + } + } + + if (irradiance_atlas_tx_.is_valid()) { + irradiance_atlas_tx_.clear(float4(0.0f)); } } @@ -117,7 +127,8 @@ void IrradianceCache::set_view(View & /*view*/) /* TODO frustum cull and only load visible grids. */ - if (grids_len >= IRRADIANCE_GRID_MAX) { + /* Note that we reserve 1 slot for the world irradiance. */ + if (grids_len >= IRRADIANCE_GRID_MAX - 1) { inst_.info = "Error: Too many grid visible"; continue; } @@ -153,8 +164,17 @@ void IrradianceCache::set_view(View & /*view*/) grid.grid_index = grids_len; grids_infos_buf_[grids_len++] = grid; } + /* TODO: Stable sorting of grids. */ - /* TODO(fclem): Insert world grid here. */ + { + /* Insert world grid last. */ + IrradianceGridData grid; + grid.world_to_grid_transposed = float3x4::identity(); + grid.grid_size = int3(1); + grid.brick_offset = bricks_infos_buf_.size(); + grids_infos_buf_[grids_len++] = grid; + bricks_infos_buf_.append(world_brick_index_); + } if (grids_len < IRRADIANCE_GRID_MAX) { /* Tag last grid as invalid to stop the iteration. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 73479ae323b..38d54e10618 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -128,6 +128,8 @@ class IrradianceCache { /** Atlas 3D texture containing all loaded grid data. */ Texture irradiance_atlas_tx_ = {"irradiance_atlas_tx_"}; + /** Reserved atlas brick for world irradiance. */ + int world_brick_index_ = 0; /** Data structure used to index irradiance cache pages inside the atlas. */ IrradianceGridDataBuf grids_infos_buf_ = {"grids_infos_buf_"}; IrradianceBrickBuf bricks_infos_buf_ = {"bricks_infos_buf_"}; -- 2.30.2 From 502d9db82c74df563094a451959be020e6e9b7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 30 Apr 2023 22:02:56 +0200 Subject: [PATCH 37/58] EEVEE-Next: Irradiance Cache: Add support for cancelling operator --- .../draw/engines/eevee_next/eevee_instance.cc | 5 +++++ .../draw/engines/eevee_next/eevee_instance.hh | 1 + .../draw/engines/eevee_next/eevee_lightcache.cc | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 4610826f439..3a5ac7d9206 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -455,6 +455,7 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view void Instance::light_bake_irradiance(Object &probe, std::function context_enable, std::function context_disable, + std::function stop, std::function result_update) { BLI_assert(is_baking()); @@ -509,6 +510,10 @@ void Instance::light_bake_irradiance(Object &probe, } result_update(cache_frame); }); + + if (stop()) { + return; + } } } } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index bcb1ace04f5..5d11d982e04 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -149,6 +149,7 @@ class Instance { void light_bake_irradiance(Object &probe, std::function context_enable, std::function context_disable, + std::function stop, std::function result_update); static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index 388d0e0eb38..716da5360c0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -138,8 +138,6 @@ class LightBake { */ void run(bool *stop = nullptr, bool *do_update = nullptr, float *progress = nullptr) { - UNUSED_VARS(stop, do_update, progress); - DEG_graph_relations_update(depsgraph_); DEG_evaluate_on_framechange(depsgraph_, frame_); @@ -160,6 +158,7 @@ class LightBake { *eval_ob, [this]() { context_enable(); }, [this]() { context_disable(); }, + [&]() { return (G.is_break == true) || ((stop != nullptr) ? *stop : false); }, [&](LightProbeGridCacheFrame *cache_frame) { { std::scoped_lock lock(result_mutex_); @@ -169,9 +168,19 @@ class LightBake { } bake_result_[i] = cache_frame; } - *do_update = true; - /* TODO: Update progress. */ + + if (do_update) { + *do_update = true; + } + + if (progress) { + /* TODO: Update progress. */ + } }); + + if ((G.is_break == true) || ((stop != nullptr && *stop == true))) { + break; + } } delete_resources(); -- 2.30.2 From e6d1a42d12bb3f858fb6dcba8b1d22e9541ab396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 30 Apr 2023 22:06:59 +0200 Subject: [PATCH 38/58] EEVEE-Next: Irradiance Cache: Add version control for new parameter --- source/blender/blenloader/intern/versioning_400.cc | 12 +++++++++--- source/blender/makesdna/DNA_scene_defaults.h | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 1fc1e0146ff..72d41fe3fbb 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -8,11 +8,12 @@ #include "CLG_log.h" -#include "DNA_movieclip_types.h" - #include "BLI_assert.h" #include "BLI_listbase.h" +#include "DNA_genfile.h" +#include "DNA_movieclip_types.h" + #include "BKE_main.h" #include "BKE_mesh_legacy_convert.h" #include "BKE_tracking.h" @@ -90,7 +91,7 @@ static void version_movieclips_legacy_camera_object(Main *bmain) } } -void blo_do_versions_400(FileData * /*fd*/, Library * /*lib*/, Main *bmain) +void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) { // if (!MAIN_VERSION_ATLEAST(bmain, 400, 0)) { /* This is done here because we will continue to write with the old format until 4.0, so we need @@ -112,6 +113,11 @@ void blo_do_versions_400(FileData * /*fd*/, Library * /*lib*/, Main *bmain) * \note Keep this message at the bottom of the function. */ { + if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "int", "gi_irradiance_samples")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->eevee.gi_irradiance_samples = 512; + } + } /* Keep this block, even when empty. */ } } diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index f6b77aede0d..9c874d4162f 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -163,7 +163,7 @@ #define _DNA_DEFAULT_SceneEEVEE \ { \ .gi_diffuse_bounces = 3, \ - .gi_irradiance_samples = 128, \ + .gi_irradiance_samples = 512, \ .gi_cubemap_resolution = 512, \ .gi_visibility_resolution = 32, \ .gi_cubemap_draw_size = 0.3f, \ -- 2.30.2 From 12fc600d7f807afcbf776ff32720611e43c4dbd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Fri, 5 May 2023 16:14:11 +0200 Subject: [PATCH 39/58] EEVEE-Next: Fix compilation error with Metal --- .../shaders/eevee_display_probe_grid_vert.glsl | 11 +++++------ .../eevee_next/shaders/eevee_lightprobe_eval_lib.glsl | 2 +- .../shaders/eevee_lightprobe_irradiance_ray_comp.glsl | 6 +++--- .../eevee_next/shaders/eevee_lightprobe_lib.glsl | 4 ++-- .../shaders/eevee_shadow_tag_usage_lib.glsl | 4 ++-- .../eevee_next/shaders/eevee_surfel_bounce_comp.glsl | 2 +- .../shaders/eevee_surfel_list_build_comp.glsl | 2 +- .../shaders/eevee_surfel_list_sort_comp.glsl | 2 +- .../eevee_next/shaders/eevee_surfel_ray_comp.glsl | 4 ++-- .../eevee_next/shaders/infos/eevee_shadow_info.hh | 3 ++- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl index 18b15cbbb1d..5f507aef3e0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_vert.glsl @@ -17,14 +17,13 @@ void main() lP = pos[gl_VertexID % 6]; int cell_index = gl_VertexID / 6; - ivec3 grid_resolution = textureSize(irradiance_a_tx, 0); + ivec3 grid_res = grid_resolution; - cell = ivec3(cell_index / (grid_resolution.z * grid_resolution.y), - (cell_index / grid_resolution.z) % grid_resolution.y, - cell_index % grid_resolution.z); + cell = ivec3(cell_index / (grid_res.z * grid_res.y), + (cell_index / grid_res.z) % grid_res.y, + cell_index % grid_res.z); - vec3 ws_cell_pos = lightprobe_irradiance_grid_sample_position( - grid_to_world, grid_resolution, cell); + vec3 ws_cell_pos = lightprobe_irradiance_grid_sample_position(grid_to_world, grid_res, cell); vec3 vs_offset = vec3(lP, 0.0) * sphere_radius; vec3 vP = (ViewMatrix * vec4(ws_cell_pos, 1.0)).xyz + vs_offset; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl index 901ddb44601..d278936a1e9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl @@ -25,7 +25,7 @@ vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 l int brick_index = lightprobe_irradiance_grid_brick_index_get(grid_data, brick_coord); IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]); - vec3 output_coord = vec3(brick.atlas_coord, 0.0) + brick_lP; + vec3 output_coord = vec3(vec2(brick.atlas_coord), 0.0) + brick_lP; return output_coord; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl index c82033c4db4..a304afd160c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -33,12 +33,12 @@ void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 void main() { - if (any(greaterThanEqual(gl_GlobalInvocationID, capture_info_buf.irradiance_grid_size))) { + ivec3 grid_coord = ivec3(gl_GlobalInvocationID); + + if (any(greaterThanEqual(grid_coord, capture_info_buf.irradiance_grid_size))) { return; } - ivec3 grid_coord = ivec3(gl_GlobalInvocationID); - vec3 P = lightprobe_irradiance_grid_sample_position( capture_info_buf.irradiance_grid_local_to_world, capture_info_buf.irradiance_grid_size, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl index ccfaa025fb8..139c744386d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl @@ -2,10 +2,10 @@ #pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world, - ivec3 grid_resolution, + ivec3 grid_res, ivec3 cell_coord) { - vec3 ls_cell_pos = (vec3(cell_coord) + vec3(0.5)) / vec3(grid_resolution); + vec3 ls_cell_pos = (vec3(cell_coord) + vec3(0.5)) / vec3(grid_res); ls_cell_pos = ls_cell_pos * 2.0 - 1.0; vec3 ws_cell_pos = (grid_local_to_world * vec4(ls_cell_pos, 1.0)).xyz; return ws_cell_pos; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index f4f2f464fb2..caec6764dda 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -187,12 +187,12 @@ void shadow_tag_usage(vec3 vP, vec3 P, vec2 pixel) shadow_tag_usage(vP, P, vec3(0), 0, dist_to_cam, pixel); } -void shadow_tag_usage_surfel(Surfel surfel, int directional_level) +void shadow_tag_usage_surfel(Surfel surfel, int directional_lvl) { vec3 P = surfel.position; LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { - shadow_tag_usage_tilemap_directional_at_level(l_idx, P, directional_level); + shadow_tag_usage_tilemap_directional_at_level(l_idx, P, directional_lvl); } LIGHT_FOREACH_END diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl index 77bcb65fb7f..3634f81196c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl @@ -21,7 +21,7 @@ vec3 finalize_integration(vec4 radiance) void main() { - int surfel_index = int(gl_GlobalInvocationID); + int surfel_index = int(gl_GlobalInvocationID.x); if (surfel_index >= capture_info_buf.surfel_len) { return; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl index 0ce97ca0b1d..e6dbae33339 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl @@ -16,7 +16,7 @@ void main() { - int surfel_index = int(gl_GlobalInvocationID); + int surfel_index = int(gl_GlobalInvocationID.x); if (surfel_index >= capture_info_buf.surfel_len) { return; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl index b454ff4ec5e..068b8d322f1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl @@ -74,7 +74,7 @@ bool is_valid_surfel_link(int a, int b) void main() { - int list_index = int(gl_GlobalInvocationID); + int list_index = int(gl_GlobalInvocationID.x); if (list_index >= list_info_buf.list_max) { return; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index 712fa9252f9..040f1b61d73 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -40,8 +40,8 @@ void radiance_transfer(inout Surfel surfel, Surfel surfel_emitter) void main() { - int surfel_index = int(gl_GlobalInvocationID); - if (surfel_index >= capture_info_buf.surfel_len) { + int surfel_index = int(gl_GlobalInvocationID.x); + if (surfel_index >= int(capture_info_buf.surfel_len)) { return; } diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh index ce47a2b3dd5..b4a0defd109 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh @@ -69,7 +69,8 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_surfels) .do_static_compilation(true) .local_group_size(SURFEL_GROUP_SIZE) .storage_buf(6, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]") - .storage_buf(7, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]") + /* ShadowTileDataPacked is uint. But MSL translation need the real type. */ + .storage_buf(7, Qualifier::READ_WRITE, "uint", "tiles_buf[]") .push_constant(Type::INT, "directional_level") .push_constant(Type::FLOAT, "tilemap_projection_ratio") .additional_info("eevee_shared", -- 2.30.2 From 487ab2073bd2ca6f725c50f58fc61c6607167bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Sun, 7 May 2023 16:06:04 +0200 Subject: [PATCH 40/58] EEVEE-Next: Irradiance Bake: Avoid asserts on Metal This avoid triggering the asserts in place in the Metal backend. However it does not work yet. --- .../draw/engines/eevee_next/eevee_irradiance_cache.cc | 8 +++++--- .../blender/draw/engines/eevee_next/eevee_lightcache.cc | 8 +++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 7a2ac8c7fdb..3ee480e5ab7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -39,7 +39,8 @@ void IrradianceCache::init() int atlas_row_count = divide_ceil_u(atlas_byte_size, row_byte_size); atlas_extent.y *= atlas_row_count; - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ; + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_ATTACHMENT; do_full_update_ = irradiance_atlas_tx_.ensure_3d(GPU_RGBA16F, atlas_extent, usage); if (do_full_update_) { @@ -505,7 +506,7 @@ void IrradianceBake::surfels_create(const Object &probe_object) (2 * inst_.sampling.sample_count()); eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | - GPU_TEXTURE_USAGE_HOST_READ; + GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_ATTACHMENT; /* 32bit float is needed here otherwise we loose too much energy from rounding error during the * accumulation when the sample count is above 500. */ @@ -706,7 +707,8 @@ void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame) { if (!ELEM(inst_.debug_mode, eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL, - eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) { + eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) + { return; } diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index 716da5360c0..a4d49ed69e3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -58,9 +58,6 @@ class LightBake { /** Baking instance. Created and freed in the worker thread. */ Instance *instance_ = nullptr; - /** Light Cache being baked. Create in worker thread and pass ownership to original scene on - * first `update()` call. */ - ::LightCache *light_cache_ = nullptr; /** Manager used for command submission. Created and freed in the worker thread. */ draw::Manager *manager_ = nullptr; @@ -216,20 +213,21 @@ class LightBake { void context_disable() { - GPU_render_end(); - if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { /* Reuse main draw context. */ DRW_opengl_context_disable(); + GPU_render_end(); GPU_context_main_unlock(); } else if (gl_context_ == nullptr) { /* Main thread case. */ DRW_opengl_context_disable(); + GPU_render_end(); } else { /* Worker thread case. */ DRW_gpu_render_context_disable(gpu_context_); + GPU_render_end(); DRW_opengl_render_context_disable(gl_context_); } } -- 2.30.2 From 4ad77fcc74c90bca593984c42efd82f813f386a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 24 May 2023 22:51:44 +0200 Subject: [PATCH 41/58] EEVEE-Next: Add progress to irradiance bake --- .../draw/engines/eevee_next/eevee_instance.cc | 16 ++++++++++------ .../draw/engines/eevee_next/eevee_instance.hh | 11 ++++++----- .../draw/engines/eevee_next/eevee_lightcache.cc | 4 ++-- .../draw/engines/eevee_next/eevee_sampling.hh | 7 +++++++ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 5eb2932b5c3..9bcad8f4394 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -452,11 +452,12 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL); } -void Instance::light_bake_irradiance(Object &probe, - std::function context_enable, - std::function context_disable, - std::function stop, - std::function result_update) +void Instance::light_bake_irradiance( + Object &probe, + std::function context_enable, + std::function context_disable, + std::function stop, + std::function result_update) { BLI_assert(is_baking()); @@ -508,7 +509,10 @@ void Instance::light_bake_irradiance(Object &probe, else { cache_frame = irradiance_cache.bake.read_result_packed(); } - result_update(cache_frame); + + float bounce_progress = sampling.sample_index() / float(sampling.sample_count()); + float progress = (bounce - 1 + bounce_progress) / float(bounce_len); + result_update(cache_frame, progress); }); if (stop()) { diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 5d11d982e04..b61ab2eb37d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -146,11 +146,12 @@ class Instance { /* Light bake. */ void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager); - void light_bake_irradiance(Object &probe, - std::function context_enable, - std::function context_disable, - std::function stop, - std::function result_update); + void light_bake_irradiance( + Object &probe, + std::function context_enable, + std::function context_disable, + std::function stop, + std::function result_update); static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer); diff --git a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc index a4d49ed69e3..4961e502006 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightcache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightcache.cc @@ -156,7 +156,7 @@ class LightBake { [this]() { context_enable(); }, [this]() { context_disable(); }, [&]() { return (G.is_break == true) || ((stop != nullptr) ? *stop : false); }, - [&](LightProbeGridCacheFrame *cache_frame) { + [&](LightProbeGridCacheFrame *cache_frame, float grid_progress) { { std::scoped_lock lock(result_mutex_); /* Delete any existing cache that wasn't transferred to the original object. */ @@ -171,7 +171,7 @@ class LightBake { } if (progress) { - /* TODO: Update progress. */ + *progress = (i + grid_progress) / original_probes_.size(); } }); diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh index 372618521cc..3e1b8fed983 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh @@ -129,11 +129,18 @@ class Sampling { return interactive_mode_; } + /* Target sample count. */ uint64_t sample_count() const { return sample_count_; } + /* 0 based current sample. Might not increase sequentially in viewport. */ + uint64_t sample_index() const + { + return sample_; + } + /* Return true if we are starting a new motion blur step. We need to run sync again since * depsgraph was updated by MotionBlur::step(). */ bool do_render_sync() const -- 2.30.2 From 1be9514e8b1a0568e7737b2967193f014a632773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 25 May 2023 01:26:51 +0200 Subject: [PATCH 42/58] EEVEE-Next: Make progressive accumulation normalized --- .../draw/engines/eevee_next/eevee_instance.cc | 19 +++++++--- .../eevee_next/eevee_irradiance_cache.cc | 36 +++++++++++++------ .../eevee_next/eevee_irradiance_cache.hh | 11 +++++- .../engines/eevee_next/eevee_shader_shared.hh | 8 ++--- .../eevee_lightprobe_irradiance_ray_comp.glsl | 22 ++++++++++-- 5 files changed, 73 insertions(+), 23 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 9bcad8f4394..b90b58e6d95 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -486,8 +486,10 @@ void Instance::light_bake_irradiance( }); int bounce_len = scene->eevee.gi_diffuse_bounces; - /* Start at bounce 1 as 0 bounce is no indirect lighting. */ - for (int bounce = 1; bounce <= bounce_len; bounce++) { + for (int bounce = 0; bounce <= bounce_len; bounce++) { + /* Last iteration only captures lighting. */ + const bool is_last_bounce = (bounce == bounce_len); + sampling.init(scene); while (!sampling.finished()) { context_wrapper([&]() { @@ -495,9 +497,16 @@ void Instance::light_bake_irradiance( * the update function & context switch. */ for (int i = 0; i < 16 && !sampling.finished(); i++) { sampling.step(); - irradiance_cache.bake.propagate_light_sample(); + + irradiance_cache.bake.raylists_build(); + if (!is_last_bounce) { + irradiance_cache.bake.propagate_light(); + } + if (is_last_bounce) { + irradiance_cache.bake.irradiance_capture(); + } } - if (sampling.finished()) { + if (sampling.finished() && !is_last_bounce) { irradiance_cache.bake.accumulate_bounce(); } @@ -511,7 +520,7 @@ void Instance::light_bake_irradiance( } float bounce_progress = sampling.sample_index() / float(sampling.sample_count()); - float progress = (bounce - 1 + bounce_progress) / float(bounce_len); + float progress = (bounce + bounce_progress) / float(bounce_len + 1); result_update(cache_frame, progress); }); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 3ee480e5ab7..8194d884f06 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -399,7 +399,7 @@ void IrradianceBake::sync() pass.dispatch(&dispatch_per_surfel_); } { - PassSimple &pass = surfel_light_propagate_ps_; + PassSimple &pass = surfel_ray_build_ps_; pass.init(); { PassSimple::Sub &sub = pass.sub("ListBuild"); @@ -421,6 +421,10 @@ void IrradianceBake::sync() sub.barrier(GPU_BARRIER_SHADER_STORAGE); sub.dispatch(&dispatch_per_list_); } + } + { + PassSimple &pass = surfel_light_propagate_ps_; + pass.init(); { PassSimple::Sub &sub = pass.sub("RayEval"); sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY)); @@ -500,10 +504,7 @@ void IrradianceBake::surfels_create(const Object &probe_object) capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world; capture_info_buf_.irradiance_grid_world_to_local_rotation = float4x4( (invert(normalize(float3x3(grid_local_to_world))))); - capture_info_buf_.irradiance_accum_solid_angle = 0.0f; - /* Divide by twice the sample count because each ray is evaluated in both directions. */ - capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) / - (2 * inst_.sampling.sample_count()); + capture_info_buf_.irradiance_accum_sample_count = 0; eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_ATTACHMENT; @@ -633,6 +634,8 @@ void IrradianceBake::surfels_create(const Object &probe_object) /* Sync with any other following pass using the surfel buffer. */ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + /* Read back so that following push_update will contain correct surfel count. */ + capture_info_buf_.read(); DRW_stats_group_end(); } @@ -649,7 +652,7 @@ void IrradianceBake::surfels_lights_eval() inst_.manager->submit(surfel_light_eval_ps_, view_z_); } -void IrradianceBake::propagate_light_sample() +void IrradianceBake::raylists_build() { using namespace blender::math; @@ -670,8 +673,7 @@ void IrradianceBake::propagate_light_sample() /* NOTE: Z values do not really matter since we are not doing any rasterization. */ const float4x4 winmat = projection::orthographic(min.x, max.x, min.y, max.y, 0, 1); - View ray_view = {"RayProjectionView"}; - ray_view.sync(viewmat, winmat); + ray_view_.sync(viewmat, winmat); /* This avoid light leaking by making sure that for one surface there will always be at least 1 * surfel capture inside a ray list. Since the surface with the maximum distance (after @@ -694,8 +696,22 @@ void IrradianceBake::propagate_light_sample() list_start_buf_.resize(ceil_to_multiple_u(list_info_buf_.list_max, 4)); GPU_storagebuf_clear(list_start_buf_, -1); - inst_.manager->submit(surfel_light_propagate_ps_, ray_view); - inst_.manager->submit(irradiance_capture_ps_, ray_view); + inst_.manager->submit(surfel_ray_build_ps_, ray_view_); +} + +void IrradianceBake::propagate_light() +{ + inst_.manager->submit(surfel_light_propagate_ps_, ray_view_); +} + +void IrradianceBake::irradiance_capture() +{ + capture_info_buf_.push_update(); + + inst_.manager->submit(irradiance_capture_ps_, ray_view_); + + /* Increment twice because each ray is evaluated in both directions. */ + capture_info_buf_.irradiance_accum_sample_count += 2; } void IrradianceBake::accumulate_bounce() diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index 38d54e10618..e984de61ebc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -41,6 +41,8 @@ class IrradianceBake { Framebuffer empty_raster_fb_ = {"empty_raster_fb_"}; /** Evaluate light object contribution and store result to surfel. */ PassSimple surfel_light_eval_ps_ = {"LightEval"}; + /** Create linked list of surfel to emulated raycast. */ + PassSimple surfel_ray_build_ps_ = {"RayBuild"}; /** Propagate light from surfel to surfel. */ PassSimple surfel_light_propagate_ps_ = {"LightPropagate"}; /** Start of a light bounce. Accumulate light from previous propagation. */ @@ -78,6 +80,9 @@ class IrradianceBake { /* Dispatch size for per grid sample workload. */ int3 dispatch_per_grid_sample_ = int3(1); + /** View used to flatten the surfels into surfel lists representing rays. */ + View ray_view_ = {"RayProjectionView"}; + /** Irradiance textures for baking. Only represents one grid in there. */ Texture irradiance_L0_tx_ = {"irradiance_L0_tx_"}; Texture irradiance_L1_a_tx_ = {"irradiance_L1_a_tx_"}; @@ -100,8 +105,12 @@ class IrradianceBake { void surfels_create(const Object &probe_object); /** Evaluate direct lighting (and also clear the surfels radiance). */ void surfels_lights_eval(); + /** Create a surfel lists to emulate ray-casts for the current sample random direction. */ + void raylists_build(); /** Propagate light from surfel to surfel in a random direction over the sphere. */ - void propagate_light_sample(); + void propagate_light(); + /** Store surfel irradiance inside the irradiance grid samples. */ + void irradiance_capture(); /** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */ void accumulate_bounce(); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 22c11da4673..0f2e53a390a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -870,10 +870,10 @@ struct CaptureInfoData { bool1 do_surfel_count; /** Number of surfels inside the surfel buffer or the needed len. */ uint surfel_len; - /** Solid angle subtended by a single ray sample. Equal to `4 * pi / sample_count`. */ - float irradiance_sample_solid_angle; - /** Accumulated solid angle. Should reach `4 * pi` at the end of the accumulation. */ - float irradiance_accum_solid_angle; + + float _pad3; + /** Accumulated directional samples in irradiance grid. */ + int irradiance_accum_sample_count; /** Transform of the lightprobe object. */ float4x4 irradiance_grid_local_to_world; /** Transform vectors from world space to local space. Does not have location component. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl index a304afd160c..37e5fceea0e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -18,15 +18,15 @@ void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh) { vec3 lL = transform_direction(capture_info_buf.irradiance_grid_world_to_local_rotation, L); - spherical_harmonics_encode_signal_sample( - lL, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh); + spherical_harmonics_encode_signal_sample(lL, vec4(irradiance, 1.0), sh); } void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh) { vec3 L = safe_normalize(surfel_emitter.position - P); bool facing = dot(-L, surfel_emitter.normal) > 0.0; - vec3 irradiance = facing ? surfel_emitter.radiance_front : surfel_emitter.radiance_back; + vec3 irradiance = facing ? surfel_emitter.outgoing_light_front : + surfel_emitter.outgoing_light_back; irradiance_capture(L, irradiance, sh); } @@ -67,6 +67,15 @@ void main() sh.L1.M0 = imageLoad(irradiance_L1_b_img, grid_coord); sh.L1.Mp1 = imageLoad(irradiance_L1_c_img, grid_coord); + /* Spherical harmonics need to be weighted by sphere area. */ + const float sphere_area = 4.0 * M_PI; + /* Un-normalize for accumulation. */ + float weight_captured = float(capture_info_buf.irradiance_accum_sample_count) / sphere_area; + sh.L0.M0 *= weight_captured; + sh.L1.Mn1 *= weight_captured; + sh.L1.M0 *= weight_captured; + sh.L1.Mp1 *= weight_captured; + if (surfel_next > -1) { irradiance_capture(surfel_buf[surfel_next], P, sh); } @@ -83,6 +92,13 @@ void main() irradiance_capture(-sky_L, vec3(0.0), sh); } + /* Normalize for storage. */ + weight_captured += 2.0 / sphere_area; + sh.L0.M0 /= weight_captured; + sh.L1.Mn1 /= weight_captured; + sh.L1.M0 /= weight_captured; + sh.L1.Mp1 /= weight_captured; + imageStore(irradiance_L0_img, grid_coord, sh.L0.M0); imageStore(irradiance_L1_a_img, grid_coord, sh.L1.Mn1); imageStore(irradiance_L1_b_img, grid_coord, sh.L1.M0); -- 2.30.2 From 36a6a020507ce94ad31d61f4abb73975a5c17d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 25 May 2023 17:05:16 +0200 Subject: [PATCH 43/58] EEVEE-Next: Make irradiance grid composition stable --- .../eevee_next/eevee_irradiance_cache.cc | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 8194d884f06..428a51ce6bc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -165,7 +165,28 @@ void IrradianceCache::set_view(View & /*view*/) grid.grid_index = grids_len; grids_infos_buf_[grids_len++] = grid; } - /* TODO: Stable sorting of grids. */ + + { + /* Stable sorting of grids. */ + MutableSpan grid_span(grids_infos_buf_.data(), grids_len); + + std::sort(grid_span.begin(), + grid_span.end(), + [](const IrradianceGridData &a, const IrradianceGridData &b) { + float volume_a = math::determinant(float3x3(a.world_to_grid_transposed)); + float volume_b = math::determinant(float3x3(b.world_to_grid_transposed)); + if (volume_a != volume_b) { + /* Smallest first. */ + return volume_a > volume_b; + } + /* Volumes are identical. Any arbitrary criteria can be used to sort them. + * Use position to avoid unstable result caused by depsgraph non deterministic eval + * order. This could also become a parameter. */ + return a.world_to_grid_transposed[0][0] < a.world_to_grid_transposed[0][0] || + a.world_to_grid_transposed[0][1] < a.world_to_grid_transposed[0][1] || + a.world_to_grid_transposed[0][2] < a.world_to_grid_transposed[0][2]; + }); + } { /* Insert world grid last. */ -- 2.30.2 From 9ef8e9ac6b6c82beccf4db15702575b88f2a749f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 25 May 2023 18:12:25 +0200 Subject: [PATCH 44/58] EEVEE-Next: Fix surfel creation view --- .../blender/draw/engines/eevee_next/eevee_irradiance_cache.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 428a51ce6bc..a00c579dcf2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -495,7 +495,7 @@ void IrradianceBake::surfel_raster_views_sync(const float3 &scene_min, const flo float3 extent_min = transform_point(invert(basis), scene_min); float3 extent_max = transform_point(invert(basis), scene_max); float4x4 winmat = projection::orthographic( - extent_min.x, extent_max.x, extent_min.y, extent_max.y, extent_min.z, extent_max.z); + extent_min.x, extent_max.x, extent_min.y, extent_max.y, -extent_min.z, -extent_max.z); float4x4 viewinv = from_rotation(to_quaternion(basis)); view.visibility_test(false); view.sync(invert(viewinv), winmat); -- 2.30.2 From befd3465d60ffd6f293f33d0bd025734e6ad6388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 26 May 2023 00:42:54 +0200 Subject: [PATCH 45/58] EEVEE-Next: Add spherical harmonic de-ringing --- .../eevee_display_probe_grid_frag.glsl | 3 +- .../eevee_spherical_harmonics_lib.glsl | 198 ++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl index fbaca7ad04f..93b16740459 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_display_probe_grid_frag.glsl @@ -1,6 +1,7 @@ + +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) void main() { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index 0f82b34a366..c30c240308d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) + /* -------------------------------------------------------------------- */ /** \name Spherical Harmonics Functions * @@ -291,3 +293,199 @@ void spherical_harmonics_pack(SphericalHarmonicL1 sh, } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deringing + * + * Adapted from Google's Filament under Apache 2.0 licence. + * https://github.com/google/filament + * + * SH from environment with high dynamic range (or high frequencies -- high dynamic range creates + * high frequencies) exhibit "ringing" and negative values when reconstructed. + * To mitigate this, we need to low-pass the input image -- or equivalently window the SH by + * coefficient that tapper towards zero with the band. + * + * "Stupid Spherical Harmonics (SH)" + * "Deringing Spherical Harmonics" + * by Peter-Pike Sloan + * https://www.ppsloan.org/publications/shdering.pdf + * + * \{ */ + +float spherical_harmonics_sinc_window(const int l, float w) +{ + if (l == 0) { + return 1.0; + } + else if (float(l) >= w) { + return 0.0; + } + + /* We use a sinc window scaled to the desired window size in bands units a sinc window only has + * zonal harmonics. */ + float x = (M_PI * float(l)) / w; + x = sin(x) / x; + + /* The convolution of a SH function f and a ZH function h is just the product of both + * scaled by 1 / K(0,l) -- the window coefficients include this scale factor. */ + + /* Taking the window to power N is equivalent to applying the filter N times. */ + return pow4f(x); +} + +void spherical_harmonics_apply_windowing(inout SphericalHarmonicL2 sh, float cutoff) +{ + float w_l0 = spherical_harmonics_sinc_window(0, cutoff); + sh.L0.M0 *= w_l0; + + float w_l1 = spherical_harmonics_sinc_window(1, cutoff); + sh.L1.Mn1 *= w_l1; + sh.L1.M0 *= w_l1; + sh.L1.Mp1 *= w_l1; + + float w_l2 = spherical_harmonics_sinc_window(2, cutoff); + sh.L2.Mn2 *= w_l2; + sh.L2.Mn1 *= w_l2; + sh.L2.M0 *= w_l2; + sh.L2.Mp1 *= w_l2; + sh.L2.Mp2 *= w_l2; +} + +/** + * Find the minimum of the Spherical harmonic function. + * Section 2.3 from the paper. + * Only work on a single channel at a time. + */ +float spherical_harmonics_find_minimum(SphericalHarmonicL2 sh, int channel) +{ + const int comp = channel; + const float coef_L0_M0 = 0.282094792; + const float coef_L1_Mn1 = -0.488602512; + const float coef_L1_M0 = 0.488602512; + const float coef_L1_Mp1 = -0.488602512; + const float coef_L2_Mn2 = 1.092548431; + const float coef_L2_Mn1 = -1.092548431; + const float coef_L2_M0 = 0.315391565; + const float coef_L2_Mp1 = -1.092548431; + const float coef_L2_Mp2 = 0.546274215; + + /* Align the SH Z axis with the optimal linear direction. */ + vec3 z_axis = normalize(vec3(sh.L1.Mp1[comp], sh.L1.Mn1[comp], -sh.L1.M0[comp])); + vec3 x_axis = normalize(cross(z_axis, vec3(0.0, 1.0, 0.0))); + vec3 y_axis = cross(x_axis, z_axis); + mat3x3 rotation = transpose(mat3x3(x_axis, y_axis, -z_axis)); + + spherical_harmonics_L1_rotate(rotation, sh.L1); + + /* 2.3.2: Find the min for |m| = 2: + * + * Peter-Pike Sloan shows that the minimum can be expressed as a function + * of z such as: m2min = -m2_max * (1 - z^2) = m2_max * z^2 - m2_max + * with m2_max = A[8] * sqrt(f[8] * f[8] + f[4] * f[4]); + * We can therefore include this in the ZH min computation (which is function of z^2 as well). + */ + float m2_max = coef_L2_Mp2 * sqrt(square_f(sh.L2.Mp2[comp]) + square_f(sh.L2.Mn2[comp])); + + /* 2.3.1: Find the min of the zonal harmonics: + * + * This comes from minimizing the function: + * ZH(z) = (coef_L0_M0 * sh.L0.M0) + * + (coef_L1_M0 * sh.L1.M0) * z + * + (coef_L2_M0 * sh.L2.M0) * (3 * sqr(s.z) - 1) + * + * We do that by finding where it's derivative d/dz is zero: + * dZH(z)/dz = a * z^2 + b * z + c + * which is zero for z = -b / 2 * a + * + * We also needs to check that -1 < z < 1, otherwise the min is either in z = -1 or 1 + */ + float a = 3.0 * coef_L2_M0 * sh.L2.M0[comp] + m2_max; + float b = coef_L1_M0 * sh.L1.M0[comp]; + float c = coef_L0_M0 * sh.L0.M0[comp] - coef_L2_M0 * sh.L2.M0[comp] - m2_max; + + float z_min = -b / (2.0 * a); + float m0_min_z = a * pow2f(z_min) + b * z_min + c; + float m0_min_b = min(a + b + c, a - b + c); + + float m0_min = (a > 0.0 && z_min >= -1.0 && z_min <= 1.0) ? m0_min_z : m0_min_b; + + /* 2.3.3: Find the min for l = 2, |m| = 1: + * + * Note l = 1, |m| = 1 is guaranteed to be 0 because of the rotation step + * + * The function considered is: + * Y(x, y, z) = A[5] * f[5] * s.y * s.z + * + A[7] * f[7] * s.z * s.x + */ + float d = coef_L2_Mn2 * sqrt(square_f(sh.L2.Mp1[comp]) + square_f(sh.L2.Mn1[comp])); + + /* The |m|=1 function is minimal in -0.5 -- use that to skip the Newton's loop when possible. */ + float minimum = m0_min - 0.5 * d; + if (minimum < 0) { + /* We could be negative, to find the minimum we will use Newton's method + * See https://en.wikipedia.org/wiki/Newton%27s_method_in_optimization */ + + /* This is the function we're trying to minimize: + * y = a * sqr(x) + b * x + c) + (d * x * sqrt(1.0 - sqr(x)) + * First term accounts for ZH + |m| = 2, second terms for |m| = 1. */ + + /* We start guessing at the min of |m|=1 function. */ + float z = -M_SQRT1_2; + float dz; + do { + /* Evaluate our function. */ + minimum = (a * pow2f(z) + b * z + c) + (d * z * sqrt(1.0 - pow2f(z))); + /* This is `func' / func''`. This was computed with Mathematica. */ + dz = (pow2f(z) - 1.0) * (d - 2 * d * pow2f(z) + (b + 2.0 * a * z) * sqrt(1 - pow2f(z))) / + (3 * d * z - 2 * d * pow3f(z) - 2.0 * a * pow(1 - pow2f(z), 1.5f)); + /* Refine our guess by this amount. */ + z = z - dz; + /* Exit if z goes out of range, or if we have reached enough precision. */ + } while (abs(z) <= 1.0 && abs(dz) > 1.0e-5); + + if (abs(z) > 1.0) { + /* z was out of range. Compute `min(function(1), function(-1))`. */ + float func_pos = (a + b + c); + float func_neg = (a - b + c); + minimum = min(func_pos, func_neg); + } + } + return minimum; +} + +void spherical_harmonics_dering(inout SphericalHarmonicL2 sh) +{ + float cutoff = 0.0; + if (true) { + /* Auto windowing. + * Find cutoff threshold automatically. Can be replaced by manual cutoff value. */ + SphericalHarmonicL2 tmp_sh = sh; + + const int num_bands = 3; + /* Start at a large band. */ + float cutoff = float(num_bands * 4 + 1); + /* We need to process each channel separately. */ + for (int channel = 0; channel < 4; channel++) { + /* Find a cut-off band that works. */ + float l = num_bands; + float r = cutoff; + for (int i = 0; i < 16 && (l + 0.1) < r; i++) { + float m = 0.5 * (l + r); + spherical_harmonics_apply_windowing(tmp_sh, m); + + float sh_min = spherical_harmonics_find_minimum(tmp_sh, channel); + if (sh_min < 0.0) { + r = m; + } + else { + l = m; + } + } + cutoff = min(cutoff, l); + } + } + + spherical_harmonics_apply_windowing(sh, cutoff); +} + +/** \} */ -- 2.30.2 From eb64a1dd7b51c41f5c570171d4921d9a74b23fa0 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 26 May 2023 09:13:52 +0200 Subject: [PATCH 46/58] Fix compilation issue. --- source/blender/editors/render/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt index a91a63201c4..de80ffda7c5 100644 --- a/source/blender/editors/render/CMakeLists.txt +++ b/source/blender/editors/render/CMakeLists.txt @@ -35,6 +35,7 @@ set(SRC ) set(LIB + bf_draw ) if(WITH_HEADLESS) -- 2.30.2 From e3608d689d8841a4a64a7c6380a41a483108a51e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 26 May 2023 09:22:15 +0200 Subject: [PATCH 47/58] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f6b871b57a45710b550314b29b9efe75454bddde Merge: f8e24de1764 ad6fa960410 Author: Jeroen Bakker Date: Fri May 26 08:41:34 2023 +0200 Merge branch 'main' into eevee-next-world-shader commit f8e24de1764691d9776ed9e6df9378e81aaaaee9 Author: Jeroen Bakker Date: Fri May 26 08:30:28 2023 +0200 Fix cubemap array. commit 27e98ea4d0b96ec6e3bf98943d0ca961fb7278c5 Author: Jeroen Bakker Date: Thu May 25 18:03:39 2023 +0200 Mark initial world dirty. commit fb7c5cadf25c58185ab0c29d25792f5d74509028 Author: Jeroen Bakker Date: Thu May 25 17:56:58 2023 +0200 Use same reflection as eevee-legacy commit 773c58ba3caf38b90f6235dfafefb9ddffc378d5 Author: Jeroen Bakker Date: Thu May 25 15:24:33 2023 +0200 Incorrect merge. commit 6e6281a8456970134f2ab2b55dc06baf40029334 Author: Jeroen Bakker Date: Thu May 25 15:23:52 2023 +0200 Revert "Merge branch 'eevee-next-irradiance-cache' into eevee-next-world-shader" This reverts commit 272eb1da8243ca5030a69b9a034f6879e56f2017, reversing changes made to d09a8d09e1028fa9d96da4956715368f4a102be4. commit 7dc261508a6cb76e49b7f3d201b6bd52879f6c8c Author: Jeroen Bakker Date: Thu May 25 15:16:22 2023 +0200 Fixed many small issues. commit 9ad112740dddaa1848fffa66b3255d35fc8d7c54 Author: Jeroen Bakker Date: Thu May 25 11:08:03 2023 +0200 Remove flags structure. commit e243f56f207a253a4a21a03e50769e573cb10ac1 Author: Jeroen Bakker Date: Thu May 25 10:12:16 2023 +0200 Fix compilation issue commit 272eb1da8243ca5030a69b9a034f6879e56f2017 Merge: d09a8d09e10 4ad77fcc74c Author: Jeroen Bakker Date: Thu May 25 10:12:06 2023 +0200 Merge branch 'eevee-next-irradiance-cache' into eevee-next-world-shader commit 4ad77fcc74c90bca593984c42efd82f813f386a0 Author: Clément Foucault Date: Wed May 24 22:51:44 2023 +0200 EEVEE-Next: Add progress to irradiance bake commit d09a8d09e1028fa9d96da4956715368f4a102be4 Author: Jeroen Bakker Date: Tue May 23 14:28:39 2023 +0200 Removed comments. commit 94d4d77936347c4c97282c3cbef65585f2cd9cb7 Author: Jeroen Bakker Date: Tue May 23 14:19:23 2023 +0200 Rendering of cubemap. commit fc91ed10a9260e81348e3ce34bb35bef5dcda79f Merge: 487ab2073bd a9beb624494 Author: Clément Foucault Date: Tue May 23 12:31:50 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit cb2dd516503afbb9eacdfc3f3005c335374c518d Author: Jeroen Bakker Date: Tue May 23 11:49:20 2023 +0200 Initial world probe pipeline. commit 2167d5c32eb23b29bb0132c5da96478fc3eec9d9 Author: Jeroen Bakker Date: Tue May 23 11:32:29 2023 +0200 Renames and reorganizing code according to code review. commit 7d1ec9cfd49a2beb1a54d33d023b700fce811414 Author: Jeroen Bakker Date: Tue May 23 11:18:24 2023 +0200 Rename module to eevee_reflection_probes commit 01382ca4eb60742ebabe56f40143ffbd1242215e Merge: 3e2cd9ab68a 4997748e5e6 Author: Jeroen Bakker Date: Tue May 23 11:15:07 2023 +0200 Merge branch 'main' into eevee-next-world-shader commit 3e2cd9ab68a5a316d07628a2801941f0523fc2ff Author: Jeroen Bakker Date: Tue May 23 09:28:35 2023 +0200 Research how to use GPUMaterials commit 1381e4c4171cdf776dbf74b56aee4d08a5261170 Author: Jeroen Bakker Date: Mon May 22 16:00:59 2023 +0200 Add initial CubemapsModule. commit 45d5339f3ac711ebf7f7eaeed1168a0561e082a3 Merge: 1ab265f5350 2164515fab1 Author: Jeroen Bakker Date: Mon May 22 13:00:02 2023 +0200 Merge branch 'main' into eevee-next-world-shader commit 487ab2073bd2ca6f725c50f58fc61c6607167bf6 Author: Clément Foucault Date: Sun May 7 16:06:04 2023 +0200 EEVEE-Next: Irradiance Bake: Avoid asserts on Metal This avoid triggering the asserts in place in the Metal backend. However it does not work yet. commit 63a2736e5e6768285a44f056e23d3777207ce935 Merge: 12fc600d7f8 c796cbebefc Author: Clément Foucault Date: Fri May 5 16:18:36 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit 12fc600d7f807afcbf776ff32720611e43c4dbd2 Author: Clément Foucault Date: Fri May 5 16:14:11 2023 +0200 EEVEE-Next: Fix compilation error with Metal commit 76777bb87485223f53d57c4440fa5c6c49356b5a Merge: dcf3392cec0 29053c5c025 Author: Clément Foucault Date: Fri May 5 14:54:31 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit dcf3392cec085cb3712235de6e4748f3b8f935e3 Merge: e6d1a42d12b 358ce4f52b5 Author: Clément Foucault Date: Sun Apr 30 22:14:57 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit e6d1a42d12bb3f858fb6dcba8b1d22e9541ab396 Author: Clément Foucault Date: Sun Apr 30 22:06:59 2023 +0200 EEVEE-Next: Irradiance Cache: Add version control for new parameter commit 502d9db82c74df563094a451959be020e6e9b7b9 Author: Clément Foucault Date: Sun Apr 30 22:02:56 2023 +0200 EEVEE-Next: Irradiance Cache: Add support for cancelling operator commit 95b8b71908249eee1f8ccb633541147c040d840d Author: Clément Foucault Date: Sun Apr 30 21:35:42 2023 +0200 EEVEE-Next: Irradiance Cache: Reserve slot for world irradiance This doesn't implement the world irradiance extraction process, but only reserve the storage for it. commit 12095b8e16b8bba779ebfe37d8a2c7732d351b17 Author: Clément Foucault Date: Sun Apr 30 21:10:55 2023 +0200 EEVEE-Next: Irradiance Cache: Use scene bounds for surfel cache This remove the per irradiance grid approach. The idea is to do the light transport only once for each visibility collection and store the irradiance for all irradiance grid using this visibility mask. This removes the need for the lightprobe near/far clip parameters. commit 0388f93e790883c7c7d6c389d7e63af7d38776f6 Author: Clément Foucault Date: Thu Apr 27 19:59:11 2023 +0200 EEVEE-Next: Irradiance Cache: Store final format commit f14c7e804c44ba424b9b38b45323b996c1e967a5 Author: Clément Foucault Date: Sat Apr 22 18:08:27 2023 +0200 EEVEE-Next: Irradiance Cache: Load irradiance in atlas This approach allow streaming of visible grids and has a controlled memory budget. commit 3452d3998cb4786e335a738c22e2aea67aecaef0 Merge: d3e761cda3d 99f5e60b86d Author: Clément Foucault Date: Sat Apr 22 17:37:49 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit d3e761cda3d1e3786465ddd1690a21a338b80581 Author: Clément Foucault Date: Sun Apr 16 22:00:56 2023 +0200 EEVEE-Next: Make Spherical harmonic follow object transform This make the baking step output local space spherical harmonics which need to be rotated back to world before being evaluated. In practice we rotate the evaluated direction. commit a5310b77205fd80dc25c7f4e891af4a4d0e44cfc Author: Clément Foucault Date: Thu Apr 13 20:12:32 2023 +0200 EEVEE-Next: Adapt code to new irradiance cache commit b1af33d52ae97132dec275503e754becdf1a9652 Merge: 892f7077bcf 7e764ec692f Author: Clément Foucault Date: Thu Apr 13 14:12:40 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit 892f7077bcf14d643ab23563f6fd7becaf6553be Author: Clément Foucault Date: Thu Apr 13 11:51:59 2023 +0200 Add new lightprobe cache Waiting for the commit to be in master. commit 2018d6c8c14e0009feb1d8e19d7c51b42872a29e Merge: d30ad0e381d c26083b6be7 Author: Clément Foucault Date: Thu Apr 13 11:40:30 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit d30ad0e381d55dccfcb2c53c71072610e442f08f Author: Clément Foucault Date: Wed Apr 5 12:47:24 2023 +0200 Cleanup: EEVEE-Next: Avoid implicit type cast commit a0610ef654fce6fe862da04fce8c9e8c87234cfa Author: Clément Foucault Date: Sun Apr 2 20:09:20 2023 +0200 EEVEE-Next: IrradianceBake: Fix shadow tagging & rendering commit ff5db3a72effc103b3873fe9d4beb4a34aecab35 Merge: 74d89d0b241 70970299ef2 Author: Clément Foucault Date: Sun Apr 2 16:23:52 2023 +0200 Merge branch 'eevee-next-irradiance-cache' of projects.blender.org:pragma37/blender into eevee-next-irradiance-cache commit 74d89d0b241dfba1c8b8e42488a64c20168a5100 Merge: 9461d7fc9ad 7592ec35d34 Author: Clément Foucault Date: Sat Apr 1 17:17:19 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit 9461d7fc9ad5edf66171c780b47384b6b8423d3e Author: Clément Foucault Date: Sat Apr 1 17:10:23 2023 +0200 EEVEE-Next: IrradianceBake: Add spherical harmonic encoding This adds a separate pass to project the neighbor surfels to the final irradiance sample points. commit 6a6ff3310a46cba4a752d46b090c6001c6faa2e6 Merge: 85bbf53f849 e652d0002b5 Author: Clément Foucault Date: Sat Apr 1 13:31:18 2023 +0200 Merge branch 'main' into eevee-next-irradiance-cache commit 85bbf53f8490f89ac71dc4c18955a87471207acf Author: Clément Foucault Date: Sat Apr 1 12:02:46 2023 +0200 EEVEE-Next: Fix wrong irradiance surfel spawning area commit 14973fabe617ad640ba8bd4a6665081dbbf4ceb8 Author: Clément Foucault Date: Sat Apr 1 12:00:33 2023 +0200 BLI: Rotation: Add CartesianBasis `transform_point` and `invert` commit 34adebcf43e489c8707bfc79ddf8b0091c946e29 Author: Clément Foucault Date: Sat Apr 1 11:54:02 2023 +0200 GPU: FrameBuffer: Fix empty framebuffer update The framebuffer default size was only set during the first bind. This is because the `dirty_attachments_ tag` wasn't set and thus the framebuffer size was never passed down to the GL. Split to `default_size_set()` to not affect other code paths that use `size_set()`. commit 70970299ef2c28356c76b999dbeb1af30ff8f1ae Author: Miguel Pozo Date: Wed Mar 29 18:48:24 2023 +0200 Shadow tag usage for surfels commit bfeb1a1a8d9f4398e7c25bb614948061c8cc9672 Author: Clément Foucault Date: Wed Mar 29 21:01:09 2023 +0200 EEVEE-Next: IrradianceBake: Fix light bounce Light bouncing needs a feedback mechanism. We cannot reuse the same radiance for scattering light and accumulating it. This splits the final accumulation to `radiance` and bouncing light into `incomming_light` and `outgoing_light`. commit 5dc6535591153306618640c2466e3b0087f595a9 Author: Clément Foucault Date: Wed Mar 29 14:52:44 2023 +0200 EEVEE-Next: IrradianceBake: Fix integration power and inconsistencies Now it follows proper conventional notation. commit a484f43a177dd30d1a1bd360cd7634acab95352a Author: Miguel Pozo Date: Tue Mar 28 20:05:15 2023 +0200 Irradiance Cache: Display Grid commit 114713c3544ec727ba5f41f69d89ad0a14806e66 Author: Miguel Pozo Date: Tue Mar 28 20:00:19 2023 +0200 Workaround compilation error on Win Nvidia drivers commit a4c571dc8e0f32ec3f5ddc1d576155256abce1a0 Author: Miguel Pozo Date: Tue Mar 28 19:59:35 2023 +0200 Fix irradiance_grid.world_to_grid commit 22ccf837001221201b2ea00a6dc0a1d2f34a4157 Author: Miguel Pozo Date: Tue Mar 28 17:51:27 2023 +0200 Cleanup: Fix typo commit 73f96fe2161e0f8c6c2ba539546e37ff9096bd18 Author: Clément Foucault Date: Tue Mar 28 18:12:28 2023 +0200 EEVEE-Next: IrradianceBake: Implement multi bounce support This is still broken as the light seems to never converge. commit db3028a6249d719913523a2c2a8a69108a655def Author: Clément Foucault Date: Tue Mar 28 13:21:04 2023 +0200 EEVEE-Next: IrradianceBake: Implement light propagation commit 4e3eb2f7a5d1509da3fe2685a9f29d339c2018d6 Author: Clément Foucault Date: Tue Mar 28 13:18:22 2023 +0200 EEVEE-Next: IrradianceBake: Fix crash caused by motion blur commit 5de76bbbffde17d56a4c9f601e56048aaac5f244 Author: Clément Foucault Date: Tue Mar 28 13:17:25 2023 +0200 EEVEE-Next: IrradianceBake: Fix light-leak This fixes the light leaking issue caused by the surfel lists. The fix consists in clumping more surfel together when creating the lists then rewire the coplanar surfels to more valid surfels up and down the same list. commit da91a2ccb028dc7399a10fbae277a6d1dcac42b7 Author: Clément Foucault Date: Sun Mar 26 15:39:17 2023 +0200 EEVEE-Next: IrradianceBake: Fix memleak commit 75b62a9cfb2cecde91300ae0f19c2a746bfe671a Author: Clément Foucault Date: Fri Mar 24 20:46:02 2023 +0100 EEVEE-Next: IrradianceBake: Add surfel list & sorting commit 6f6c3afe912f7f21e86e535c3cce84b408c98771 Merge: 0297bde6290 9c8cb823a05 Author: Clément Foucault Date: Fri Mar 24 17:45:20 2023 +0100 Merge branch 'main' into eevee-next-irradiance-cache commit 0297bde6290d38d9b7267bab358793d7d59d03d4 Author: Clément Foucault Date: Fri Mar 24 17:34:39 2023 +0100 EEVEE-Next: Debug: Fix surfel debugging showing background commit caa0e7edcfc122a26a701cba9f2b3ffe4385752e Author: Clément Foucault Date: Fri Mar 24 17:34:20 2023 +0100 EEVEE-Next: IrradianceBake: Correct surfel placement from grid objects commit 6669771215c81607a2fe8542666143fb56d45ee6 Author: Clément Foucault Date: Thu Mar 23 18:16:22 2023 +0100 EEVEE-Next: LightBake: Fix few mistakes preventing correct baking & debug Was missing manager sync, a buffer read and copy-on-write tagging. commit e696507aa5e9c2ab626c5cf2a208f86575175b3d Merge: 38f6de30cf1 b57b22388cd Author: Clément Foucault Date: Thu Mar 23 16:44:05 2023 +0100 Merge branch 'main' into eevee-next-irradiance-cache commit 38f6de30cf15d3c43768f9a501a1bf7e5b7a6f5f Author: Clément Foucault Date: Thu Mar 23 16:41:48 2023 +0100 EEVEE-Next: LightCache: Solidify the baking pipeline commit e6de2fc4f70655b360efc0f3257a97521df04842 Author: Clément Foucault Date: Thu Mar 23 16:39:02 2023 +0100 EEVEE-Next: GI: Add irradiance sample count option commit 7b3d4c9e4980e6ab4ad2b6231618ef4ff4d13e3b Author: Clément Foucault Date: Tue Mar 21 20:15:59 2023 +0100 EEVEE-Next: LightCache: Add basis of new lightcache version This should allow to keep both version working during the period where both eevee versions are available. commit 795bc96cfe4449c125f7d6c1db0d49bf8ba89500 Author: Clément Foucault Date: Tue Mar 21 12:25:17 2023 +0100 EEVEE-Next: LightCache: Add GL & GPU context functions commit dddfcd6f7abab96eda776576380bc610c27fd26b Author: Clément Foucault Date: Mon Mar 20 22:07:21 2023 +0100 EEVEE-Next: LightProbe: Add auto update detection This allow probe update and deletion to trigger an auto bake. commit 52a7125ce3cdc6467dc3965ef9a68a0490642b4c Author: Clément Foucault Date: Mon Mar 20 19:45:25 2023 +0100 EEVEE-Next: LightCache: Port basic lightcache structure to C++ This removes the complexity of resource sharing as it isn't even leveraged yet. So keep the code as simple as possible for now. commit 408c5d72810ef4bb5f5f8a7ce814164f8858a375 Author: Clément Foucault Date: Sun Mar 19 21:33:55 2023 +0100 EEVEE-Next: Irradiance-Cache: Add surfel placement Spawning surfels uses a new shader pipeline (called `capture`) to avoid the complexity of modifying the deferred or forward pipeline. The shaders are light to compile and should only be compiled on bake. This is very WIP: the surfel projection box is hardcoded and the 3 projections are done every frame. The surfels are placed in a regular grid because of the raster pipeline used to spawn them. Note: While this is really fast, I am not sure this will scale well for highly detailed lighting conditions. In the case where the surfel density is too low, the aliasing might create really poor result. --- source/blender/draw/CMakeLists.txt | 3 + .../draw/engines/eevee_next/eevee_defines.hh | 1 + .../draw/engines/eevee_next/eevee_instance.cc | 2 + .../draw/engines/eevee_next/eevee_instance.hh | 3 + .../draw/engines/eevee_next/eevee_pipeline.cc | 70 +++++++++++++++ .../draw/engines/eevee_next/eevee_pipeline.hh | 44 ++++++++- .../eevee_next/eevee_reflection_probes.cc | 70 +++++++++++++++ .../eevee_next/eevee_reflection_probes.hh | 89 +++++++++++++++++++ .../draw/engines/eevee_next/eevee_view.cc | 3 +- .../draw/engines/eevee_next/eevee_world.cc | 17 +++- .../draw/engines/eevee_next/eevee_world.hh | 7 ++ .../shaders/eevee_deferred_light_frag.glsl | 2 + .../shaders/eevee_reflection_probe_lib.glsl | 16 ++++ .../shaders/infos/eevee_deferred_info.hh | 1 + .../infos/eevee_reflection_probe_info.hh | 11 +++ source/blender/draw/intern/DRW_gpu_wrapper.hh | 5 +- source/blender/gpu/CMakeLists.txt | 1 + 17 files changed, 338 insertions(+), 7 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc create mode 100644 source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6edd63698b2..7709c1cf9ea 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -148,6 +148,7 @@ set(SRC engines/eevee_next/eevee_material.cc engines/eevee_next/eevee_motion_blur.cc engines/eevee_next/eevee_pipeline.cc + engines/eevee_next/eevee_reflection_probes.cc engines/eevee_next/eevee_renderbuffers.cc engines/eevee_next/eevee_sampling.cc engines/eevee_next/eevee_shader.cc @@ -286,6 +287,7 @@ set(SRC engines/eevee_next/eevee_material.hh engines/eevee_next/eevee_motion_blur.hh engines/eevee_next/eevee_pipeline.hh + engines/eevee_next/eevee_reflection_probes.hh engines/eevee_next/eevee_renderbuffers.hh engines/eevee_next/eevee_sampling.hh engines/eevee_next/eevee_shader.hh @@ -478,6 +480,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl engines/eevee_next/shaders/eevee_motion_blur_lib.glsl engines/eevee_next/shaders/eevee_nodetree_lib.glsl + engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl engines/eevee_next/shaders/eevee_sampling_lib.glsl engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl engines/eevee_next/shaders/eevee_shadow_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index fd7fd7b55b6..9e98dd21210 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -95,6 +95,7 @@ /* Only during shadow rendering. */ #define SHADOW_RENDER_MAP_SLOT 13 #define RBUFS_UTILITY_TEX_SLOT 14 +#define REFLECTION_PROBE_TEX_SLOT 16 /* Images. */ #define RBUFS_NORMAL_SLOT 0 diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 575ef7a086d..995a7d5c323 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -71,6 +71,7 @@ void Instance::init(const int2 &output_res, motion_blur.init(); main_view.init(); irradiance_cache.init(); + reflection_probes.init(); } void Instance::set_time(float time) @@ -117,6 +118,7 @@ void Instance::begin_sync() hiz_buffer.sync(); main_view.sync(); world.sync(); + reflection_probes.sync(); film.sync(); irradiance_cache.sync(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 8e7855961e4..5bc280bc8cf 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -26,6 +26,7 @@ #include "eevee_material.hh" #include "eevee_motion_blur.hh" #include "eevee_pipeline.hh" +#include "eevee_reflection_probes.hh" #include "eevee_renderbuffers.hh" #include "eevee_sampling.hh" #include "eevee_shader.hh" @@ -51,6 +52,7 @@ class Instance { PipelineModule pipelines; ShadowModule shadows; LightModule lights; + ReflectionProbeModule reflection_probes; VelocityModule velocity; MotionBlurModule motion_blur; DepthOfField depth_of_field; @@ -97,6 +99,7 @@ class Instance { pipelines(*this), shadows(*this), lights(*this), + reflection_probes(*this), velocity(*this), motion_blur(*this), depth_of_field(*this), diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 78ff35136aa..5d695cbd88e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -62,6 +62,75 @@ void WorldPipeline::render(View &view) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name World Probe Pipeline + * \{ */ + +void WorldProbePipeline::sync() +{ + for (int face : IndexRange(6)) { + CubemapSide &side = sides_[face]; + /* View */ + float4x4 view_m4 = cubeface_mat(face); + float4x4 win_m4; + cubeface_winmat_get(win_m4, 1.0f, 10.0f); + side.view.sync(view_m4, win_m4); + + side.cubemap_face_ps.init(); + side.cubemap_face_ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS); + } +} + +void WorldProbePipeline::sync(GPUMaterial *gpumat) +{ + for (int face : IndexRange(6)) { + sync(gpumat, face); + } +} + +void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) +{ + Manager &manager = *inst_.manager; + + CubemapSide &side = sides_[face]; + + /* Framebuffer. */ + Texture &cubemap = inst_.reflection_probes.cubemaps_tx_; + side.cubemap_face_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_CUBEFACE(cubemap, face)); + + ResourceHandle handle = manager.resource_handle(float4x4::identity()); + + side.cubemap_face_ps.framebuffer_set(&side.cubemap_face_fb); + side.cubemap_face_ps.material_set(manager, gpumat); + + side.cubemap_face_ps.draw(DRW_cache_fullscreen_quad_get(), handle); + /* To allow opaque pass rendering over it. */ + side.cubemap_face_ps.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); +} + +void WorldProbePipeline::render() +{ + GPUFrameBuffer *previous_framebuffer = GPU_framebuffer_active_get(); + + GPU_debug_group_begin("World.Probe"); + for (int face : IndexRange(6)) { + sides_[face].render(inst_); + } + GPU_debug_group_end(); + + GPU_texture_update_mipmap_chain(inst_.reflection_probes.cubemaps_tx_); + if (previous_framebuffer) { + GPU_framebuffer_bind(previous_framebuffer); + } +} + +void WorldProbePipeline::CubemapSide::render(Instance &instance) +{ + instance.manager->submit(cubemap_face_ps, view); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Shadow Pipeline * @@ -387,6 +456,7 @@ void DeferredLayer::end_sync() inst_.shadows.bind_resources(&eval_light_ps_); inst_.sampling.bind_resources(&eval_light_ps_); inst_.hiz_buffer.bind_resources(&eval_light_ps_); + inst_.reflection_probes.bind_resources(&eval_light_ps_); eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index c09f5fe99cf..e1e1af08821 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -43,6 +43,45 @@ class WorldPipeline { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name World Probe Pipeline + * + * Render reflection probe of the world background. + * \{ */ + +class WorldProbePipeline { + private: + Instance &inst_; + + struct CubemapSide { + PassSimple cubemap_face_ps; + View view; + Framebuffer cubemap_face_fb; + void render(Instance &instance); + }; + + CubemapSide sides_[6] = { + {{"PosX"}, {"PosX"}}, + {{"NegX"}, {"NegX"}}, + {{"PosY"}, {"PosY"}}, + {{"NegY"}, {"NegY"}}, + {{"PosZ"}, {"PosZ"}}, + {{"NegZ"}, {"NegZ"}}, + }; + + public: + WorldProbePipeline(Instance &inst) : inst_(inst){}; + + void sync(); + void sync(GPUMaterial *gpumat); + void render(); + + private: + void sync(GPUMaterial *gpumat, int face); +}; // namespace blender::eevee + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Shadow Pass * @@ -266,6 +305,7 @@ class UtilityTexture : public Texture { class PipelineModule { public: WorldPipeline world; + WorldProbePipeline world_probe; DeferredPipeline deferred; ForwardPipeline forward; ShadowPipeline shadow; @@ -273,13 +313,15 @@ class PipelineModule { UtilityTexture utility_tx; public: - PipelineModule(Instance &inst) : world(inst), deferred(inst), forward(inst), shadow(inst){}; + PipelineModule(Instance &inst) + : world(inst), world_probe(inst), deferred(inst), forward(inst), shadow(inst){}; void begin_sync() { deferred.begin_sync(); forward.sync(); shadow.sync(); + world_probe.sync(); } void end_sync() diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc new file mode 100644 index 00000000000..62d61ee7251 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. */ + +#include "eevee_reflection_probes.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +void ReflectionProbeModule::init() +{ + if (cubemaps_.is_empty()) { + cubemaps_.reserve(MAX_PROBES); + + /* Initialize the world cubemap. */ + ReflectionProbe world_cubemap; + world_cubemap.type = ReflectionProbe::Type::World; + world_cubemap.is_dirty = true; + cubemaps_.append(world_cubemap); + + cubemaps_tx_.ensure_cube_array(GPU_RGBA16F, + MAX_RESOLUTION, + MAX_PROBES, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT, + NULL, + 12); + GPU_texture_mipmap_mode(cubemaps_tx_, true, true); + } +} + +void ReflectionProbeModule::sync() +{ + for (int index : IndexRange(MAX_PROBES)) { + ReflectionProbe &cubemap = cubemaps_[index]; + if (!cubemap.needs_update()) { + continue; + } + sync(cubemap); + cubemap.is_dirty = false; + } +} + +void ReflectionProbeModule::sync(const ReflectionProbe &cubemap) +{ + if (cubemap.type == ReflectionProbe::Type::World) { + GPUMaterial *world_material = instance_.world.get_world_material(); + instance_.pipelines.world_probe.sync(world_material); + } + else { + BLI_assert_unreachable(); + } +} + +void ReflectionProbeModule::set_world_dirty() +{ + cubemaps_[WORLD_SLOT].is_dirty = true; +} + +/* -------------------------------------------------------------------- */ +/** \name World + * + * \{ */ + +bool ReflectionProbe::needs_update() const +{ + return type != Type::Unused && is_dirty; +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh new file mode 100644 index 00000000000..7e567d1de73 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. */ + +/** \file + * \ingroup eevee + * + * Cubemaps + * + * Cubemaps record light from different locations in the scene. These cubemaps are used to add + * environment and indirect lighting from light probes. + * + * - Although we have Global illumination light probes can still be used for situations that + * GI doesn't work. For example semi-transparent surfaces. + * - The first cubemap is always reserved for the world shading. + * + */ + +#pragma once + +#include "eevee_shader_shared.hh" + +#include "BKE_cryptomatte.hh" + +extern "C" { +struct Material; +} + +namespace blender::eevee { + +class Instance; +class WorldProbePipeline; + +/* -------------------------------------------------------------------- */ +/** \name Reflection Probes + * \{ */ +class ReflectionProbe { + public: + enum Type { Unused, World }; + + Type type; + bool is_dirty = false; + + bool needs_update() const; +}; + +class ReflectionProbeModule { + private: + /** The max number of probes to track. */ + static constexpr int MAX_PROBES = 1; + + /** + * The maximum resolution of a cubemap side. + * + * Must be a power of two; intension to be used as a cubemap atlas. + */ + static constexpr int MAX_RESOLUTION = 2048; + + /** + * Index of the probe that is used for world background. + * + * NOTE: First probe always contains the world probe. + */ + static constexpr int WORLD_SLOT = 0; + + Instance &instance_; + + Vector cubemaps_; + Texture cubemaps_tx_ = {"Probes"}; + + public: + ReflectionProbeModule(Instance &instance) : instance_(instance) {} + + void init(); + void set_world_dirty(); + + void sync(); + + template void bind_resources(draw::detail::PassBase *pass) + { + pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, cubemaps_tx_); + } + + private: + void sync(const ReflectionProbe &cubemap); + + friend class WorldProbePipeline; +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index d2d8f80feb7..86a4b4635c1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -119,14 +119,13 @@ void ShadingView::render() GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f); inst_.pipelines.world.render(render_view_new_); + inst_.pipelines.world_probe.render(); /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */ inst_.lights.set_view(render_view_new_, extent_); inst_.pipelines.deferred.render(render_view_new_, prepass_fb_, combined_fb_, extent_); - // inst_.lightprobes.draw_cache_display(); - // inst_.lookdev.render_overlay(view_fb_); inst_.pipelines.forward.render(render_view_new_, prepass_fb_, combined_fb_, rbufs.combined_tx); diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index 97312ca5800..3c25b9d5d5e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -90,7 +90,7 @@ void World::sync() WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); if (wo_handle.recalc != 0) { - // inst_.lightprobes.set_world_dirty(); + inst_.reflection_probes.set_world_dirty(); } wo_handle.reset_recalc_flag(); @@ -111,6 +111,21 @@ void World::sync() inst_.pipelines.world.sync(gpumat); } +GPUMaterial *World::get_world_material() +{ + ::World *bl_world = inst_.scene->world; + if (bl_world == nullptr) { + bl_world = default_world_get(); + } + + bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ? + bl_world->nodetree : + default_tree.nodetree_get(bl_world); + + GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree); + return gpumat; +} + /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh index f21c9b964f0..ae599021302 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.hh +++ b/source/blender/draw/engines/eevee_next/eevee_world.hh @@ -62,6 +62,13 @@ class World { ~World(); void sync(); + + /** + * Get the world material. + * + * NOTE: this function should only be called after World::sync has been executed. + */ + GPUMaterial *get_world_material(); }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index 80dcaaf67cb..8d6953bfafb 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -9,6 +9,7 @@ #pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl) void main() { @@ -52,6 +53,7 @@ void main() vec3 diffuse_light = vec3(0.0); vec3 reflection_light = vec3(0.0); + light_world_eval(diffuse_data, reflection_data, P, V, diffuse_light, reflection_light); light_eval( diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl new file mode 100644 index 00000000000..eef1569cff3 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl @@ -0,0 +1,16 @@ +void light_world_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 V, + inout vec3 out_diffuse, + inout vec3 out_specular) +{ + float linear_roughness = fast_sqrt(reflection.roughness); + /* TODO: This should be based by actual LOD?.*/ + float lod_cube_max = 12.0; + float lod = linear_roughness * lod_cube_max; + + vec3 R = -reflect(V, reflection.N); + vec3 world_light = textureLod_cubemapArray(reflectionProbes, vec4(R, 0.0), lod).rgb; + out_specular += world_light; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh index f5bc967e5b6..af402ec7601 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -37,6 +37,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light) .additional_info("eevee_shared", "eevee_utility_texture", "eevee_light_data", + "eevee_reflection_probe_data", "eevee_shadow_data", "eevee_deferred_base", "eevee_hiz_data", diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh new file mode 100644 index 00000000000..9eac2f05e89 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh @@ -0,0 +1,11 @@ +#include "eevee_defines.hh" +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Shared + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data) + .sampler(REFLECTION_PROBE_TEX_SLOT, ImageType::FLOAT_CUBE_ARRAY, "reflectionProbes"); + +/** \} */ diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 2c7e3623a43..d7e68b1bdb7 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -674,7 +674,7 @@ class Texture : NonCopyable { float *data = nullptr, int mip_len = 1) { - return ensure_impl(extent, extent, layers, mip_len, format, usage, data, false, true); + return ensure_impl(extent, extent, layers, mip_len, format, usage, data, true, true); } /** @@ -1033,8 +1033,7 @@ class TextureRef : public Texture { * Dummy type to bind texture as image. * It is just a GPUTexture in disguise. */ -class Image { -}; +class Image {}; static inline Image *as_image(GPUTexture *tex) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 5e2ac715a7e..845b8425dc3 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -648,6 +648,7 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_info.hh -- 2.30.2 From 3b88bd559c7b46361ea7aac4058271fa415671da Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 26 May 2023 12:53:52 +0200 Subject: [PATCH 48/58] Add world irradiance caching. --- .../draw/engines/eevee_next/eevee_instance.cc | 1 + .../engines/eevee_next/eevee_irradiance_cache.cc | 2 ++ .../eevee_lightprobe_irradiance_ray_comp.glsl | 14 ++++++++++---- .../eevee_next/shaders/eevee_surfel_ray_comp.glsl | 14 ++++++++++---- .../shaders/infos/eevee_irradiance_cache_info.hh | 10 ++++++++-- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index f34b10c30da..5ba789fb5b6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -100,6 +100,7 @@ void Instance::init_light_bake(Depsgraph *depsgraph, draw::Manager *manager) shadows.init(); main_view.init(); irradiance_cache.init(); + reflection_probes.init(); } void Instance::set_time(float time) diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 3ee480e5ab7..60967dcd822 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -426,6 +426,7 @@ void IrradianceBake::sync() sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY)); sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); + inst_.reflection_probes.bind_resources(&sub); sub.barrier(GPU_BARRIER_SHADER_STORAGE); sub.dispatch(&dispatch_per_surfel_); } @@ -442,6 +443,7 @@ void IrradianceBake::sync() pass.bind_image("irradiance_L1_a_img", &irradiance_L1_a_tx_); pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_); pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_); + inst_.reflection_probes.bind_resources(&pass); pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS); pass.dispatch(&dispatch_per_grid_sample_); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl index a304afd160c..eea58eea5a1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl @@ -13,6 +13,7 @@ #pragma BLENDER_REQUIRE(eevee_surfel_list_lib.glsl) #pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(cubemap_lib.glsl) void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh) { @@ -31,6 +32,11 @@ void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 irradiance_capture(L, irradiance, sh); } +vec3 irradiance_sky_sample(vec3 R) +{ + return textureLod_cubemapArray(reflectionProbes, vec4(R, 0.0), 0.0).rgb; +} + void main() { ivec3 grid_coord = ivec3(gl_GlobalInvocationID); @@ -71,16 +77,16 @@ void main() irradiance_capture(surfel_buf[surfel_next], P, sh); } else { - /* TODO(fclem): Sky radiance. */ - irradiance_capture(sky_L, vec3(0.0), sh); + vec3 world_radiance = irradiance_sky_sample(sky_L); + irradiance_capture(sky_L, world_radiance, sh); } if (surfel_prev > -1) { irradiance_capture(surfel_buf[surfel_prev], P, sh); } else { - /* TODO(fclem): Sky radiance. */ - irradiance_capture(-sky_L, vec3(0.0), sh); + vec3 world_radiance = irradiance_sky_sample(-sky_L); + irradiance_capture(-sky_L, world_radiance, sh); } imageStore(irradiance_L0_img, grid_coord, sh.L0.M0); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index 040f1b61d73..47cb3095d87 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -12,6 +12,7 @@ #pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(cubemap_lib.glsl) void radiance_transfer(inout Surfel surfel, vec3 irradiance, vec3 L) { @@ -38,6 +39,11 @@ void radiance_transfer(inout Surfel surfel, Surfel surfel_emitter) radiance_transfer(surfel, irradiance, L); } +vec3 radiance_sky_sample(vec3 R) +{ + return textureLod_cubemapArray(reflectionProbes, vec4(R, 0.0), 0.0).rgb; +} + void main() { int surfel_index = int(gl_GlobalInvocationID.x); @@ -53,16 +59,16 @@ void main() radiance_transfer(surfel, surfel_buf[surfel.next]); } else { - /* TODO(fclem): Sky radiance. */ - radiance_transfer(surfel, vec3(0.0), sky_L); + vec3 world_radiance = radiance_sky_sample(sky_L); + radiance_transfer(surfel, world_radiance, sky_L); } if (surfel.prev > -1) { radiance_transfer(surfel, surfel_buf[surfel.prev]); } else { - /* TODO(fclem): Sky radiance. */ - radiance_transfer(surfel, vec3(0.0), -sky_L); + vec3 world_radiance = radiance_sky_sample(-sky_L); + radiance_transfer(surfel, world_radiance, -sky_L); } surfel_buf[surfel_index].incomming_light_front = surfel.incomming_light_front; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index 6434b795c51..d948da52b94 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -87,7 +87,10 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort) GPU_SHADER_CREATE_INFO(eevee_surfel_ray) .local_group_size(SURFEL_GROUP_SIZE) - .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") + .additional_info("eevee_shared", + "eevee_surfel_common", + "eevee_reflection_probe_data", + "draw_view") .compute_source("eevee_surfel_ray_comp.glsl") .do_static_compilation(true); @@ -105,7 +108,10 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray) .local_group_size(IRRADIANCE_GRID_GROUP_SIZE, IRRADIANCE_GRID_GROUP_SIZE, IRRADIANCE_GRID_GROUP_SIZE) - .additional_info("eevee_shared", "eevee_surfel_common", "draw_view") + .additional_info("eevee_shared", + "eevee_surfel_common", + "eevee_reflection_probe_data", + "draw_view") .storage_buf(0, Qualifier::READ, "int", "list_start_buf[]") .storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf") .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_img") -- 2.30.2 From e592e2083d691a101261efa24a1083e590c02505 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 26 May 2023 14:20:46 +0200 Subject: [PATCH 49/58] Fix sampling world background. --- source/blender/draw/engines/eevee_next/eevee_instance.cc | 2 ++ source/blender/gpu/opengl/gl_shader.cc | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 5ba789fb5b6..6114791f6b8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -484,6 +484,8 @@ void Instance::light_bake_irradiance( render_sync(); manager->end_sync(); + pipelines.world_probe.render(); + irradiance_cache.bake.surfels_create(probe); irradiance_cache.bake.surfels_lights_eval(); }); diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 51ec9667d57..e63b58f7a8b 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -894,6 +894,11 @@ static char *glsl_patch_compute_get() STR_CONCAT(patch, slen, "#version 430\n"); STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n"); + if (GLContext::texture_cube_map_array_support) { + STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n"); + STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); + } + /* Array compat. */ STR_CONCAT(patch, slen, "#define gpu_Array(_type) _type[]\n"); -- 2.30.2 From d58847b79863bac228600e60f53cd7fa7128f61b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 30 May 2023 08:13:15 +0200 Subject: [PATCH 50/58] Don't apply film transparecy to world probe. --- source/blender/draw/engines/eevee_next/eevee_pipeline.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 5d695cbd88e..0c11fc3aabb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -102,6 +102,7 @@ void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) side.cubemap_face_ps.framebuffer_set(&side.cubemap_face_fb); side.cubemap_face_ps.material_set(manager, gpumat); + side.cubemap_face_ps.push_constant("world_opacity_fade", 1.0f); side.cubemap_face_ps.draw(DRW_cache_fullscreen_quad_get(), handle); /* To allow opaque pass rendering over it. */ -- 2.30.2 From 0fc43cc088b019bf332fe69801b6976a9595c4b6 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 30 May 2023 08:58:16 +0200 Subject: [PATCH 51/58] Only apply world lighting to first bounce. --- .../draw/engines/eevee_next/eevee_instance.cc | 4 +++- .../engines/eevee_next/eevee_irradiance_cache.cc | 16 +++++++++++----- .../engines/eevee_next/eevee_irradiance_cache.hh | 4 ++-- .../eevee_next/eevee_reflection_probes.cc | 5 +++++ .../eevee_next/eevee_reflection_probes.hh | 6 ++++++ 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 5190aa58e7b..7a3b1ee8836 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -494,6 +494,8 @@ void Instance::light_bake_irradiance( for (int bounce = 0; bounce <= bounce_len; bounce++) { /* Last iteration only captures lighting. */ const bool is_last_bounce = (bounce == bounce_len); + /* First bounce includes world lighting. */ + const bool is_first_bounce = bounce == 0; sampling.init(scene); while (!sampling.finished()) { @@ -505,7 +507,7 @@ void Instance::light_bake_irradiance( irradiance_cache.bake.raylists_build(); if (!is_last_bounce) { - irradiance_cache.bake.propagate_light(); + irradiance_cache.bake.propagate_light(is_first_bounce); } if (is_last_bounce) { irradiance_cache.bake.irradiance_capture(); diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index fde8d81a0c5..1f890135f23 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -443,15 +443,20 @@ void IrradianceBake::sync() sub.dispatch(&dispatch_per_list_); } } - { - PassSimple &pass = surfel_light_propagate_ps_; + for (int pass_index : IndexRange(2)) { + PassSimple &pass = surfel_light_propagate_ps_[pass_index]; pass.init(); { PassSimple::Sub &sub = pass.sub("RayEval"); sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY)); sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_); sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_); - inst_.reflection_probes.bind_resources(&sub); + if (pass_index == 0) { + inst_.reflection_probes.bind_resources(&sub); + } + else { + inst_.reflection_probes.bind_dummy_resources(&sub); + } sub.barrier(GPU_BARRIER_SHADER_STORAGE); sub.dispatch(&dispatch_per_surfel_); } @@ -722,9 +727,10 @@ void IrradianceBake::raylists_build() inst_.manager->submit(surfel_ray_build_ps_, ray_view_); } -void IrradianceBake::propagate_light() +void IrradianceBake::propagate_light(bool first_bounce) { - inst_.manager->submit(surfel_light_propagate_ps_, ray_view_); + PassSimple &pass = surfel_light_propagate_ps_[first_bounce ? 0 : 1]; + inst_.manager->submit(pass, ray_view_); } void IrradianceBake::irradiance_capture() diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh index e984de61ebc..b08ac06be18 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.hh @@ -44,7 +44,7 @@ class IrradianceBake { /** Create linked list of surfel to emulated raycast. */ PassSimple surfel_ray_build_ps_ = {"RayBuild"}; /** Propagate light from surfel to surfel. */ - PassSimple surfel_light_propagate_ps_ = {"LightPropagate"}; + PassSimple surfel_light_propagate_ps_[2] = {{"LightPropagateFirstBounce"}, {"LightPropagate"}}; /** Start of a light bounce. Accumulate light from previous propagation. */ PassSimple surfel_light_bounce_ps_ = {"LightBounce"}; /** Capture surfel lighting to irradiance samples. */ @@ -108,7 +108,7 @@ class IrradianceBake { /** Create a surfel lists to emulate ray-casts for the current sample random direction. */ void raylists_build(); /** Propagate light from surfel to surfel in a random direction over the sphere. */ - void propagate_light(); + void propagate_light(bool first_bounce); /** Store surfel irradiance inside the irradiance grid samples. */ void irradiance_capture(); /** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc index 62d61ee7251..daa8739976c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -24,6 +24,11 @@ void ReflectionProbeModule::init() NULL, 12); GPU_texture_mipmap_mode(cubemaps_tx_, true, true); + + dummy_tx_.ensure_cube_array( + GPU_RGBA16F, 1, 1, GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT, NULL, 1); + dummy_tx_.clear(float4(0.0f)); + GPU_texture_mipmap_mode(dummy_tx_, false, false); } } diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh index 7e567d1de73..6114d260ee1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh @@ -66,6 +66,7 @@ class ReflectionProbeModule { Vector cubemaps_; Texture cubemaps_tx_ = {"Probes"}; + Texture dummy_tx_ = {"DummyProbes"}; public: ReflectionProbeModule(Instance &instance) : instance_(instance) {} @@ -80,6 +81,11 @@ class ReflectionProbeModule { pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, cubemaps_tx_); } + template void bind_dummy_resources(draw::detail::PassBase *pass) + { + pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, dummy_tx_); + } + private: void sync(const ReflectionProbe &cubemap); -- 2.30.2 From 95771741ae49ea08fbebb960d646fa10a8f8bc8b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 1 Jun 2023 08:06:11 +0200 Subject: [PATCH 52/58] Use dummy textures when drawing world reflection probe. --- .../draw/engines/eevee_next/eevee_pipeline.cc | 30 +++++++++++++++---- .../draw/engines/eevee_next/eevee_pipeline.hh | 5 ++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 0c11fc3aabb..2ba0bf51839 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -79,6 +79,14 @@ void WorldProbePipeline::sync() side.cubemap_face_ps.init(); side.cubemap_face_ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS); } + dummy_cryptomatte_tx_.ensure_2d( + GPU_RGBA16F, int2(1), GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); + dummy_renderpass_tx_.ensure_2d( + GPU_RGBA32F, int2(1), GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); + dummy_aov_color_tx_.ensure_2d_array( + GPU_RGBA16F, int2(1), 1, GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); + dummy_aov_value_tx_.ensure_2d_array( + GPU_R16F, int2(1), 1, GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); } void WorldProbePipeline::sync(GPUMaterial *gpumat) @@ -99,14 +107,26 @@ void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) side.cubemap_face_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_CUBEFACE(cubemap, face)); ResourceHandle handle = manager.resource_handle(float4x4::identity()); + PassSimple &pass = side.cubemap_face_ps; - side.cubemap_face_ps.framebuffer_set(&side.cubemap_face_fb); - side.cubemap_face_ps.material_set(manager, gpumat); - side.cubemap_face_ps.push_constant("world_opacity_fade", 1.0f); + pass.framebuffer_set(&side.cubemap_face_fb); + pass.material_set(manager, gpumat); + pass.push_constant("world_opacity_fade", 1.0f); - side.cubemap_face_ps.draw(DRW_cache_fullscreen_quad_get(), handle); + pass.bind_image("rp_normal_img", dummy_renderpass_tx_); + pass.bind_image("rp_light_img", dummy_renderpass_tx_); + pass.bind_image("rp_diffuse_color_img", dummy_renderpass_tx_); + pass.bind_image("rp_specular_color_img", dummy_renderpass_tx_); + pass.bind_image("rp_emission_img", dummy_renderpass_tx_); + pass.bind_image("rp_cryptomatte_img", dummy_cryptomatte_tx_); + + pass.bind_image("aov_color_img", dummy_aov_color_tx_); + pass.bind_image("aov_value_img", dummy_aov_value_tx_); + pass.bind_ssbo("aov_buf", &inst_.film.aovs_info); + + pass.draw(DRW_cache_fullscreen_quad_get(), handle); /* To allow opaque pass rendering over it. */ - side.cubemap_face_ps.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); + pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } void WorldProbePipeline::render() diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index e1e1af08821..b08cc723e70 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -53,6 +53,11 @@ class WorldProbePipeline { private: Instance &inst_; + Texture dummy_renderpass_tx_; + Texture dummy_cryptomatte_tx_; + Texture dummy_aov_color_tx_; + Texture dummy_aov_value_tx_; + struct CubemapSide { PassSimple cubemap_face_ps; View view; -- 2.30.2 From 48dacdabb8ae8ec4604b18052489612757b2c92b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 1 Jun 2023 08:25:24 +0200 Subject: [PATCH 53/58] Skip mipmap generation when world isn't changed. --- .../draw/engines/eevee_next/eevee_pipeline.cc | 24 ++++++++++++------- .../draw/engines/eevee_next/eevee_pipeline.hh | 6 +++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 2ba0bf51839..d29a74a3220 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -79,14 +79,14 @@ void WorldProbePipeline::sync() side.cubemap_face_ps.init(); side.cubemap_face_ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS); } - dummy_cryptomatte_tx_.ensure_2d( - GPU_RGBA16F, int2(1), GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); - dummy_renderpass_tx_.ensure_2d( - GPU_RGBA32F, int2(1), GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); - dummy_aov_color_tx_.ensure_2d_array( - GPU_RGBA16F, int2(1), 1, GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); - dummy_aov_value_tx_.ensure_2d_array( - GPU_R16F, int2(1), 1, GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE); + const int2 extent(1); + const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE; + dummy_cryptomatte_tx_.ensure_2d(GPU_RGBA32F, extent, usage); + dummy_renderpass_tx_.ensure_2d(GPU_RGBA16F, extent, usage); + dummy_aov_color_tx_.ensure_2d_array(GPU_RGBA16F, extent, 1, usage); + dummy_aov_value_tx_.ensure_2d_array(GPU_R16F, extent, 1, usage); + + has_draw_commands_ = false; } void WorldProbePipeline::sync(GPUMaterial *gpumat) @@ -94,6 +94,7 @@ void WorldProbePipeline::sync(GPUMaterial *gpumat) for (int face : IndexRange(6)) { sync(gpumat, face); } + has_draw_commands_ = true; } void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) @@ -119,18 +120,23 @@ void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) pass.bind_image("rp_specular_color_img", dummy_renderpass_tx_); pass.bind_image("rp_emission_img", dummy_renderpass_tx_); pass.bind_image("rp_cryptomatte_img", dummy_cryptomatte_tx_); + /* Required by validation layers. */ + inst_.cryptomatte.bind_resources(&pass); pass.bind_image("aov_color_img", dummy_aov_color_tx_); pass.bind_image("aov_value_img", dummy_aov_value_tx_); pass.bind_ssbo("aov_buf", &inst_.film.aovs_info); pass.draw(DRW_cache_fullscreen_quad_get(), handle); - /* To allow opaque pass rendering over it. */ + pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } void WorldProbePipeline::render() { + if (!has_draw_commands_) { + return; + } GPUFrameBuffer *previous_framebuffer = GPU_framebuffer_active_get(); GPU_debug_group_begin("World.Probe"); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index b08cc723e70..e66549b2d77 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -65,6 +65,12 @@ class WorldProbePipeline { void render(Instance &instance); }; + /** + * Keep track if the world probe needs to be updated. This should only be the case when the + * world is updated. This flag is used to skip updating mipmaps when the world isn't changed. + */ + bool has_draw_commands_ = false; + CubemapSide sides_[6] = { {{"PosX"}, {"PosX"}}, {{"NegX"}, {"NegX"}}, -- 2.30.2 From a58620c766e807c7c9be321fa96eeb7cff18ad0c Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 1 Jun 2023 08:45:44 +0200 Subject: [PATCH 54/58] Some small cleanups and code comments. --- source/blender/draw/engines/eevee_next/eevee_pipeline.cc | 2 +- .../blender/draw/engines/eevee_next/eevee_reflection_probes.cc | 2 +- .../blender/draw/engines/eevee_next/eevee_reflection_probes.hh | 1 + .../engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index d29a74a3220..a75341936f5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -80,7 +80,7 @@ void WorldProbePipeline::sync() side.cubemap_face_ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS); } const int2 extent(1); - const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE; + constexpr eGPUTextureUsage usage = GPU_TEXTURE_USAGE_MEMORYLESS | GPU_TEXTURE_USAGE_SHADER_WRITE; dummy_cryptomatte_tx_.ensure_2d(GPU_RGBA32F, extent, usage); dummy_renderpass_tx_.ensure_2d(GPU_RGBA16F, extent, usage); dummy_aov_color_tx_.ensure_2d_array(GPU_RGBA16F, extent, 1, usage); diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc index 62d61ee7251..505325f3072 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -22,7 +22,7 @@ void ReflectionProbeModule::init() MAX_PROBES, GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT, NULL, - 12); + MIPMAP_LEVELS); GPU_texture_mipmap_mode(cubemaps_tx_, true, true); } } diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh index 7e567d1de73..5f9740d9794 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh @@ -54,6 +54,7 @@ class ReflectionProbeModule { * Must be a power of two; intension to be used as a cubemap atlas. */ static constexpr int MAX_RESOLUTION = 2048; + static constexpr int MIPMAP_LEVELS = 12; /** * Index of the probe that is used for world background. diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl index eef1569cff3..b6b73c57c34 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl @@ -6,7 +6,8 @@ void light_world_eval(ClosureDiffuse diffuse, inout vec3 out_specular) { float linear_roughness = fast_sqrt(reflection.roughness); - /* TODO: This should be based by actual LOD?.*/ + /* TODO: This should be based by actual resolution. Currently the resolution is fixed but + * eventually this should based on a user setting. */ float lod_cube_max = 12.0; float lod = linear_roughness * lod_cube_max; -- 2.30.2 From 845406721f1e7cd3c360fa404fb35f0f6c446a62 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 16 Jun 2023 07:59:44 +0200 Subject: [PATCH 55/58] Make format --- source/blender/draw/intern/DRW_gpu_wrapper.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 12eb202db4b..9ff08b18498 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -1035,7 +1035,8 @@ class TextureRef : public Texture { * Dummy type to bind texture as image. * It is just a GPUTexture in disguise. */ -class Image {}; +class Image { +}; static inline Image *as_image(GPUTexture *tex) { -- 2.30.2 From dd7948de9d1bcb110bd137e8d5bc2e97bb2ecbc6 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 19 Jun 2023 09:27:15 +0200 Subject: [PATCH 56/58] Attach missing render buffers --- source/blender/draw/engines/eevee_next/eevee_pipeline.cc | 3 +++ source/blender/draw/engines/eevee_next/eevee_view.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index edec07969fb..f21eeb7d546 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -108,6 +108,9 @@ void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) pass.material_set(manager, gpumat); pass.push_constant("world_opacity_fade", 1.0f); + pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + pass.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); + pass.bind_ubo(RBUFS_BUF_SLOT, &inst_.render_buffers.data); pass.bind_image("rp_normal_img", dummy_renderpass_tx_); pass.bind_image("rp_light_img", dummy_renderpass_tx_); pass.bind_image("rp_diffuse_color_img", dummy_renderpass_tx_); diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index c7dd6486358..9ba82467e1e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -118,8 +118,8 @@ void ShadingView::render() GPU_framebuffer_bind(combined_fb_); GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f); - inst_.pipelines.world.render(render_view_new_); inst_.pipelines.world_probe.render(); + inst_.pipelines.world.render(render_view_new_); /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */ inst_.lights.set_view(render_view_new_, extent_); -- 2.30.2 From 9fc915ee6716e8f478c72cdeddbc218003e13b60 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 19 Jun 2023 09:38:01 +0200 Subject: [PATCH 57/58] Add all missing texture/image bindings. --- source/blender/draw/engines/eevee_next/eevee_pipeline.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index f21eeb7d546..b3fc4e02ae8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -117,6 +117,8 @@ void WorldProbePipeline::sync(GPUMaterial *gpumat, int face) pass.bind_image("rp_specular_color_img", dummy_renderpass_tx_); pass.bind_image("rp_emission_img", dummy_renderpass_tx_); pass.bind_image("rp_cryptomatte_img", dummy_cryptomatte_tx_); + pass.bind_image("rp_color_img", dummy_aov_color_tx_); + pass.bind_image("rp_value_img", dummy_aov_value_tx_); /* Required by validation layers. */ inst_.cryptomatte.bind_resources(&pass); -- 2.30.2 From 94d9b911fc8a9e36e3598ea4c991156d7d875480 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 23 Jun 2023 15:32:43 +0200 Subject: [PATCH 58/58] Fix merge issues --- .../draw/engines/eevee_next/eevee_sampling.cc | 9 +---- .../shaders/eevee_deferred_light_frag.glsl | 5 +-- .../shaders/eevee_surfel_bounce_comp.glsl | 40 ------------------- .../shaders/eevee_surfel_ray_comp.glsl | 10 +++-- .../shaders/infos/eevee_deferred_info.hh | 1 - source/blender/makesdna/DNA_scene_defaults.h | 1 - source/blender/makesdna/DNA_scene_types.h | 1 - source/blender/makesrna/intern/rna_scene.c | 8 ---- 8 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 0f30b452ba1..f84849b1df0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -24,14 +24,7 @@ namespace blender::eevee { void Sampling::init(const Scene *scene) { - if (inst_.is_baking()) { - sample_count_ = max_ii(1, scene->eevee.gi_irradiance_samples); - sample_ = 0; - } - else { - sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : - scene->eevee.taa_render_samples; - } + sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : scene->eevee.taa_render_samples; if (sample_count_ == 0) { BLI_assert(inst_.is_viewport()); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index a47b3e93b9a..75dfc73b8f5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -58,10 +58,7 @@ void main() lightprobe_eval(diffuse_data, reflection_data, P, Ng, V, diffuse_light, reflection_light); - light_world_eval(diffuse_data, reflection_data, P, V, diffuse_light, reflection_light); - - light_eval( - diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light); + light_world_eval(reflection_data, P, V, reflection_light); light_eval(diffuse_data, reflection_data, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl deleted file mode 100644 index 3634f81196c..00000000000 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_bounce_comp.glsl +++ /dev/null @@ -1,40 +0,0 @@ - -/** - * Accumulate light from a bounce of indirect light into each surfel radiance. - * This feeds back the light for the next bounce. - * - * Dispatched as one thread per surfel. - */ - -#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -vec3 finalize_integration(vec4 radiance) -{ - /* Divide by sample count. */ - radiance.rgb *= safe_rcp(radiance.w); - /* TODO: Find why this is needed. */ - radiance.rgb *= 2.0; - /* Multiply by hemisphere area since we are integrating over it. */ - return radiance.rgb * M_TAU; -} - -void main() -{ - int surfel_index = int(gl_GlobalInvocationID.x); - if (surfel_index >= capture_info_buf.surfel_len) { - return; - } - Surfel surfel = surfel_buf[surfel_index]; - vec3 radiance_front = finalize_integration(surfel.incomming_light_front); - vec3 radiance_back = finalize_integration(surfel.incomming_light_back); - /* Re-inject the bounced light for the next bounce event. */ - surfel_buf[surfel_index].outgoing_light_front = radiance_front; - surfel_buf[surfel_index].outgoing_light_back = radiance_back; - /* Add to final radiance. */ - surfel_buf[surfel_index].radiance_front += radiance_front; - surfel_buf[surfel_index].radiance_back += radiance_back; - /* Reset accumulator for next bounce. */ - surfel_buf[surfel_index].incomming_light_front = vec4(0.0); - surfel_buf[surfel_index].incomming_light_back = vec4(0.0); -} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl index 9d662f7ff60..dffe8b0942a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_ray_comp.glsl @@ -14,7 +14,7 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(cubemap_lib.glsl) -void radiance_transfer(inout Surfel surfel, vec3 irradiance, vec3 L) +void radiance_transfer(inout Surfel surfel, vec3 in_radiance, vec3 L) { float NL = dot(surfel.normal, L); /* Lambertian BSDF. Albedo applied later depending on which side of the surfel was hit. */ @@ -70,6 +70,11 @@ void radiance_transfer_world(inout Surfel receiver, vec3 sky_L) radiance_transfer(receiver, radiance, -sky_L); } +vec3 radiance_sky_sample(vec3 R) +{ + return textureLod_cubemapArray(reflectionProbes, vec4(R, 0.0), 0.0).rgb; +} + void main() { int surfel_index = int(gl_GlobalInvocationID.x); @@ -99,6 +104,5 @@ void main() radiance_transfer(surfel, world_radiance, -sky_L); } - surfel_buf[surfel_index].incomming_light_front = surfel.incomming_light_front; - surfel_buf[surfel_index].incomming_light_back = surfel.incomming_light_back; + surfel_buf[surfel_index] = surfel; } diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh index 1868af3667e..63d29cbb562 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -39,7 +39,6 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light) "eevee_light_data", "eevee_lightprobe_data", "eevee_reflection_probe_data", - "eevee_lightprobe_data", "eevee_shadow_data", "eevee_deferred_base", "eevee_transmittance_data", diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 436ed0b8a50..7d32b304401 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -165,7 +165,6 @@ #define _DNA_DEFAULT_SceneEEVEE \ { \ .gi_diffuse_bounces = 3, \ - .gi_irradiance_samples = 512, \ .gi_cubemap_resolution = 512, \ .gi_visibility_resolution = 32, \ .gi_cubemap_draw_size = 0.3f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index e4690c5c057..306044e5f9c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1787,7 +1787,6 @@ typedef struct SceneDisplay { typedef struct SceneEEVEE { int flag; int gi_diffuse_bounces; - int gi_irradiance_samples; int gi_cubemap_resolution; int gi_visibility_resolution; float gi_irradiance_smoothing; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 880f31cd656..73cce2e6458 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -7449,14 +7449,6 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_range(prop, 0, INT_MAX); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - prop = RNA_def_property(srna, "gi_irradiance_samples", PROP_INT, PROP_NONE); - RNA_def_property_ui_text(prop, - "Diffuse Samples", - "Number of rays direction to evaluate when baking a single " - "bounce of indirect lighting"); - RNA_def_property_range(prop, 0, INT_MAX); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - prop = RNA_def_property(srna, "gi_cubemap_resolution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, eevee_shadow_size_items); RNA_def_property_ui_text(prop, "Cubemap Size", "Size of every cubemaps"); -- 2.30.2