Merge branch 'master' into blender2.8
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 19 Sep 2018 16:19:49 +0000 (18:19 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 19 Sep 2018 16:20:50 +0000 (18:20 +0200)
69 files changed:
1  2 
build_files/build_environment/install_deps.sh
build_files/cmake/config/blender_full.cmake
build_files/cmake/config/blender_headless.cmake
build_files/cmake/config/blender_release.cmake
build_files/cmake/config/bpy_module.cmake
build_files/cmake/macros.cmake
build_files/cmake/platform/platform_apple.cmake
build_files/cmake/platform/platform_unix.cmake
doc/python_api/rst/info_api_reference.rst
release/scripts/startup/bl_operators/object_randomize_transform.py
release/scripts/startup/bl_operators/uvcalc_smart_project.py
release/scripts/startup/bl_ui/properties_object.py
release/scripts/startup/bl_ui/space_view3d.py
source/blender/draw/engines/basic/basic_engine.c
source/blender/draw/engines/eevee/eevee_depth_of_field.c
source/blender/draw/engines/eevee/eevee_effects.c
source/blender/draw/engines/eevee/eevee_engine.c
source/blender/draw/engines/eevee/eevee_lightcache.c
source/blender/draw/engines/eevee/eevee_lightprobes.c
source/blender/draw/engines/eevee/eevee_lights.c
source/blender/draw/engines/eevee/eevee_materials.c
source/blender/draw/engines/eevee/eevee_motion_blur.c
source/blender/draw/engines/eevee/eevee_private.h
source/blender/draw/engines/eevee/eevee_screen_raytrace.c
source/blender/draw/engines/eevee/eevee_volumes.c
source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl
source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
source/blender/draw/engines/gpencil/gpencil_engine.c
source/blender/draw/engines/gpencil/gpencil_engine.h
source/blender/draw/engines/workbench/workbench_deferred.c
source/blender/draw/engines/workbench/workbench_forward.c
source/blender/draw/intern/draw_armature.c
source/blender/draw/intern/draw_cache_impl_mesh.c
source/blender/draw/intern/draw_cache_impl_particles.c
source/blender/draw/intern/draw_common.h
source/blender/draw/intern/draw_hair.c
source/blender/draw/intern/draw_instance_data.c
source/blender/draw/intern/draw_manager.c
source/blender/draw/intern/draw_manager.h
source/blender/draw/intern/draw_manager_data.c
source/blender/draw/intern/draw_manager_exec.c
source/blender/draw/modes/edit_curve_mode.c
source/blender/draw/modes/edit_lattice_mode.c
source/blender/draw/modes/edit_surface_mode.c
source/blender/draw/modes/edit_text_mode.c
source/blender/draw/modes/overlay_mode.c
source/blender/draw/modes/paint_texture_mode.c
source/blender/draw/modes/sculpt_mode.c
source/blender/draw/modes/shaders/armature_axes_vert.glsl
source/blender/draw/modes/shaders/armature_envelope_outline_vert.glsl
source/blender/draw/modes/shaders/armature_envelope_solid_vert.glsl
source/blender/draw/modes/shaders/armature_shape_outline_vert.glsl
source/blender/draw/modes/shaders/armature_shape_solid_vert.glsl
source/blender/draw/modes/shaders/armature_sphere_outline_vert.glsl
source/blender/draw/modes/shaders/armature_sphere_solid_vert.glsl
source/blender/draw/modes/shaders/armature_stick_vert.glsl
source/blender/draw/modes/shaders/common_hair_lib.glsl
source/blender/draw/modes/shaders/edit_mesh_overlay_frag.glsl
source/blender/draw/modes/shaders/object_empty_axes_vert.glsl
source/blender/draw/modes/shaders/object_empty_image_vert.glsl
source/blender/draw/modes/shaders/object_grid_frag.glsl
source/blender/draw/modes/shaders/object_mball_handles_vert.glsl
source/blender/draw/modes/shaders/object_outline_prepass_geom.glsl
source/blender/python/bmesh/bmesh_py_types.c
source/blender/python/intern/bpy_rna.c

@@@ -6,9 -6,10 +6,9 @@@
  #
  
  set(WITH_HEADLESS            ON  CACHE BOOL "" FORCE)
 -set(WITH_GAMEENGINE          OFF CACHE BOOL "" FORCE)
  
  # disable audio, its possible some devs may want this but for now disable
- # so the python module doesnt hold the audio device and loads quickly.
+ # so the python module doesn't hold the audio device and loads quickly.
  set(WITH_AUDASPACE           OFF CACHE BOOL "" FORCE)
  set(WITH_FFTW3               OFF CACHE BOOL "" FORCE)
  set(WITH_JACK                OFF CACHE BOOL "" FORCE)
@@@ -12,8 -12,11 +12,8 @@@ set(WITH_INSTALL_PORTABLE    OFF CACHE 
  # no point int copying python into python
  set(WITH_PYTHON_INSTALL      OFF CACHE BOOL "" FORCE)
  
 -# dont build the game engine
 -set(WITH_GAMEENGINE          OFF CACHE BOOL "" FORCE)
 -
  # disable audio, its possible some devs may want this but for now disable
- # so the python module doesnt hold the audio device and loads quickly.
+ # so the python module doesn't hold the audio device and loads quickly.
  set(WITH_AUDASPACE           OFF CACHE BOOL "" FORCE)
  set(WITH_FFTW3               OFF CACHE BOOL "" FORCE)
  set(WITH_JACK                OFF CACHE BOOL "" FORCE)
Simple merge
@@@ -154,8 -154,8 +154,8 @@@ class RandomizeLocRotSize(Operator)
          default=False,
      )
  
 -    '''scale_min = FloatProperty(
 +    '''scale_min: FloatProperty(
-             name="Minimun Scale Factor",
+             name="Minimum Scale Factor",
              description="Lowest scale percentage possible",
              min=-1.0, max=1.0, precision=3,
              default=0.15,
@@@ -189,30 -199,35 +189,30 @@@ class OBJECT_PT_collections(ObjectButto
          obj = context.object
  
          row = layout.row(align=True)
 -        if bpy.data.groups:
 -            row.operator("object.group_link", text="Add to Group")
 +        if bpy.data.collections:
 +            row.operator("object.collection_link", text="Add to Collection")
          else:
 -            row.operator("object.group_add", text="Add to Group")
 -        row.operator("object.group_add", text="", icon='ZOOMIN')
 +            row.operator("object.collection_add", text="Add to Collection")
 +        row.operator("object.collection_add", text="", icon='ZOOMIN')
  
          obj_name = obj.name
 -        for group in bpy.data.groups:
 +        for collection in bpy.data.collections:
-             # XXX this is slow and stupid!, we need 2 checks, one thats fast
+             # XXX this is slow and stupid!, we need 2 checks, one that's fast
              # and another that we can be sure its not a name collision
              # from linked library data
 -            group_objects = group.objects
 -            if obj_name in group.objects and obj in group_objects[:]:
 +            collection_objects = collection.objects
 +            if obj_name in collection.objects and obj in collection_objects[:]:
                  col = layout.column(align=True)
  
 -                col.context_pointer_set("group", group)
 +                col.context_pointer_set("collection", collection)
  
                  row = col.box().row()
 -                row.prop(group, "name", text="")
 -                row.operator("object.group_remove", text="", icon='X', emboss=False)
 -                row.menu("GROUP_MT_specials", icon='DOWNARROW_HLT', text="")
 +                row.prop(collection, "name", text="")
 +                row.operator("object.collection_remove", text="", icon='X', emboss=False)
 +                row.menu("COLLECTION_MT_specials", icon='DOWNARROW_HLT', text="")
  
 -                split = col.box().split()
 -
 -                col = split.column()
 -                col.prop(group, "layers", text="Dupli Visibility")
 -
 -                col = split.column()
 -                col.prop(group, "dupli_offset", text="")
 +                row = col.box().row()
 +                row.prop(collection, "dupli_offset", text="")
  
  
  class OBJECT_PT_display(ObjectButtonsPanel, Panel):
index d7928a6,0000000..2e3fdf3
mode 100644,000000..100644
--- /dev/null
@@@ -1,215 -1,0 +1,215 @@@
-                       /* Avoid loosing flat objects when in ortho views (see T56549) */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file basic_engine.c
 + *  \ingroup draw_engine
 + *
 + * Simple engine for drawing color and/or depth.
 + * When we only need simple flat shaders.
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BKE_icons.h"
 +#include "BKE_idprop.h"
 +#include "BKE_main.h"
 +#include "BKE_particle.h"
 +
 +#include "DNA_particle_types.h"
 +
 +#include "GPU_shader.h"
 +
 +#include "basic_engine.h"
 +/* Shaders */
 +
 +#define BASIC_ENGINE "BLENDER_BASIC"
 +
 +/* *********** LISTS *********** */
 +
 +/* GPUViewport.storage
 + * Is freed everytime the viewport engine changes */
 +typedef struct BASIC_StorageList {
 +      struct BASIC_PrivateData *g_data;
 +} BASIC_StorageList;
 +
 +typedef struct BASIC_PassList {
 +      struct DRWPass *depth_pass;
 +      struct DRWPass *depth_pass_cull;
 +} BASIC_PassList;
 +
 +typedef struct BASIC_Data {
 +      void *engine_type;
 +      DRWViewportEmptyList *fbl;
 +      DRWViewportEmptyList *txl;
 +      BASIC_PassList *psl;
 +      BASIC_StorageList *stl;
 +} BASIC_Data;
 +
 +/* *********** STATIC *********** */
 +
 +static struct {
 +      /* Depth Pre Pass */
 +      struct GPUShader *depth_sh;
 +} e_data = {NULL}; /* Engine data */
 +
 +typedef struct BASIC_PrivateData {
 +      DRWShadingGroup *depth_shgrp;
 +      DRWShadingGroup *depth_shgrp_cull;
 +      DRWShadingGroup *depth_shgrp_hair;
 +} BASIC_PrivateData; /* Transient data */
 +
 +/* Functions */
 +
 +static void basic_engine_init(void *UNUSED(vedata))
 +{
 +      /* Depth prepass */
 +      if (!e_data.depth_sh) {
 +              e_data.depth_sh = DRW_shader_create_3D_depth_only();
 +      }
 +}
 +
 +static void basic_cache_init(void *vedata)
 +{
 +      BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl;
 +      BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl;
 +
 +      if (!stl->g_data) {
 +              /* Alloc transient pointers */
 +              stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
 +      }
 +
 +      {
 +              psl->depth_pass = DRW_pass_create(
 +                      "Depth Pass", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WIRE);
 +              stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass);
 +
 +              psl->depth_pass_cull = DRW_pass_create(
 +                      "Depth Pass Cull",
 +                      DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK);
 +              stl->g_data->depth_shgrp_cull = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass_cull);
 +      }
 +}
 +
 +static void basic_cache_populate(void *vedata, Object *ob)
 +{
 +      BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl;
 +
 +      if (!DRW_object_is_renderable(ob)) {
 +              return;
 +      }
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      if (ob != draw_ctx->object_edit) {
 +              for (ParticleSystem *psys = ob->particlesystem.first;
 +                   psys != NULL;
 +                   psys = psys->next)
 +              {
 +                      if (!psys_check_enabled(ob, psys, false)) {
 +                              continue;
 +                      }
 +                      if (!DRW_check_psys_visible_within_active_context(ob, psys)) {
 +                              continue;
 +                      }
 +                      ParticleSettings *part = psys->part;
 +                      const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
 +                      if (draw_as == PART_DRAW_PATH) {
 +                              struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL);
 +                              DRW_shgroup_call_add(stl->g_data->depth_shgrp, hairs, NULL);
 +                      }
 +              }
 +      }
 +
 +      /* Make flat object selectable in ortho view if wireframe is enabled. */
 +      if ((draw_ctx->v3d->overlay.flag & V3D_OVERLAY_WIREFRAMES) ||
 +          (ob->dtx & OB_DRAWWIRE) ||
 +          (ob->dt == OB_WIRE))
 +      {
 +              int flat_axis = 0;
 +              bool is_flat_object_viewed_from_side = (draw_ctx->rv3d->persp == RV3D_ORTHO) &&
 +                                                     DRW_object_is_flat(ob, &flat_axis) &&
 +                                                     DRW_object_axis_orthogonal_to_view(ob, flat_axis);
 +
 +              if (is_flat_object_viewed_from_side) {
++                      /* Avoid losing flat objects when in ortho views (see T56549) */
 +                      struct GPUBatch *geom = DRW_cache_object_wire_outline_get(ob);
 +                      DRW_shgroup_call_object_add(stl->g_data->depth_shgrp, geom, ob);
 +                      return;
 +              }
 +      }
 +
 +      struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
 +      if (geom) {
 +              const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->flag2 & V3D_BACKFACE_CULLING));
 +              /* Depth Prepass */
 +              DRW_shgroup_call_add((do_cull) ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp, geom, ob->obmat);
 +      }
 +}
 +
 +static void basic_cache_finish(void *vedata)
 +{
 +      BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl;
 +
 +      UNUSED_VARS(stl);
 +}
 +
 +static void basic_draw_scene(void *vedata)
 +{
 +      BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl;
 +
 +      DRW_draw_pass(psl->depth_pass);
 +      DRW_draw_pass(psl->depth_pass_cull);
 +}
 +
 +static void basic_engine_free(void)
 +{
 +      /* all shaders are builtin */
 +}
 +
 +static const DrawEngineDataSize basic_data_size = DRW_VIEWPORT_DATA_SIZE(BASIC_Data);
 +
 +DrawEngineType draw_engine_basic_type = {
 +      NULL, NULL,
 +      N_("Basic"),
 +      &basic_data_size,
 +      &basic_engine_init,
 +      &basic_engine_free,
 +      &basic_cache_init,
 +      &basic_cache_populate,
 +      &basic_cache_finish,
 +      NULL,
 +      &basic_draw_scene,
 +      NULL,
 +      NULL,
 +      NULL,
 +};
 +
 +/* Note: currently unused, we may want to register so we can see this when debugging the view. */
 +
 +RenderEngineType DRW_engine_viewport_basic_type = {
 +      NULL, NULL,
 +      BASIC_ENGINE, N_("Basic"), RE_INTERNAL,
 +      NULL, NULL, NULL, NULL, NULL, NULL, NULL,
 +      &draw_engine_basic_type,
 +      {NULL, NULL, NULL}
 +};
 +
 +
 +#undef BASIC_ENGINE
index cb54ad3,0000000..e51fb2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,264 -1,0 +1,264 @@@
-                       /* Retreive Near and Far distance */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_depth_of_field.c
 + *  \ingroup draw_engine
 + *
 + * Depth of field post process effect.
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BLI_dynstr.h"
 +#include "BLI_rand.h"
 +
 +#include "DNA_anim_types.h"
 +#include "DNA_camera_types.h"
 +#include "DNA_object_force_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_view3d_types.h"
 +#include "DNA_world_types.h"
 +
 +#include "BKE_global.h" /* for G.debug_value */
 +#include "BKE_camera.h"
 +#include "BKE_mesh.h"
 +#include "BKE_object.h"
 +#include "BKE_animsys.h"
 +#include "BKE_screen.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_query.h"
 +
 +#include "eevee_private.h"
 +#include "GPU_extensions.h"
 +#include "GPU_framebuffer.h"
 +#include "GPU_texture.h"
 +
 +#include "ED_screen.h"
 +
 +static struct {
 +      /* Depth Of Field */
 +      struct GPUShader *dof_downsample_sh;
 +      struct GPUShader *dof_scatter_sh;
 +      struct GPUShader *dof_resolve_sh;
 +} e_data = {NULL}; /* Engine data */
 +
 +extern char datatoc_effect_dof_vert_glsl[];
 +extern char datatoc_effect_dof_frag_glsl[];
 +
 +static void eevee_create_shader_depth_of_field(void)
 +{
 +      e_data.dof_downsample_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_dof_frag_glsl, "#define STEP_DOWNSAMPLE\n");
 +      e_data.dof_scatter_sh = DRW_shader_create(
 +              datatoc_effect_dof_vert_glsl, NULL,
 +              datatoc_effect_dof_frag_glsl, "#define STEP_SCATTER\n");
 +      e_data.dof_resolve_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_dof_frag_glsl, "#define STEP_RESOLVE\n");
 +}
 +
 +int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *camera)
 +{
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_EffectsInfo *effects = stl->effects;
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
 +
 +      if (scene_eval->eevee.flag & SCE_EEVEE_DOF_ENABLED) {
 +              RegionView3D *rv3d = draw_ctx->rv3d;
 +
 +              if (!e_data.dof_downsample_sh) {
 +                      eevee_create_shader_depth_of_field();
 +              }
 +
 +              if (camera) {
 +                      const float *viewport_size = DRW_viewport_size_get();
 +                      Camera *cam = (Camera *)camera->data;
 +
++                      /* Retrieve Near and Far distance */
 +                      effects->dof_near_far[0] = -cam->clipsta;
 +                      effects->dof_near_far[1] = -cam->clipend;
 +
 +                      int buffer_size[2] = {(int)viewport_size[0] / 2, (int)viewport_size[1] / 2};
 +
 +                      effects->dof_down_near = DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], GPU_R11F_G11F_B10F,
 +                                                                         &draw_engine_eevee_type);
 +                      effects->dof_down_far =  DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], GPU_R11F_G11F_B10F,
 +                                                                         &draw_engine_eevee_type);
 +                      effects->dof_coc =       DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], GPU_RG16F,
 +                                                                         &draw_engine_eevee_type);
 +
 +                      GPU_framebuffer_ensure_config(&fbl->dof_down_fb, {
 +                              GPU_ATTACHMENT_NONE,
 +                              GPU_ATTACHMENT_TEXTURE(effects->dof_down_near),
 +                              GPU_ATTACHMENT_TEXTURE(effects->dof_down_far),
 +                              GPU_ATTACHMENT_TEXTURE(effects->dof_coc)
 +                      });
 +
 +                      /* Go full 32bits for rendering and reduce the color artifacts. */
 +                      GPUTextureFormat fb_format = DRW_state_is_image_render() ? GPU_RGBA32F : GPU_RGBA16F;
 +
 +                      effects->dof_blur = DRW_texture_pool_query_2D(buffer_size[0] * 2, buffer_size[1], fb_format,
 +                                                                    &draw_engine_eevee_type);
 +                      GPU_framebuffer_ensure_config(&fbl->dof_scatter_fb, {
 +                              GPU_ATTACHMENT_NONE,
 +                              GPU_ATTACHMENT_TEXTURE(effects->dof_blur),
 +                      });
 +
 +                      /* Parameters */
 +                      /* TODO UI Options */
 +                      float fstop = cam->gpu_dof.fstop;
 +                      float blades = cam->gpu_dof.num_blades;
 +                      float rotation = cam->gpu_dof.rotation;
 +                      float ratio = 1.0f / cam->gpu_dof.ratio;
 +                      float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
 +                      float focus_dist = BKE_camera_object_dof_distance(camera);
 +                      float focal_len = cam->lens;
 +
 +                      /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm
 +                       * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though
 +                       * because the shader reads coordinates in world space, which is in blender units.
 +                       * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */
 +                      float scale = (scene_eval->unit.system) ? scene_eval->unit.scale_length : 1.0f;
 +                      float scale_camera = 0.001f / scale;
 +                      /* we want radius here for the aperture number  */
 +                      float aperture = 0.5f * scale_camera * focal_len / fstop;
 +                      float focal_len_scaled = scale_camera * focal_len;
 +                      float sensor_scaled = scale_camera * sensor;
 +
 +                      if (rv3d != NULL) {
 +                              sensor_scaled *= rv3d->viewcamtexcofac[0];
 +                      }
 +
 +                      effects->dof_params[0] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
 +                      effects->dof_params[1] = -focus_dist;
 +                      effects->dof_params[2] = viewport_size[0] / sensor_scaled;
 +                      effects->dof_bokeh[0] = rotation;
 +                      effects->dof_bokeh[1] = ratio;
 +                      effects->dof_bokeh[2] = scene_eval->eevee.bokeh_max_size;
 +
 +                      /* Precompute values to save instructions in fragment shader. */
 +                      effects->dof_bokeh_sides[0] = blades;
 +                      effects->dof_bokeh_sides[1] = 2.0f * M_PI / blades;
 +                      effects->dof_bokeh_sides[2] = blades / (2.0f * M_PI);
 +                      effects->dof_bokeh_sides[3] = cosf(M_PI / blades);
 +
 +                      return EFFECT_DOF | EFFECT_POST_BUFFER;
 +              }
 +      }
 +
 +      /* Cleanup to release memory */
 +      GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_down_fb);
 +      GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_fb);
 +
 +      return 0;
 +}
 +
 +void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_EffectsInfo *effects = stl->effects;
 +      DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 +
 +      if ((effects->enabled_effects & EFFECT_DOF) != 0) {
 +              /**  Depth of Field algorithm
 +               *
 +               * Overview :
 +               * - Downsample the color buffer into 2 buffers weighted with
 +               *   CoC values. Also output CoC into a texture.
 +               * - Shoot quads for every pixel and expand it depending on the CoC.
 +               *   Do one pass for near Dof and one pass for far Dof.
 +               * - Finally composite the 2 blurred buffers with the original render.
 +               **/
 +              DRWShadingGroup *grp;
 +              struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
 +
 +              psl->dof_down = DRW_pass_create("DoF Downsample", DRW_STATE_WRITE_COLOR);
 +
 +              grp = DRW_shgroup_create(e_data.dof_downsample_sh, psl->dof_down);
 +              DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 +              DRW_shgroup_uniform_vec2(grp, "nearFar", effects->dof_near_far, 1);
 +              DRW_shgroup_uniform_vec3(grp, "dofParams", effects->dof_params, 1);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +
 +              psl->dof_scatter = DRW_pass_create("DoF Scatter", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE_FULL);
 +
 +              /* This create an empty batch of N triangles to be positioned
 +               * by the vertex shader 0.4ms against 6ms with instancing */
 +              const float *viewport_size = DRW_viewport_size_get();
 +              const int sprite_len = ((int)viewport_size[0] / 2) * ((int)viewport_size[1] / 2); /* brackets matters */
 +              grp = DRW_shgroup_empty_tri_batch_create(e_data.dof_scatter_sh, psl->dof_scatter, sprite_len);
 +
 +              DRW_shgroup_uniform_texture_ref(grp, "nearBuffer", &effects->dof_down_near);
 +              DRW_shgroup_uniform_texture_ref(grp, "farBuffer", &effects->dof_down_far);
 +              DRW_shgroup_uniform_texture_ref(grp, "cocBuffer", &effects->dof_coc);
 +              DRW_shgroup_uniform_vec4(grp, "bokehParams", effects->dof_bokeh, 2);
 +
 +              psl->dof_resolve = DRW_pass_create("DoF Resolve", DRW_STATE_WRITE_COLOR);
 +
 +              grp = DRW_shgroup_create(e_data.dof_resolve_sh, psl->dof_resolve);
 +              DRW_shgroup_uniform_texture_ref(grp, "scatterBuffer", &effects->dof_blur);
 +              DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 +              DRW_shgroup_uniform_vec2(grp, "nearFar", effects->dof_near_far, 1);
 +              DRW_shgroup_uniform_vec3(grp, "dofParams", effects->dof_params, 1);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +      }
 +}
 +
 +void EEVEE_depth_of_field_draw(EEVEE_Data *vedata)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_EffectsInfo *effects = stl->effects;
 +
 +      /* Depth Of Field */
 +      if ((effects->enabled_effects & EFFECT_DOF) != 0) {
 +              float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 +
 +              /* Downsample */
 +              GPU_framebuffer_bind(fbl->dof_down_fb);
 +              DRW_draw_pass(psl->dof_down);
 +
 +              /* Scatter */
 +              GPU_framebuffer_bind(fbl->dof_scatter_fb);
 +              GPU_framebuffer_clear_color(fbl->dof_scatter_fb, clear_col);
 +              DRW_draw_pass(psl->dof_scatter);
 +
 +              /* Resolve */
 +              GPU_framebuffer_bind(effects->target_buffer);
 +              DRW_draw_pass(psl->dof_resolve);
 +              SWAP_BUFFERS();
 +      }
 +}
 +
 +void EEVEE_depth_of_field_free(void)
 +{
 +      DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh);
 +}
index 0a93db1,0000000..bb3938f
mode 100644,000000..100644
--- /dev/null
@@@ -1,584 -1,0 +1,584 @@@
-        * Compute Mipmap texel alignement.
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_effects.c
 + *  \ingroup draw_engine
 + *
 + * Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BKE_global.h" /* for G.debug_value */
 +
 +#include "BLI_string_utils.h"
 +
 +#include "eevee_private.h"
 +#include "GPU_texture.h"
 +#include "GPU_extensions.h"
 +
 +static struct {
 +      /* Downsample Depth */
 +      struct GPUShader *minz_downlevel_sh;
 +      struct GPUShader *maxz_downlevel_sh;
 +      struct GPUShader *minz_downdepth_sh;
 +      struct GPUShader *maxz_downdepth_sh;
 +      struct GPUShader *minz_downdepth_layer_sh;
 +      struct GPUShader *maxz_downdepth_layer_sh;
 +      struct GPUShader *maxz_copydepth_layer_sh;
 +      struct GPUShader *minz_copydepth_sh;
 +      struct GPUShader *maxz_copydepth_sh;
 +
 +      /* Simple Downsample */
 +      struct GPUShader *downsample_sh;
 +      struct GPUShader *downsample_cube_sh;
 +
 +      /* Velocity Resolve */
 +      struct GPUShader *velocity_resolve_sh;
 +
 +      /* Theses are just references, not actually allocated */
 +      struct GPUTexture *depth_src;
 +      struct GPUTexture *color_src;
 +
 +      int depth_src_layer;
 +      float cube_texel_size;
 +} e_data = {NULL}; /* Engine data */
 +
 +extern char datatoc_common_uniforms_lib_glsl[];
 +extern char datatoc_common_view_lib_glsl[];
 +extern char datatoc_bsdf_common_lib_glsl[];
 +extern char datatoc_effect_velocity_resolve_frag_glsl[];
 +extern char datatoc_effect_minmaxz_frag_glsl[];
 +extern char datatoc_effect_downsample_frag_glsl[];
 +extern char datatoc_effect_downsample_cube_frag_glsl[];
 +extern char datatoc_lightprobe_vert_glsl[];
 +extern char datatoc_lightprobe_geom_glsl[];
 +
 +static void eevee_create_shader_downsample(void)
 +{
 +      char *frag_str = BLI_string_joinN(
 +          datatoc_common_uniforms_lib_glsl,
 +          datatoc_common_view_lib_glsl,
 +          datatoc_bsdf_common_lib_glsl,
 +          datatoc_effect_velocity_resolve_frag_glsl);
 +
 +      e_data.velocity_resolve_sh = DRW_shader_create_fullscreen(frag_str, NULL);
 +
 +      MEM_freeN(frag_str);
 +
 +      e_data.downsample_sh = DRW_shader_create_fullscreen(datatoc_effect_downsample_frag_glsl, NULL);
 +      e_data.downsample_cube_sh = DRW_shader_create(
 +              datatoc_lightprobe_vert_glsl,
 +              datatoc_lightprobe_geom_glsl,
 +              datatoc_effect_downsample_cube_frag_glsl, NULL);
 +
 +      e_data.minz_downlevel_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MIN_PASS\n");
 +      e_data.maxz_downlevel_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MAX_PASS\n");
 +      e_data.minz_downdepth_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MIN_PASS\n");
 +      e_data.maxz_downdepth_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MAX_PASS\n");
 +      e_data.minz_downdepth_layer_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MIN_PASS\n"
 +              "#define LAYERED\n");
 +      e_data.maxz_downdepth_layer_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MAX_PASS\n"
 +              "#define LAYERED\n");
 +      e_data.maxz_copydepth_layer_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MAX_PASS\n"
 +              "#define COPY_DEPTH\n"
 +              "#define LAYERED\n");
 +      e_data.minz_copydepth_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MIN_PASS\n"
 +              "#define COPY_DEPTH\n");
 +      e_data.maxz_copydepth_sh = DRW_shader_create_fullscreen(
 +              datatoc_effect_minmaxz_frag_glsl,
 +              "#define MAX_PASS\n"
 +              "#define COPY_DEPTH\n");
 +}
 +
 +#define SETUP_BUFFER(tex, fb, fb_color) { \
 +      DRW_texture_ensure_fullscreen_2D(&tex, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP); \
 +      GPU_framebuffer_ensure_config(&fb, { \
 +              GPU_ATTACHMENT_TEXTURE(dtxl->depth), \
 +              GPU_ATTACHMENT_TEXTURE(tex), \
 +      }); \
 +      GPU_framebuffer_ensure_config(&fb_color, { \
 +              GPU_ATTACHMENT_NONE, \
 +              GPU_ATTACHMENT_TEXTURE(tex), \
 +      }); \
 +}
 +
 +#define CLEANUP_BUFFER(tex, fb, fb_color) { \
 +      /* Cleanup to release memory */ \
 +      DRW_TEXTURE_FREE_SAFE(tex); \
 +      GPU_FRAMEBUFFER_FREE_SAFE(fb); \
 +      GPU_FRAMEBUFFER_FREE_SAFE(fb_color); \
 +}
 +
 +void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera)
 +{
 +      EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_EffectsInfo *effects;
 +      DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      ViewLayer *view_layer = draw_ctx->view_layer;
 +
 +      const float *viewport_size = DRW_viewport_size_get();
 +      int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
 +
 +      /* Shaders */
 +      if (!e_data.downsample_sh) {
 +              eevee_create_shader_downsample();
 +      }
 +
 +      if (!stl->effects) {
 +              stl->effects = MEM_callocN(sizeof(EEVEE_EffectsInfo), "EEVEE_EffectsInfo");
 +      }
 +
 +      effects = stl->effects;
 +
 +      effects->enabled_effects = 0;
 +      effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0;
 +      effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata, camera);
 +      effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata);
 +      effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera);
 +      effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata);
 +      effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata);
 +      effects->enabled_effects |= EEVEE_subsurface_init(sldata, vedata);
 +      effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, vedata);
 +      effects->enabled_effects |= EEVEE_volumes_init(sldata, vedata);
 +
 +      /* Force normal buffer creation. */
 +      if (DRW_state_is_image_render() &&
 +          (view_layer->passflag & SCE_PASS_NORMAL) != 0)
 +      {
 +              effects->enabled_effects |= EFFECT_NORMAL_BUFFER;
 +      }
 +
 +      /**
 +       * Ping Pong buffer
 +       */
 +      if ((effects->enabled_effects & EFFECT_POST_BUFFER) != 0) {
 +              SETUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
 +      }
 +      else {
 +              CLEANUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
 +      }
 +
 +      /**
 +       * MinMax Pyramid
 +       */
 +      const bool half_res_hiz = true;
 +      int size[2], div;
 +      common_data->hiz_mip_offset = (half_res_hiz) ? 1 : 0;
 +      div = (half_res_hiz) ? 2 : 1;
 +      size[0] = max_ii(size_fs[0] / div, 1);
 +      size[1] = max_ii(size_fs[1] / div, 1);
 +
 +      if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
 +              /* Intel gpu seems to have problem rendering to only depth format */
 +              DRW_texture_ensure_2D(&txl->maxzbuffer, size[0], size[1], GPU_R32F, DRW_TEX_MIPMAP);
 +      }
 +      else {
 +              DRW_texture_ensure_2D(&txl->maxzbuffer, size[0], size[1], GPU_DEPTH_COMPONENT24, DRW_TEX_MIPMAP);
 +      }
 +
 +      if (fbl->downsample_fb == NULL) {
 +              fbl->downsample_fb = GPU_framebuffer_create();
 +      }
 +
 +      /**
++       * Compute Mipmap texel alignment.
 +       */
 +      for (int i = 0; i < 10; ++i) {
 +              int mip_size[2];
 +              GPU_texture_get_mipmap_size(txl->color, i, mip_size);
 +              common_data->mip_ratio[i][0] = viewport_size[0] / (mip_size[0] * powf(2.0f, i));
 +              common_data->mip_ratio[i][1] = viewport_size[1] / (mip_size[1] * powf(2.0f, i));
 +      }
 +
 +      /**
 +       * Normal buffer for deferred passes.
 +       */
 +      if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0)     {
 +              effects->ssr_normal_input = DRW_texture_pool_query_2D(size_fs[0], size_fs[1], GPU_RG16,
 +                                                                    &draw_engine_eevee_type);
 +
 +              GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0);
 +      }
 +      else {
 +              effects->ssr_normal_input = NULL;
 +      }
 +
 +      /**
 +       * Motion vector buffer for correct TAA / motion blur.
 +       */
 +      if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
 +              effects->velocity_tx = DRW_texture_pool_query_2D(size_fs[0], size_fs[1], GPU_RG16,
 +                                                               &draw_engine_eevee_type);
 +
 +              /* TODO output objects velocity during the mainpass. */
 +              // GPU_framebuffer_texture_attach(fbl->main_fb, effects->velocity_tx, 1, 0);
 +
 +              GPU_framebuffer_ensure_config(&fbl->velocity_resolve_fb, {
 +                      GPU_ATTACHMENT_NONE,
 +                      GPU_ATTACHMENT_TEXTURE(effects->velocity_tx)
 +              });
 +      }
 +      else {
 +              effects->velocity_tx = NULL;
 +      }
 +
 +      /**
 +       * Setup depth double buffer.
 +       */
 +      if ((effects->enabled_effects & EFFECT_DEPTH_DOUBLE_BUFFER) != 0) {
 +              DRW_texture_ensure_fullscreen_2D(&txl->depth_double_buffer, GPU_DEPTH24_STENCIL8, 0);
 +
 +              GPU_framebuffer_ensure_config(&fbl->double_buffer_depth_fb, {
 +                      GPU_ATTACHMENT_TEXTURE(txl->depth_double_buffer)
 +              });
 +      }
 +      else {
 +              /* Cleanup to release memory */
 +              DRW_TEXTURE_FREE_SAFE(txl->depth_double_buffer);
 +              GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_depth_fb);
 +      }
 +
 +      /**
 +       * Setup double buffer so we can access last frame as it was before post processes.
 +       */
 +      if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) {
 +              SETUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
 +      }
 +      else {
 +              CLEANUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
 +      }
 +
 +      if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
 +              SETUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
 +      }
 +      else {
 +              CLEANUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
 +      }
 +}
 +
 +void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_EffectsInfo *effects = stl->effects;
 +      int downsample_write = DRW_STATE_WRITE_DEPTH;
 +
 +      /* Intel gpu seems to have problem rendering to only depth format.
 +       * Use color texture instead. */
 +      if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
 +              downsample_write = DRW_STATE_WRITE_COLOR;
 +      }
 +
 +      struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
 +
 +      {
 +              psl->color_downsample_ps = DRW_pass_create(
 +                      "Downsample", DRW_STATE_WRITE_COLOR);
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.downsample_sh, psl->color_downsample_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
 +              DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +      }
 +
 +      {
 +              static int zero = 0;
 +              static uint six = 6;
 +              psl->color_downsample_cube_ps = DRW_pass_create(
 +                      "Downsample Cube", DRW_STATE_WRITE_COLOR);
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.downsample_cube_sh, psl->color_downsample_cube_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
 +              DRW_shgroup_uniform_float(grp, "texelSize", &e_data.cube_texel_size, 1);
 +              DRW_shgroup_uniform_int(grp, "Layer", &zero, 1);
 +              DRW_shgroup_call_instances_add(grp, quad, NULL, &six);
 +      }
 +
 +      {
 +              /* Perform min/max downsample */
 +              DRWShadingGroup *grp;
 +
 +              psl->maxz_downlevel_ps = DRW_pass_create(
 +                      "HiZ Max Down Level", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 +              grp = DRW_shgroup_create(e_data.maxz_downlevel_sh, psl->maxz_downlevel_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &txl->maxzbuffer);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +
 +              /* Copy depth buffer to halfres top level of HiZ */
 +
 +              psl->maxz_downdepth_ps = DRW_pass_create(
 +                      "HiZ Max Copy Depth Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 +              grp = DRW_shgroup_create(e_data.maxz_downdepth_sh, psl->maxz_downdepth_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +
 +              psl->maxz_downdepth_layer_ps = DRW_pass_create(
 +                      "HiZ Max Copy DepthLayer Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 +              grp = DRW_shgroup_create(e_data.maxz_downdepth_layer_sh, psl->maxz_downdepth_layer_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 +              DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +
 +              psl->maxz_copydepth_ps = DRW_pass_create(
 +                      "HiZ Max Copy Depth Fullres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 +              grp = DRW_shgroup_create(e_data.maxz_copydepth_sh, psl->maxz_copydepth_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +
 +              psl->maxz_copydepth_layer_ps = DRW_pass_create(
 +                      "HiZ Max Copy DepthLayer Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 +              grp = DRW_shgroup_create(e_data.maxz_copydepth_layer_sh, psl->maxz_copydepth_layer_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 +              DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +      }
 +
 +      if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
 +              /* This pass compute camera motions to the non moving objects. */
 +              psl->velocity_resolve = DRW_pass_create(
 +                      "Velocity Resolve", DRW_STATE_WRITE_COLOR);
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.velocity_resolve_sh, psl->velocity_resolve);
 +              DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 +              DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +              DRW_shgroup_uniform_mat4(grp, "currPersinv", effects->velocity_curr_persinv);
 +              DRW_shgroup_uniform_mat4(grp, "pastPersmat", effects->velocity_past_persmat);
 +              DRW_shgroup_call_add(grp, quad, NULL);
 +      }
 +}
 +
 +#if 0 /* Not required for now */
 +static void min_downsample_cb(void *vedata, int UNUSED(level))
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      DRW_draw_pass(psl->minz_downlevel_ps);
 +}
 +#endif
 +
 +static void max_downsample_cb(void *vedata, int UNUSED(level))
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      DRW_draw_pass(psl->maxz_downlevel_ps);
 +}
 +
 +static void simple_downsample_cb(void *vedata, int UNUSED(level))
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      DRW_draw_pass(psl->color_downsample_ps);
 +}
 +
 +static void simple_downsample_cube_cb(void *vedata, int level)
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      e_data.cube_texel_size = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src);
 +      DRW_draw_pass(psl->color_downsample_cube_ps);
 +}
 +
 +void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int layer)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_TextureList *txl = vedata->txl;
 +
 +      e_data.depth_src = depth_src;
 +      e_data.depth_src_layer = layer;
 +
 +#if 0 /* Not required for now */
 +      DRW_stats_group_start("Min buffer");
 +      /* Copy depth buffer to min texture top level */
 +      GPU_framebuffer_texture_attach(fbl->downsample_fb, stl->g_data->minzbuffer, 0, 0);
 +      GPU_framebuffer_bind(fbl->downsample_fb);
 +      if (layer >= 0) {
 +              DRW_draw_pass(psl->minz_downdepth_layer_ps);
 +      }
 +      else {
 +              DRW_draw_pass(psl->minz_downdepth_ps);
 +      }
 +      GPU_framebuffer_texture_detach(stl->g_data->minzbuffer);
 +
 +      /* Create lower levels */
 +      GPU_framebuffer_recursive_downsample(fbl->downsample_fb, stl->g_data->minzbuffer, 8, &min_downsample_cb, vedata);
 +      DRW_stats_group_end();
 +#endif
 +      int minmax_size[3], depth_size[3];
 +      GPU_texture_get_mipmap_size(depth_src, 0, depth_size);
 +      GPU_texture_get_mipmap_size(txl->maxzbuffer, 0, minmax_size);
 +      bool is_full_res_minmaxz = (minmax_size[0] == depth_size[0] && minmax_size[1] == depth_size[1]);
 +
 +      DRW_stats_group_start("Max buffer");
 +      /* Copy depth buffer to max texture top level */
 +      GPU_framebuffer_texture_attach(fbl->downsample_fb, txl->maxzbuffer, 0, 0);
 +      GPU_framebuffer_bind(fbl->downsample_fb);
 +      if (layer >= 0) {
 +              if (is_full_res_minmaxz) {
 +                      DRW_draw_pass(psl->maxz_copydepth_layer_ps);
 +              }
 +              else {
 +                      DRW_draw_pass(psl->maxz_downdepth_layer_ps);
 +              }
 +      }
 +      else {
 +              if (is_full_res_minmaxz) {
 +                      DRW_draw_pass(psl->maxz_copydepth_ps);
 +              }
 +              else {
 +                      DRW_draw_pass(psl->maxz_downdepth_ps);
 +              }
 +      }
 +
 +      /* Create lower levels */
 +      GPU_framebuffer_recursive_downsample(fbl->downsample_fb, 8, &max_downsample_cb, vedata);
 +      GPU_framebuffer_texture_detach(fbl->downsample_fb, txl->maxzbuffer);
 +      DRW_stats_group_end();
 +
 +      /* Restore */
 +      GPU_framebuffer_bind(fbl->main_fb);
 +}
 +
 +/**
 + * Simple downsampling algorithm. Reconstruct mip chain up to mip level.
 + **/
 +void EEVEE_downsample_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
 +{
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      e_data.color_src = texture_src;
 +
 +      /* Create lower levels */
 +      DRW_stats_group_start("Downsample buffer");
 +      GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
 +      GPU_framebuffer_recursive_downsample(fbl->downsample_fb, level, &simple_downsample_cb, vedata);
 +      GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
 +      DRW_stats_group_end();
 +}
 +
 +/**
 + * Simple downsampling algorithm for cubemap. Reconstruct mip chain up to mip level.
 + **/
 +void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
 +{
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      e_data.color_src = texture_src;
 +
 +      /* Create lower levels */
 +      DRW_stats_group_start("Downsample Cube buffer");
 +      GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
 +      GPU_framebuffer_recursive_downsample(fbl->downsample_fb, level, &simple_downsample_cube_cb, vedata);
 +      GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
 +      DRW_stats_group_end();
 +}
 +
 +void EEVEE_draw_effects(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_EffectsInfo *effects = stl->effects;
 +
 +      /* First resolve the velocity. */
 +      if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
 +              DRW_viewport_matrix_get(effects->velocity_curr_persinv, DRW_MAT_PERSINV);
 +
 +              GPU_framebuffer_bind(fbl->velocity_resolve_fb);
 +              DRW_draw_pass(psl->velocity_resolve);
 +      }
 +      DRW_viewport_matrix_get(effects->velocity_past_persmat, DRW_MAT_PERS);
 +
 +      /* only once per frame after the first post process */
 +      effects->swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0);
 +
 +      /* Init pointers */
 +      effects->source_buffer = txl->color; /* latest updated texture */
 +      effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
 +
 +      /* Post process stack (order matters) */
 +      EEVEE_motion_blur_draw(vedata);
 +      EEVEE_depth_of_field_draw(vedata);
 +      EEVEE_temporal_sampling_draw(vedata);
 +      EEVEE_bloom_draw(vedata);
 +
 +      /* Save the final texture and framebuffer for final transformation or read. */
 +      effects->final_tx = effects->source_buffer;
 +      effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb : fbl->effect_fb;
 +      if ((effects->enabled_effects & EFFECT_TAA) &&
 +          (effects->source_buffer == txl->taa_history))
 +      {
 +              effects->final_fb = fbl->taa_history_fb;
 +      }
 +
 +      /* If no post processes is enabled, buffers are still not swapped, do it now. */
 +      SWAP_DOUBLE_BUFFERS();
 +
 +      if (!stl->g_data->valid_double_buffer &&
 +          ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) &&
 +          (DRW_state_is_image_render() == false))
 +      {
 +              /* If history buffer is not valid request another frame.
 +               * This fix black reflections on area resize. */
 +              DRW_viewport_request_redraw();
 +      }
 +
 +      /* Record pers matrix for the next frame. */
 +      DRW_viewport_matrix_get(stl->effects->prev_persmat, DRW_MAT_PERS);
 +
 +      /* Update double buffer status if render mode. */
 +      if (DRW_state_is_image_render()) {
 +              stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
 +              stl->g_data->valid_taa_history = (txl->taa_history != NULL);;
 +      }
 +}
 +
 +void EEVEE_effects_free(void)
 +{
 +      DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
 +
 +      DRW_SHADER_FREE_SAFE(e_data.downsample_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.downsample_cube_sh);
 +
 +      DRW_SHADER_FREE_SAFE(e_data.minz_downlevel_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.maxz_downlevel_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.minz_downdepth_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.maxz_downdepth_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.minz_downdepth_layer_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.maxz_downdepth_layer_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.maxz_copydepth_layer_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.minz_copydepth_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.maxz_copydepth_sh);
 +}
index a566158,0000000..c5531ba
mode 100644,000000..100644
--- /dev/null
@@@ -1,478 -1,0 +1,478 @@@
-       /* Debug : Ouput buffer to view. */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_engine.c
 + *  \ingroup draw_engine
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BLI_dynstr.h"
 +#include "BLI_rand.h"
 +
 +#include "BKE_object.h"
 +#include "BKE_global.h" /* for G.debug_value */
 +#include "BKE_screen.h"
 +
 +#include "DNA_world_types.h"
 +
 +#include "ED_screen.h"
 +
 +#include "GPU_material.h"
 +#include "GPU_glew.h"
 +
 +#include "eevee_engine.h"
 +#include "eevee_private.h"
 +
 +#define EEVEE_ENGINE "BLENDER_EEVEE"
 +
 +extern GlobalsUboStorage ts;
 +
 +/* *********** FUNCTIONS *********** */
 +
 +static void eevee_engine_init(void *ved)
 +{
 +      EEVEE_Data *vedata = (EEVEE_Data *)ved;
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +      DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      View3D *v3d = draw_ctx->v3d;
 +      RegionView3D *rv3d = draw_ctx->rv3d;
 +      Object *camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
 +
 +      if (!stl->g_data) {
 +              /* Alloc transient pointers */
 +              stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
 +      }
 +      stl->g_data->use_color_view_settings = USE_SCENE_LIGHT(v3d) || !LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d);
 +      stl->g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
 +      stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
 +      stl->g_data->valid_taa_history = (txl->taa_history != NULL);
 +
 +      /* Main Buffer */
 +      DRW_texture_ensure_fullscreen_2D(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
 +
 +      GPU_framebuffer_ensure_config(&fbl->main_fb, {
 +              GPU_ATTACHMENT_TEXTURE(dtxl->depth),
 +              GPU_ATTACHMENT_TEXTURE(txl->color),
 +              GPU_ATTACHMENT_LEAVE,
 +              GPU_ATTACHMENT_LEAVE,
 +              GPU_ATTACHMENT_LEAVE,
 +              GPU_ATTACHMENT_LEAVE
 +      });
 +
 +      GPU_framebuffer_ensure_config(&fbl->main_color_fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_TEXTURE(txl->color)
 +      });
 +
 +      if (sldata->common_ubo == NULL) {
 +              sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), &sldata->common_data);
 +      }
 +      if (sldata->clip_ubo == NULL) {
 +              sldata->clip_ubo = DRW_uniformbuffer_create(sizeof(sldata->clip_data), &sldata->clip_data);
 +      }
 +
 +      /* EEVEE_effects_init needs to go first for TAA */
 +      EEVEE_effects_init(sldata, vedata, camera);
 +      EEVEE_materials_init(sldata, stl, fbl);
 +      EEVEE_lights_init(sldata);
 +      EEVEE_lightprobes_init(sldata, vedata);
 +
 +      if ((stl->effects->taa_current_sample > 1) && !DRW_state_is_image_render()) {
 +              /* XXX otherwise it would break the other engines. */
 +              DRW_viewport_matrix_override_unset_all();
 +      }
 +}
 +
 +static void eevee_cache_init(void *vedata)
 +{
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +
 +      EEVEE_bloom_cache_init(sldata, vedata);
 +      EEVEE_depth_of_field_cache_init(sldata, vedata);
 +      EEVEE_effects_cache_init(sldata, vedata);
 +      EEVEE_lightprobes_cache_init(sldata, vedata);
 +      EEVEE_lights_cache_init(sldata, vedata);
 +      EEVEE_materials_cache_init(sldata, vedata);
 +      EEVEE_motion_blur_cache_init(sldata, vedata);
 +      EEVEE_occlusion_cache_init(sldata, vedata);
 +      EEVEE_screen_raytrace_cache_init(sldata, vedata);
 +      EEVEE_subsurface_cache_init(sldata, vedata);
 +      EEVEE_temporal_sampling_cache_init(sldata, vedata);
 +      EEVEE_volumes_cache_init(sldata, vedata);
 +}
 +
 +void EEVEE_cache_populate(void *vedata, Object *ob)
 +{
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      bool cast_shadow = false;
 +
 +      if (ob->base_flag & BASE_VISIBLE) {
 +              EEVEE_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
 +      }
 +
 +      if (DRW_check_object_visible_within_active_context(ob)) {
 +              if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
 +                      EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
 +              }
 +              else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) {
 +                      /* do not add any scene light sources to the cache */
 +              }
 +              else if (ob->type == OB_LIGHTPROBE) {
 +                      if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
 +                              /* TODO: Special case for dupli objects because we cannot save the object pointer. */
 +                      }
 +                      else {
 +                              EEVEE_lightprobes_cache_add(sldata, vedata, ob);
 +                      }
 +              }
 +              else if (ob->type == OB_LAMP) {
 +                      EEVEE_lights_cache_add(sldata, ob);
 +              }
 +      }
 +
 +      if (cast_shadow) {
 +              EEVEE_lights_cache_shcaster_object_add(sldata, ob);
 +      }
 +}
 +
 +static void eevee_cache_finish(void *vedata)
 +{
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +
 +      EEVEE_materials_cache_finish(vedata);
 +      EEVEE_lights_cache_finish(sldata);
 +      EEVEE_lightprobes_cache_finish(sldata, vedata);
 +}
 +
 +/* As renders in an HDR offscreen buffer, we need draw everything once
 + * during the background pass. This way the other drawing callback between
 + * the background and the scene pass are visible.
 + * Note: we could break it up in two passes using some depth test
 + * to reduce the fillrate */
 +static void eevee_draw_background(void *vedata)
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +      EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl;
 +      EEVEE_EffectsInfo *effects = stl->effects;
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +
 +      /* Default framebuffer and texture */
 +      DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 +      DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
 +
 +      /* Sort transparents before the loop. */
 +      DRW_pass_sort_shgroup_z(psl->transparent_pass);
 +
 +      /* Number of iteration: needed for all temporal effect (SSR, volumetrics)
 +       * when using opengl render. */
 +      int loop_len = (DRW_state_is_image_render() &&
 +                     (stl->effects->enabled_effects & (EFFECT_VOLUMETRIC | EFFECT_SSR)) != 0) ? 4 : 1;
 +
 +      while (loop_len--) {
 +              float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 +              float clear_depth = 1.0f;
 +              uint clear_stencil = 0x0;
 +              uint primes[3] = {2, 3, 7};
 +              double offset[3] = {0.0, 0.0, 0.0};
 +              double r[3];
 +
 +              bool taa_use_reprojection = (stl->effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0;
 +
 +              if (DRW_state_is_image_render() ||
 +                  taa_use_reprojection ||
 +                  ((stl->effects->enabled_effects & EFFECT_TAA) != 0))
 +              {
 +                      int samp = taa_use_reprojection
 +                                  ? stl->effects->taa_reproject_sample + 1
 +                                  : stl->effects->taa_current_sample;
 +                      BLI_halton_3D(primes, offset, samp, r);
 +                      EEVEE_update_noise(psl, fbl, r);
 +                      EEVEE_volumes_set_jitter(sldata, samp - 1);
 +                      EEVEE_materials_init(sldata, stl, fbl);
 +              }
 +              /* Copy previous persmat to UBO data */
 +              copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
 +
 +              if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
 +                  (stl->effects->taa_current_sample > 1) &&
 +                  !DRW_state_is_image_render() &&
 +                  !taa_use_reprojection)
 +              {
 +                      DRW_viewport_matrix_override_set(stl->effects->overide_persmat, DRW_MAT_PERS);
 +                      DRW_viewport_matrix_override_set(stl->effects->overide_persinv, DRW_MAT_PERSINV);
 +                      DRW_viewport_matrix_override_set(stl->effects->overide_winmat, DRW_MAT_WIN);
 +                      DRW_viewport_matrix_override_set(stl->effects->overide_wininv, DRW_MAT_WININV);
 +              }
 +
 +              /* Refresh Probes */
 +              DRW_stats_group_start("Probes Refresh");
 +              EEVEE_lightprobes_refresh(sldata, vedata);
 +              /* Probes refresh can have reset the current sample. */
 +              if (stl->effects->taa_current_sample == 1) {
 +                      DRW_viewport_matrix_override_unset_all();
 +              }
 +              EEVEE_lightprobes_refresh_planar(sldata, vedata);
 +              DRW_stats_group_end();
 +
 +              /* Update common buffer after probe rendering. */
 +              DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
 +
 +              /* Refresh shadows */
 +              DRW_stats_group_start("Shadows");
 +              EEVEE_draw_shadows(sldata, psl);
 +              DRW_stats_group_end();
 +
 +              GPU_framebuffer_bind(fbl->main_fb);
 +              GPUFrameBufferBits clear_bits = GPU_DEPTH_BIT;
 +              clear_bits |= (DRW_state_draw_background()) ? 0 : GPU_COLOR_BIT;
 +              clear_bits |= ((stl->effects->enabled_effects & EFFECT_SSS) != 0) ? GPU_STENCIL_BIT : 0;
 +              GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil);
 +
 +              /* Depth prepass */
 +              DRW_stats_group_start("Prepass");
 +              DRW_draw_pass(psl->depth_pass);
 +              DRW_draw_pass(psl->depth_pass_cull);
 +              DRW_stats_group_end();
 +
 +              /* Create minmax texture */
 +              DRW_stats_group_start("Main MinMax buffer");
 +              EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
 +              DRW_stats_group_end();
 +
 +              EEVEE_occlusion_compute(sldata, vedata, dtxl->depth, -1);
 +              EEVEE_volumes_compute(sldata, vedata);
 +
 +              /* Shading pass */
 +              DRW_stats_group_start("Shading");
 +              if (DRW_state_draw_background()) {
 +                      DRW_draw_pass(psl->background_pass);
 +              }
 +              EEVEE_draw_default_passes(psl);
 +              DRW_draw_pass(psl->material_pass);
 +              EEVEE_subsurface_data_render(sldata, vedata);
 +              DRW_stats_group_end();
 +
 +              /* Effects pre-transparency */
 +              EEVEE_subsurface_compute(sldata, vedata);
 +              EEVEE_reflection_compute(sldata, vedata);
 +              EEVEE_occlusion_draw_debug(sldata, vedata);
 +              if (psl->probe_display) {
 +                      DRW_draw_pass(psl->probe_display);
 +              }
 +              EEVEE_refraction_compute(sldata, vedata);
 +
 +              /* Opaque refraction */
 +              DRW_stats_group_start("Opaque Refraction");
 +              DRW_draw_pass(psl->refract_depth_pass);
 +              DRW_draw_pass(psl->refract_depth_pass_cull);
 +              DRW_draw_pass(psl->refract_pass);
 +              DRW_stats_group_end();
 +
 +              /* Volumetrics Resolve Opaque */
 +              EEVEE_volumes_resolve(sldata, vedata);
 +
 +              /* Transparent */
 +              DRW_draw_pass(psl->transparent_pass);
 +
 +              /* Post Process */
 +              DRW_stats_group_start("Post FX");
 +              EEVEE_draw_effects(sldata, vedata);
 +              DRW_stats_group_end();
 +
 +              if ((stl->effects->taa_current_sample > 1) && !DRW_state_is_image_render()) {
 +                      DRW_viewport_matrix_override_unset_all();
 +              }
 +      }
 +
 +      /* LookDev */
 +      EEVEE_lookdev_draw_background(vedata);
 +      /* END */
 +
 +
 +      /* Tonemapping and transfer result to default framebuffer. */
 +      bool use_view_settings = stl->g_data->use_color_view_settings;
 +
 +      GPU_framebuffer_bind(dfbl->default_fb);
 +      DRW_transform_to_display(stl->effects->final_tx, use_view_settings);
 +
++      /* Debug : Output buffer to view. */
 +      switch (G.debug_value) {
 +              case 1:
 +                      if (txl->maxzbuffer) DRW_transform_to_display(txl->maxzbuffer, use_view_settings);
 +                      break;
 +              case 2:
 +                      if (effects->ssr_pdf_output) DRW_transform_to_display(effects->ssr_pdf_output, use_view_settings);
 +                      break;
 +              case 3:
 +                      if (effects->ssr_normal_input) DRW_transform_to_display(effects->ssr_normal_input, use_view_settings);
 +                      break;
 +              case 4:
 +                      if (effects->ssr_specrough_input) DRW_transform_to_display(effects->ssr_specrough_input, use_view_settings);
 +                      break;
 +              case 5:
 +                      if (txl->color_double_buffer) DRW_transform_to_display(txl->color_double_buffer, use_view_settings);
 +                      break;
 +              case 6:
 +                      if (effects->gtao_horizons_debug) DRW_transform_to_display(effects->gtao_horizons_debug, use_view_settings);
 +                      break;
 +              case 7:
 +                      if (effects->gtao_horizons) DRW_transform_to_display(effects->gtao_horizons, use_view_settings);
 +                      break;
 +              case 8:
 +                      if (effects->sss_data) DRW_transform_to_display(effects->sss_data, use_view_settings);
 +                      break;
 +              case 9:
 +                      if (effects->velocity_tx) DRW_transform_to_display(effects->velocity_tx, use_view_settings);
 +                      break;
 +              default:
 +                      break;
 +      }
 +
 +      EEVEE_volumes_free_smoke_textures();
 +
 +      stl->g_data->view_updated = false;
 +}
 +
 +static void eevee_view_update(void *vedata)
 +{
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +      if (stl->g_data) {
 +              stl->g_data->view_updated = true;
 +      }
 +}
 +
 +static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
 +{
 +      EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
 +      if (ped != NULL && ped->dd.recalc != 0) {
 +              ped->need_update = (ped->dd.recalc & (ID_RECALC_TRANSFORM | ID_RECALC_COPY_ON_WRITE)) != 0;
 +              ped->dd.recalc = 0;
 +      }
 +      EEVEE_LampEngineData *led = EEVEE_lamp_data_get(object);
 +      if (led != NULL && led->dd.recalc != 0) {
 +              led->need_update = true;
 +              led->dd.recalc = 0;
 +      }
 +      EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
 +      if (oedata != NULL && oedata->dd.recalc != 0) {
 +              oedata->need_update = true;
 +              oedata->dd.recalc = 0;
 +      }
 +}
 +
 +static void eevee_id_world_update(void *vedata, World *wo)
 +{
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +      LightCache *lcache = stl->g_data->light_cache;
 +
 +      EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
 +
 +      if (wedata != NULL && wedata->dd.recalc != 0) {
 +              if ((lcache->flag & (LIGHTCACHE_BAKED | LIGHTCACHE_BAKING)) == 0) {
 +                      lcache->flag |= LIGHTCACHE_UPDATE_WORLD;
 +              }
 +              wedata->dd.recalc = 0;
 +      }
 +}
 +
 +static void eevee_id_update(void *vedata, ID *id)
 +{
 +      /* Handle updates based on ID type. */
 +      switch (GS(id->name)) {
 +              case ID_WO:
 +                      eevee_id_world_update(vedata, (World *)id);
 +                      break;
 +              case ID_OB:
 +                      eevee_id_object_update(vedata, (Object *)id);
 +                      break;
 +              default:
 +                      /* pass */
 +                      break;
 +      }
 +}
 +
 +static void eevee_render_to_image(void *vedata, RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect)
 +{
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      EEVEE_render_init(vedata, engine, draw_ctx->depsgraph);
 +
 +      DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, EEVEE_render_cache);
 +
 +      /* Actually do the rendering. */
 +      EEVEE_render_draw(vedata, engine, render_layer, rect);
 +}
 +
 +static void eevee_engine_free(void)
 +{
 +      EEVEE_bloom_free();
 +      EEVEE_depth_of_field_free();
 +      EEVEE_effects_free();
 +      EEVEE_lightprobes_free();
 +      EEVEE_lights_free();
 +      EEVEE_materials_free();
 +      EEVEE_mist_free();
 +      EEVEE_motion_blur_free();
 +      EEVEE_occlusion_free();
 +      EEVEE_screen_raytrace_free();
 +      EEVEE_subsurface_free();
 +      EEVEE_temporal_sampling_free();
 +      EEVEE_volumes_free();
 +}
 +
 +static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
 +
 +DrawEngineType draw_engine_eevee_type = {
 +      NULL, NULL,
 +      N_("Eevee"),
 +      &eevee_data_size,
 +      &eevee_engine_init,
 +      &eevee_engine_free,
 +      &eevee_cache_init,
 +      &EEVEE_cache_populate,
 +      &eevee_cache_finish,
 +      &eevee_draw_background,
 +      NULL, /* Everything is drawn in the background pass (see comment on function) */
 +      &eevee_view_update,
 +      &eevee_id_update,
 +      &eevee_render_to_image,
 +};
 +
 +RenderEngineType DRW_engine_viewport_eevee_type = {
 +      NULL, NULL,
 +      EEVEE_ENGINE, N_("Eevee"), RE_INTERNAL | RE_USE_SHADING_NODES | RE_USE_PREVIEW,
 +      NULL, &DRW_render_to_image, NULL, NULL, NULL, NULL,
 +      &EEVEE_render_update_passes,
 +      &draw_engine_eevee_type,
 +      {NULL, NULL, NULL}
 +};
 +
 +
 +#undef EEVEE_ENGINE
index 5ac2257,0000000..d399e48
mode 100644,000000..100644
--- /dev/null
@@@ -1,1164 -1,0 +1,1164 @@@
-       /* TODO validate irradiance and reflection cache independantly... */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_lightcache.c
 + *  \ingroup draw_engine
 + *
 + * Eevee's indirect lighting cache.
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BKE_global.h"
 +#include "BKE_blender.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 "eevee_private.h"
 +
 +#include "GPU_context.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "wm_window.h"
 +
 +/* Rounded to nearest PowerOfTwo */
 +#if defined(IRRADIANCE_SH_L2)
 +#define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */
 +#define IRRADIANCE_SAMPLE_SIZE_Y 4 /* 3 in reality */
 +#elif defined(IRRADIANCE_CUBEMAP)
 +#define IRRADIANCE_SAMPLE_SIZE_X 8
 +#define IRRADIANCE_SAMPLE_SIZE_Y 8
 +#elif defined(IRRADIANCE_HL2)
 +#define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */
 +#define IRRADIANCE_SAMPLE_SIZE_Y 2
 +#endif
 +
 +#ifdef IRRADIANCE_SH_L2
 +/* we need a signed format for Spherical Harmonics */
 +#  define IRRADIANCE_FORMAT GPU_RGBA16F
 +#else
 +#  define IRRADIANCE_FORMAT GPU_RGBA8
 +#endif
 +
 +#define IRRADIANCE_MAX_POOL_LAYER 256 /* OpenGL 3.3 core requirement, can be extended but it's already very big */
 +#define IRRADIANCE_MAX_POOL_SIZE 1024
 +#define MAX_IRRADIANCE_SAMPLES \
 +        (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_X) * \
 +        (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_Y)
 +
 +/* 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_gawain_render_context_enable(void *re_gpu_context);
 +extern void DRW_gawain_render_context_disable(void *re_gpu_context);
 +
 +typedef struct EEVEE_LightBake {
 +      Depsgraph *depsgraph;
 +      ViewLayer *view_layer;
 +      ViewLayer *view_layer_input;
 +      LightCache *lcache;
 +      Scene *scene;
 +      struct Main *bmain;
 +      EEVEE_ViewLayerData *sldata;
 +
 +      LightProbe **probe;              /* Current probe being rendered. */
 +      GPUTexture *rt_color;            /* Target cube color texture. */
 +      GPUTexture *rt_depth;            /* Target cube depth texture. */
 +      GPUFrameBuffer *rt_fb[6];        /* Target cube framebuffers. */
 +      GPUFrameBuffer *store_fb;        /* Storage framebuffer. */
 +      int rt_res;                      /* Cube render target resolution. */
 +
 +      /* Shared */
 +      int layer;                       /* Target layer to store the data to. */
 +      float samples_ct, invsamples_ct; /* Sample count for the convolution. */
 +      float lod_factor;                /* Sampling bias during convolution step. */
 +      float lod_max;                   /* Max cubemap LOD to sample when convolving. */
 +      int cube_len, grid_len;          /* Number of probes to render + world probe. */
 +
 +      /* Irradiance grid */
 +      EEVEE_LightGrid *grid;           /* Current probe being rendered (UBO data). */
 +      int irr_cube_res;                /* Target cubemap at MIP 0. */
 +      int irr_size[3];                 /* Size of the irradiance texture. */
 +      int total_irr_samples;           /* Total for all grids */
 +      int grid_sample;                 /* Nth sample of the current grid being rendered. */
 +      int grid_sample_len;             /* Total number of samples for the current grid. */
 +      int grid_curr;                   /* Nth grid in the cache being rendered. */
 +      int bounce_curr, bounce_len;     /* The current light bounce being evaluated. */
 +      float vis_range, vis_blur;       /* Sample Visibility compression and bluring. */
 +      float vis_res;                   /* Resolution of the Visibility shadowmap. */
 +      GPUTexture *grid_prev;           /* Result of previous light bounce. */
 +      LightProbe **grid_prb;           /* Pointer to the id.data of the probe object. */
 +
 +      /* Reflection probe */
 +      EEVEE_LightProbe *cube;          /* Current probe being rendered (UBO data). */
 +      int ref_cube_res;                /* Target cubemap at MIP 0. */
 +      int cube_offset;                 /* Index of the current cube. */
 +      float probemat[6][4][4];         /* ViewProjection matrix for each cube face. */
 +      float texel_size, padding_size;  /* Texel and padding size for the final octahedral map. */
 +      float roughness;                 /* Roughness level of the current mipmap. */
 +      LightProbe **cube_prb;           /* Pointer to the id.data of the probe object. */
 +
 +      /* Dummy Textures */
 +      struct GPUTexture *dummy_color, *dummy_depth;
 +      struct GPUTexture *dummy_layer_color;
 +
 +      int total, done; /* to compute progress */
 +      short *stop, *do_update;
 +      float *progress;
 +
 +      bool resource_only;              /* For only handling the resources. */
 +      bool own_resources;
 +      bool own_light_cache;            /* If the lightcache was created for baking, it's first owned by the baker. */
 +      int delay;                       /* ms. delay the start of the baking to not slowdown interactions (TODO remove) */
 +
 +      void *gl_context, *gpu_context;  /* If running in parallel (in a separate thread), use this context. */
 +} EEVEE_LightBake;
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Light Cache
 + * \{ */
 +
 +/* Return memory footprint in bytes. */
 +static unsigned int eevee_lightcache_memsize_get(LightCache *lcache)
 +{
 +      unsigned int size = 0;
 +      if (lcache->grid_tx.data) {
 +              size += MEM_allocN_len(lcache->grid_tx.data);
 +      }
 +      if (lcache->cube_tx.data) {
 +              size += MEM_allocN_len(lcache->cube_tx.data);
 +              for (int mip = 0; mip < lcache->mips_len; ++mip) {
 +                      size += MEM_allocN_len(lcache->cube_mips[mip].data);
 +              }
 +      }
 +      return size;
 +}
 +
 +static int eevee_lightcache_irradiance_sample_count(LightCache *lcache)
 +{
 +      int total_irr_samples = 0;
 +
 +      for (int i = 1; i < lcache->grid_len; ++i) {
 +              EEVEE_LightGrid *egrid = lcache->grid_data + i;
 +              total_irr_samples += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2];
 +      }
 +      return total_irr_samples;
 +}
 +
 +void EEVEE_lightcache_info_update(SceneEEVEE *eevee)
 +{
 +      LightCache *lcache = eevee->light_cache;
 +
 +      if (lcache != NULL) {
 +              char formatted_mem[15];
 +              BLI_str_format_byte_unit(formatted_mem, eevee_lightcache_memsize_get(lcache), true);
 +
 +              int irr_samples = eevee_lightcache_irradiance_sample_count(lcache);
 +
 +              BLI_snprintf(eevee->light_cache_info, sizeof(eevee->light_cache_info), IFACE_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"), lcache->cube_len - 1, irr_samples, formatted_mem);
 +      }
 +      else {
 +              BLI_strncpy(eevee->light_cache_info, IFACE_("No light cache in this scene."), sizeof(eevee->light_cache_info));
 +      }
 +}
 +
 +static void irradiance_pool_size_get(int visibility_size, int total_samples, int r_size[3])
 +{
 +      /* Compute how many irradiance samples we can store per visibility sample. */
 +      int irr_per_vis = (visibility_size / IRRADIANCE_SAMPLE_SIZE_X) *
 +                        (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y);
 +
 +      /* The irradiance itself take one layer, hence the +1 */
 +      int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER);
 +
 +      int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1));
 +      r_size[0] = visibility_size * max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
 +      r_size[1] = visibility_size * max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
 +      r_size[2] = layer_ct;
 +}
 +
 +static bool EEVEE_lightcache_validate(
 +        const LightCache *light_cache,
 +        const int cube_len,
 +        const int cube_res,
 +        const int grid_len,
 +        const int irr_size[3])
 +{
 +      if (light_cache) {
 +              /* See if we need the same amount of texture space. */
 +              if ((irr_size[0] == light_cache->grid_tx.tex_size[0]) &&
 +                  (irr_size[1] == light_cache->grid_tx.tex_size[1]) &&
 +                  (irr_size[2] == light_cache->grid_tx.tex_size[2]) &&
 +                  (grid_len != light_cache->grid_len))
 +              {
 +                      int mip_len = (int)(floorf(log2f(cube_res)) - MIN_CUBE_LOD_LEVEL);
 +                      if ((cube_res == light_cache->cube_tx.tex_size[0]) &&
 +                          (cube_len == light_cache->cube_tx.tex_size[2]) &&
 +                          (mip_len  == light_cache->mips_len))
 +                      {
 +                              return true;
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +LightCache *EEVEE_lightcache_create(
 +        const int grid_len,
 +        const int cube_len,
 +        const int cube_size,
 +        const int vis_size,
 +        const int irr_size[3])
 +{
 +      LightCache *light_cache = MEM_callocN(sizeof(LightCache), "LightCache");
 +
 +      light_cache->cube_data = MEM_callocN(sizeof(EEVEE_LightProbe) * cube_len, "EEVEE_LightProbe");
 +      light_cache->grid_data = MEM_callocN(sizeof(EEVEE_LightGrid) * grid_len, "EEVEE_LightGrid");
 +
 +      light_cache->grid_tx.tex = DRW_texture_create_2D_array(irr_size[0], irr_size[1], irr_size[2], IRRADIANCE_FORMAT, DRW_TEX_FILTER, NULL);
 +      light_cache->grid_tx.tex_size[0] = irr_size[0];
 +      light_cache->grid_tx.tex_size[1] = irr_size[1];
 +      light_cache->grid_tx.tex_size[2] = irr_size[2];
 +
 +      light_cache->cube_tx.tex = DRW_texture_create_2D_array(cube_size, cube_size, cube_len, GPU_R11F_G11F_B10F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
 +      light_cache->cube_tx.tex_size[0] = cube_size;
 +      light_cache->cube_tx.tex_size[1] = cube_size;
 +      light_cache->cube_tx.tex_size[2] = cube_len;
 +
 +      light_cache->mips_len = (int)(floorf(log2f(cube_size)) - MIN_CUBE_LOD_LEVEL);
 +      light_cache->vis_res = vis_size;
 +      light_cache->ref_res = cube_size;
 +
 +      light_cache->cube_mips = MEM_callocN(sizeof(LightCacheTexture) * light_cache->mips_len, "LightCacheTexture");
 +
 +      for (int mip = 0; mip < light_cache->mips_len; ++mip) {
 +              GPU_texture_get_mipmap_size(light_cache->cube_tx.tex, mip + 1, light_cache->cube_mips[mip].tex_size);
 +      }
 +
 +      light_cache->flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID;
 +
 +      return light_cache;
 +}
 +
 +void EEVEE_lightcache_load(LightCache *lcache)
 +{
 +      if (lcache->grid_tx.tex == NULL && lcache->grid_tx.data) {
 +              lcache->grid_tx.tex = GPU_texture_create_nD(lcache->grid_tx.tex_size[0],
 +                                                          lcache->grid_tx.tex_size[1],
 +                                                          lcache->grid_tx.tex_size[2],
 +                                                          2,
 +                                                          lcache->grid_tx.data,
 +                                                          IRRADIANCE_FORMAT,
 +                                                          GPU_DATA_UNSIGNED_BYTE,
 +                                                          0,
 +                                                          false,
 +                                                          NULL);
 +              GPU_texture_bind(lcache->grid_tx.tex, 0);
 +              GPU_texture_filter_mode(lcache->grid_tx.tex, true);
 +              GPU_texture_unbind(lcache->grid_tx.tex);
 +      }
 +
 +      if (lcache->cube_tx.tex == NULL && lcache->cube_tx.data) {
 +              lcache->cube_tx.tex = GPU_texture_create_nD(lcache->cube_tx.tex_size[0],
 +                                                          lcache->cube_tx.tex_size[1],
 +                                                          lcache->cube_tx.tex_size[2],
 +                                                          2,
 +                                                          lcache->cube_tx.data,
 +                                                          GPU_R11F_G11F_B10F,
 +                                                          GPU_DATA_10_11_11_REV,
 +                                                          0,
 +                                                          false,
 +                                                          NULL);
 +              GPU_texture_bind(lcache->cube_tx.tex, 0);
 +              GPU_texture_mipmap_mode(lcache->cube_tx.tex, true, true);
 +              for (int mip = 0; mip < lcache->mips_len; ++mip) {
 +                      GPU_texture_add_mipmap(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1, lcache->cube_mips[mip].data);
 +              }
 +              GPU_texture_unbind(lcache->cube_tx.tex);
 +      }
 +}
 +
 +static void eevee_lightbake_readback_irradiance(LightCache *lcache)
 +{
 +      MEM_SAFE_FREE(lcache->grid_tx.data);
 +      lcache->grid_tx.data = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_UNSIGNED_BYTE, 0);
 +      lcache->grid_tx.data_type = LIGHTCACHETEX_BYTE;
 +      lcache->grid_tx.components = 4;
 +}
 +
 +static void eevee_lightbake_readback_reflections(LightCache *lcache)
 +{
 +      MEM_SAFE_FREE(lcache->cube_tx.data);
 +      lcache->cube_tx.data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, 0);
 +      lcache->cube_tx.data_type = LIGHTCACHETEX_UINT;
 +      lcache->cube_tx.components = 1;
 +
 +      for (int mip = 0; mip < lcache->mips_len; ++mip) {
 +              LightCacheTexture *cube_mip = lcache->cube_mips + mip;
 +              MEM_SAFE_FREE(cube_mip->data);
 +              GPU_texture_get_mipmap_size(lcache->cube_tx.tex, mip + 1, cube_mip->tex_size);
 +
 +              cube_mip->data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1);
 +              cube_mip->data_type = LIGHTCACHETEX_UINT;
 +              cube_mip->components = 1;
 +      }
 +}
 +
 +void EEVEE_lightcache_free(LightCache *lcache)
 +{
 +      DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex);
 +      MEM_SAFE_FREE(lcache->cube_tx.data);
 +      DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex);
 +      MEM_SAFE_FREE(lcache->grid_tx.data);
 +
 +      if (lcache->cube_mips) {
 +              for (int i = 0; i < lcache->mips_len; ++i) {
 +                      MEM_SAFE_FREE(lcache->cube_mips[i].data);
 +              }
 +              MEM_SAFE_FREE(lcache->cube_mips);
 +      }
 +
 +      MEM_SAFE_FREE(lcache->cube_data);
 +      MEM_SAFE_FREE(lcache->grid_data);
 +      MEM_freeN(lcache);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Light Bake Context
 + * \{ */
 +
 +static void eevee_lightbake_context_enable(EEVEE_LightBake *lbake)
 +{
 +      if (lbake->gl_context) {
 +              DRW_opengl_render_context_enable(lbake->gl_context);
 +              if (lbake->gpu_context == NULL) {
 +                      lbake->gpu_context = GPU_context_create();
 +              }
 +              DRW_gawain_render_context_enable(lbake->gpu_context);
 +      }
 +      else {
 +              DRW_opengl_context_enable();
 +      }
 +}
 +
 +static void eevee_lightbake_context_disable(EEVEE_LightBake *lbake)
 +{
 +      if (lbake->gl_context) {
 +              DRW_gawain_render_context_disable(lbake->gpu_context);
 +              DRW_opengl_render_context_disable(lbake->gl_context);
 +      }
 +      else {
 +              DRW_opengl_context_disable();
 +      }
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Light Bake Job
 + * \{ */
 +
 +static void eevee_lightbake_count_probes(EEVEE_LightBake *lbake)
 +{
 +      Depsgraph *depsgraph = lbake->depsgraph;
 +
 +      /* At least one of each for the world */
 +      lbake->grid_len = lbake->cube_len = lbake->total_irr_samples = 1;
 +
 +      DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob)
 +      {
 +              if (!BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_FOR_RENDER)) {
 +                      continue;
 +              }
 +
 +              if (ob->type == OB_LIGHTPROBE) {
 +                      LightProbe *prb = (LightProbe *)ob->data;
 +
 +                      if (prb->type == LIGHTPROBE_TYPE_GRID) {
 +                              lbake->total_irr_samples += prb->grid_resolution_x * prb->grid_resolution_y * prb->grid_resolution_z;
 +                              lbake->grid_len++;
 +                      }
 +                      else if (prb->type == LIGHTPROBE_TYPE_CUBE) {
 +                              lbake->cube_len++;
 +                      }
 +              }
 +      }
 +      DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
 +}
 +
 +static void eevee_lightbake_create_render_target(EEVEE_LightBake *lbake, int rt_res)
 +{
 +      lbake->rt_depth = DRW_texture_create_cube(rt_res, GPU_DEPTH_COMPONENT24, 0, NULL);
 +      lbake->rt_color = DRW_texture_create_cube(rt_res, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
 +
 +      for (int i = 0; i < 6; ++i) {
 +              GPU_framebuffer_ensure_config(&lbake->rt_fb[i], {
 +                      GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_depth, i),
 +                      GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_color, i)
 +              });
 +      }
 +
 +      GPU_framebuffer_ensure_config(&lbake->store_fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_NONE
 +      });
 +}
 +
 +static void eevee_lightbake_create_resources(EEVEE_LightBake *lbake)
 +{
 +      Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
 +      SceneEEVEE *eevee = &scene_eval->eevee;
 +
 +      lbake->bounce_len   = eevee->gi_diffuse_bounces;
 +      lbake->vis_res      = eevee->gi_visibility_resolution;
 +      lbake->rt_res       = eevee->gi_cubemap_resolution;
 +
 +      irradiance_pool_size_get(lbake->vis_res, lbake->total_irr_samples, lbake->irr_size);
 +
 +      lbake->ref_cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(lbake->rt_res);
 +
 +      lbake->cube_prb = MEM_callocN(sizeof(LightProbe *) * lbake->cube_len, "EEVEE Cube visgroup ptr");
 +      lbake->grid_prb = MEM_callocN(sizeof(LightProbe *) * lbake->grid_len, "EEVEE Grid visgroup ptr");
 +
 +      lbake->grid_prev = DRW_texture_create_2D_array(lbake->irr_size[0], lbake->irr_size[1], lbake->irr_size[2],
 +                                                     IRRADIANCE_FORMAT, DRW_TEX_FILTER, NULL);
 +
 +      /* Ensure Light Cache is ready to accept new data. If not recreate one.
 +       * WARNING: All the following must be threadsafe. It's currently protected
 +       * by the DRW mutex. */
 +      lbake->lcache = eevee->light_cache;
 +
-       /* TODO do this once for the whole bake when we have independant DRWManagers. */
++      /* TODO validate irradiance and reflection cache independently... */
 +      if (lbake->lcache != NULL &&
 +          !EEVEE_lightcache_validate(lbake->lcache, lbake->cube_len, lbake->ref_cube_res, lbake->grid_len, lbake->irr_size))
 +      {
 +              eevee->light_cache = lbake->lcache = NULL;
 +      }
 +
 +      if (lbake->lcache == NULL) {
 +              lbake->lcache = EEVEE_lightcache_create(lbake->grid_len,
 +                                                      lbake->cube_len,
 +                                                      lbake->ref_cube_res,
 +                                                      lbake->vis_res,
 +                                                      lbake->irr_size);
 +              lbake->lcache->flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID;
 +              lbake->lcache->vis_res = lbake->vis_res;
 +              lbake->own_light_cache = true;
 +
 +              eevee->light_cache = lbake->lcache;
 +      }
 +
 +      EEVEE_lightcache_load(eevee->light_cache);
 +
 +      lbake->lcache->flag |= LIGHTCACHE_BAKING;
 +      lbake->lcache->cube_len = 1;
 +}
 +
 +wmJob *EEVEE_lightbake_job_create(
 +        struct wmWindowManager *wm, struct wmWindow *win, struct Main *bmain,
 +        struct ViewLayer *view_layer, struct Scene *scene, int delay)
 +{
 +      EEVEE_LightBake *lbake = NULL;
 +
 +      /* only one render job at a time */
 +      if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER))
 +              return NULL;
 +
 +      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);
 +
 +      /* If job exists do not recreate context and depsgraph. */
 +      EEVEE_LightBake *old_lbake = (EEVEE_LightBake *)WM_jobs_customdata_get(wm_job);
 +
 +      if (old_lbake && (old_lbake->view_layer_input == view_layer) && (old_lbake->bmain == bmain)) {
 +              lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake");
 +              /* Cannot reuse depsgraph for now because we cannot get the update from the
 +               * main database directly. TODO reuse depsgraph and only update positions. */
 +              /* lbake->depsgraph = old_lbake->depsgraph; */
 +              lbake->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER);
 +
 +              lbake->scene = scene;
 +              lbake->bmain = bmain;
 +              lbake->view_layer_input = view_layer;
 +              lbake->gl_context = old_lbake->gl_context;
 +              lbake->own_resources = true;
 +              lbake->delay = delay;
 +
 +              old_lbake->own_resources = false;
 +
 +              if (old_lbake->stop != NULL) {
 +                      *old_lbake->stop = 1;
 +              }
 +      }
 +      else {
 +              lbake = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, true);
 +              lbake->delay = delay;
 +      }
 +
 +      WM_jobs_customdata_set(wm_job, lbake, EEVEE_lightbake_job_data_free);
 +      WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0);
 +      WM_jobs_callbacks(wm_job, EEVEE_lightbake_job, NULL, EEVEE_lightbake_update, EEVEE_lightbake_update);
 +
 +      G.is_break = false;
 +
 +      return wm_job;
 +}
 +
 +/* MUST run on the main thread. */
 +void *EEVEE_lightbake_job_data_alloc(
 +        struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, bool run_as_job)
 +{
 +      BLI_assert(BLI_thread_is_main());
 +
 +      EEVEE_LightBake *lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake");
 +
 +      lbake->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER);
 +      lbake->scene = scene;
 +      lbake->bmain = bmain;
 +      lbake->view_layer_input = view_layer;
 +      lbake->own_resources = true;
 +      lbake->own_light_cache = false;
 +
 +      if (run_as_job) {
 +              lbake->gl_context = WM_opengl_context_create();
 +              wm_window_reset_drawable();
 +      }
 +
 +      return lbake;
 +}
 +
 +void EEVEE_lightbake_job_data_free(void *custom_data)
 +{
 +      EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
 +
 +      /* TODO reuse depsgraph. */
 +      /* if (lbake->own_resources) { */
 +              DEG_graph_free(lbake->depsgraph);
 +      /* } */
 +
 +      MEM_SAFE_FREE(lbake->cube_prb);
 +      MEM_SAFE_FREE(lbake->grid_prb);
 +
 +      MEM_freeN(lbake);
 +}
 +
 +static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake)
 +{
 +      if (lbake->gl_context) {
 +              DRW_opengl_render_context_enable(lbake->gl_context);
 +              DRW_gawain_render_context_enable(lbake->gpu_context);
 +      }
 +      else if (!lbake->resource_only) {
 +              DRW_opengl_context_enable();
 +      }
 +
 +      if (lbake->own_light_cache) {
 +              EEVEE_lightcache_free(lbake->lcache);
 +              lbake->lcache = NULL;
 +      }
 +
 +      /* XXX Free the resources contained in the viewlayer data
 +       * to be able to free the context before deleting the depsgraph.  */
 +      if (lbake->sldata) {
 +              EEVEE_view_layer_data_free(lbake->sldata);
 +      }
 +
 +      DRW_TEXTURE_FREE_SAFE(lbake->rt_depth);
 +      DRW_TEXTURE_FREE_SAFE(lbake->rt_color);
 +      DRW_TEXTURE_FREE_SAFE(lbake->grid_prev);
 +      GPU_FRAMEBUFFER_FREE_SAFE(lbake->store_fb);
 +      for (int i = 0; i < 6; ++i) {
 +              GPU_FRAMEBUFFER_FREE_SAFE(lbake->rt_fb[i]);
 +      }
 +
 +      if (lbake->gpu_context) {
 +              DRW_gawain_render_context_disable(lbake->gpu_context);
 +              DRW_gawain_render_context_enable(lbake->gpu_context);
 +              GPU_context_discard(lbake->gpu_context);
 +      }
 +
 +      if (lbake->gl_context && lbake->own_resources) {
 +              /* Delete the baking context. */
 +              DRW_opengl_render_context_disable(lbake->gl_context);
 +              WM_opengl_context_dispose(lbake->gl_context);
 +              lbake->gpu_context = NULL;
 +              lbake->gl_context = NULL;
 +      }
 +      else if (lbake->gl_context) {
 +              DRW_opengl_render_context_disable(lbake->gl_context);
 +      }
 +      else if (!lbake->resource_only) {
 +              DRW_opengl_context_disable();
 +      }
 +}
 +
 +/* Cache as in draw cache not light cache. */
 +static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lbake)
 +{
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +      Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
 +      lbake->sldata = sldata;
 +
 +      /* Disable all effects BUT high bitdepth shadows. */
 +      scene_eval->eevee.flag &= SCE_EEVEE_SHADOW_HIGH_BITDEPTH;
 +      scene_eval->eevee.taa_samples = 1;
 +
 +      stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
 +      stl->g_data->background_alpha = 1.0f;
 +
 +      /* XXX TODO remove this. This is in order to make the init functions work. */
 +      DRWMatrixState dummy_mats = {{{{{0}}}}};
 +      DRW_viewport_matrix_override_set_all(&dummy_mats);
 +
 +      if (sldata->common_ubo == NULL) {
 +              sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), &sldata->common_data);
 +      }
 +      if (sldata->clip_ubo == NULL) {
 +              sldata->clip_ubo = DRW_uniformbuffer_create(sizeof(sldata->clip_data), &sldata->clip_data);
 +      }
 +
 +      /* HACK: set txl->color but unset it before Draw Manager frees it. */
 +      txl->color = lbake->rt_color;
 +      int viewport_size[2] = {
 +              GPU_texture_width(txl->color),
 +              GPU_texture_height(txl->color)
 +      };
 +      DRW_render_viewport_size_set(viewport_size);
 +
 +      EEVEE_effects_init(sldata, vedata, NULL);
 +      EEVEE_materials_init(sldata, stl, fbl);
 +      EEVEE_lights_init(sldata);
 +      EEVEE_lightprobes_init(sldata, vedata);
 +
 +      EEVEE_effects_cache_init(sldata, vedata);
 +      EEVEE_materials_cache_init(sldata, vedata);
 +      EEVEE_lights_cache_init(sldata, vedata);
 +      EEVEE_lightprobes_cache_init(sldata, vedata);
 +
 +      EEVEE_lightbake_cache_init(sldata, vedata, lbake->rt_color, lbake->rt_depth);
 +
 +      if (lbake->probe) {
 +              EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +              LightProbe *prb = *lbake->probe;
 +              pinfo->vis_data.collection = prb->visibility_grp;
 +              pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP;
 +              pinfo->vis_data.cached = false;
 +      }
 +      DRW_render_object_iter(vedata, NULL, lbake->depsgraph, EEVEE_render_cache);
 +
 +      EEVEE_materials_cache_finish(vedata);
 +      EEVEE_lights_cache_finish(sldata);
 +      EEVEE_lightprobes_cache_finish(sldata, vedata);
 +
 +      txl->color = NULL;
 +
 +      DRW_render_instance_buffer_finish();
 +      DRW_hair_update();
 +}
 +
 +static void eevee_lightbake_copy_irradiance(EEVEE_LightBake *lbake, LightCache *lcache)
 +{
 +      DRW_TEXTURE_FREE_SAFE(lbake->grid_prev);
 +
 +      /* Copy texture by reading back and reuploading it. */
 +      float *tex = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_FLOAT, 0);
 +      lbake->grid_prev = DRW_texture_create_2D_array(lbake->irr_size[0], lbake->irr_size[1], lbake->irr_size[2],
 +                                                     IRRADIANCE_FORMAT, DRW_TEX_FILTER, tex);
 +
 +      MEM_freeN(tex);
 +}
 +
 +static void eevee_lightbake_render_world_sample(void *ved, void *user_data)
 +{
 +      EEVEE_Data *vedata = (EEVEE_Data *)ved;
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +      EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data;
 +      Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
 +      LightCache *lcache = scene_eval->eevee.light_cache;
 +
-       /* TODO do this once for the whole bake when we have independant DRWManagers.
++      /* TODO do this once for the whole bake when we have independent DRWManagers. */
 +      eevee_lightbake_cache_create(vedata, lbake);
 +
 +      EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb);
 +      EEVEE_lightbake_filter_glossy(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f, lcache->mips_len);
 +      EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f);
 +
 +      /* Clear the cache to avoid white values in the grid. */
 +      GPU_framebuffer_texture_attach(lbake->store_fb, lbake->grid_prev, 0, 0);
 +      GPU_framebuffer_bind(lbake->store_fb);
 +      /* Clear to 1.0f for visibility. */
 +      GPU_framebuffer_clear_color(lbake->store_fb, ((float[4]){1.0f, 1.0f, 1.0f, 1.0f}));
 +      DRW_draw_pass(vedata->psl->probe_grid_fill);
 +
 +      SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex);
 +
 +      /* Make a copy for later. */
 +      eevee_lightbake_copy_irradiance(lbake, lcache);
 +
 +      lcache->cube_len = 1;
 +      lcache->grid_len = lbake->grid_len;
 +
 +      lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY;
 +      lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD;
 +}
 +
 +static void cell_id_to_grid_loc(EEVEE_LightGrid *egrid, int cell_idx, int r_local_cell[3])
 +{
 +      /* Keep in sync with lightprobe_grid_display_vert */
 +      r_local_cell[2] = cell_idx % egrid->resolution[2];
 +      r_local_cell[1] = (cell_idx / egrid->resolution[2]) % egrid->resolution[1];
 +      r_local_cell[0] = cell_idx / (egrid->resolution[2] * egrid->resolution[1]);
 +}
 +
 +static void compute_cell_id(
 +        EEVEE_LightGrid *egrid, LightProbe *probe,
 +        int cell_idx, int *r_final_idx, int r_local_cell[3], int *r_stride)
 +{
 +      const int cell_count = probe->grid_resolution_x * probe->grid_resolution_y * probe->grid_resolution_z;
 +
 +      /* Add one for level 0 */
 +      int max_lvl = (int)floorf(log2f((float)MAX3(probe->grid_resolution_x,
 +                                                  probe->grid_resolution_y,
 +                                                  probe->grid_resolution_z)));
 +
 +      int visited_cells = 0;
 +      *r_stride = 0;
 +      *r_final_idx = 0;
 +      r_local_cell[0] = r_local_cell[1] = r_local_cell[2] = 0;
 +      for (int lvl = max_lvl; lvl >= 0; --lvl) {
 +              *r_stride = 1 << lvl;
 +              int prev_stride = *r_stride << 1;
 +              for (int i = 0; i < cell_count; ++i) {
 +                      *r_final_idx = i;
 +                      cell_id_to_grid_loc(egrid, *r_final_idx, r_local_cell);
 +                      if (((r_local_cell[0] % *r_stride) == 0) &&
 +                          ((r_local_cell[1] % *r_stride) == 0) &&
 +                          ((r_local_cell[2] % *r_stride) == 0))
 +                      {
 +                              if (!(((r_local_cell[0] % prev_stride) == 0) &&
 +                                    ((r_local_cell[1] % prev_stride) == 0) &&
 +                                    ((r_local_cell[2] % prev_stride) == 0)) ||
 +                                    ((i == 0) && (lvl == max_lvl)))
 +                              {
 +                                      if (visited_cells == cell_idx) {
 +                                              return;
 +                                      }
 +                                      else {
 +                                              visited_cells++;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      BLI_assert(0);
 +}
 +
 +static void grid_loc_to_world_loc(EEVEE_LightGrid *egrid, int local_cell[3], float r_pos[3])
 +{
 +      copy_v3_v3(r_pos, egrid->corner);
 +      madd_v3_v3fl(r_pos, egrid->increment_x, local_cell[0]);
 +      madd_v3_v3fl(r_pos, egrid->increment_y, local_cell[1]);
 +      madd_v3_v3fl(r_pos, egrid->increment_z, local_cell[2]);
 +}
 +
 +static void eevee_lightbake_render_grid_sample(void *ved, void *user_data)
 +{
 +      EEVEE_Data *vedata = (EEVEE_Data *)ved;
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +      EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
 +      EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data;
 +      EEVEE_LightGrid *egrid = lbake->grid;
 +      LightProbe *prb = *lbake->probe;
 +      Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
 +      LightCache *lcache = scene_eval->eevee.light_cache;
 +      int grid_loc[3], sample_id, sample_offset, stride;
 +      float pos[3];
 +      const bool is_last_bounce_sample = ((egrid->offset + lbake->grid_sample) == (lbake->total_irr_samples - 1));
 +
 +      /* No bias for rendering the probe. */
 +      egrid->level_bias = 1.0f;
 +
 +      /* Use the previous bounce for rendering this bounce. */
 +      SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex);
 +
-       /* TODO do this once for the whole bake when we have independant DRWManagers. */
++      /* TODO do this once for the whole bake when we have independent DRWManagers.
 +       * Warning: Some of the things above require this. */
 +      eevee_lightbake_cache_create(vedata, lbake);
 +
 +      /* Compute sample position */
 +      compute_cell_id(egrid, prb, lbake->grid_sample, &sample_id, grid_loc, &stride);
 +      sample_offset = egrid->offset + sample_id;
 +
 +      grid_loc_to_world_loc(egrid, grid_loc, pos);
 +
 +      /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */
 +      common_data->spec_toggle = false;
 +      common_data->prb_num_planar = 0;
 +      common_data->prb_num_render_cube = 0;
 +      if (lbake->bounce_curr == 0) {
 +              common_data->prb_num_render_grid = 0;
 +      }
 +      DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
 +
 +      EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend);
 +
 +      /* Restore before filtering. */
 +      SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex);
 +
 +      EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, sample_offset, prb->intensity);
 +
 +      if (lbake->bounce_curr == 0) {
 +              /* We only need to filter the visibility for the first bounce. */
 +              EEVEE_lightbake_filter_visibility(sldata, vedata, lbake->rt_depth, lbake->store_fb, sample_offset,
 +                                                prb->clipsta, prb->clipend, egrid->visibility_range,
 +                                                prb->vis_blur, lbake->vis_res);
 +      }
 +
 +      /* Update level for progressive update. */
 +      if (is_last_bounce_sample) {
 +              egrid->level_bias = 1.0f;
 +      }
 +      else if (lbake->bounce_curr == 0) {
 +              egrid->level_bias = (float)(stride << 1);
 +      }
 +
 +      /* Only run this for the last sample of a bounce. */
 +      if (is_last_bounce_sample) {
 +              eevee_lightbake_copy_irradiance(lbake, lcache);
 +      }
 +
 +      /* If it is the last sample grid sample (and last bounce). */
 +      if ((lbake->bounce_curr == lbake->bounce_len - 1) &&
 +          (lbake->grid_curr == lbake->grid_len - 1) &&
 +          (lbake->grid_sample == lbake->grid_sample_len - 1))
 +      {
 +              lcache->flag &= ~LIGHTCACHE_UPDATE_GRID;
 +      }
 +}
 +
 +static void eevee_lightbake_render_probe_sample(void *ved, void *user_data)
 +{
 +      EEVEE_Data *vedata = (EEVEE_Data *)ved;
 +      EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 +      EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
 +      EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data;
 +      Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
 +      LightCache *lcache = scene_eval->eevee.light_cache;
 +      EEVEE_LightProbe *eprobe = lbake->cube;
 +      LightProbe *prb = *lbake->probe;
 +
++      /* TODO do this once for the whole bake when we have independent DRWManagers. */
 +      eevee_lightbake_cache_create(vedata, lbake);
 +
 +      /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */
 +      common_data->spec_toggle = false;
 +      common_data->prb_num_planar = 0;
 +      common_data->prb_num_render_cube = 0;
 +      DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
 +
 +      EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend);
 +      EEVEE_lightbake_filter_glossy(sldata, vedata, lbake->rt_color, lbake->store_fb, lbake->cube_offset, prb->intensity, lcache->mips_len);
 +
 +      lcache->cube_len += 1;
 +
 +      /* If it's the last probe. */
 +      if (lbake->cube_offset == lbake->cube_len - 1) {
 +              lcache->flag &= ~LIGHTCACHE_UPDATE_CUBE;
 +      }
 +}
 +
 +static float eevee_lightbake_grid_influence_volume(EEVEE_LightGrid *grid)
 +{
 +      return mat4_to_scale(grid->mat);
 +}
 +
 +static float eevee_lightbake_cube_influence_volume(EEVEE_LightProbe *eprb)
 +{
 +      return mat4_to_scale(eprb->attenuationmat);
 +}
 +
 +static bool eevee_lightbake_grid_comp(EEVEE_LightGrid *grid_a, EEVEE_LightGrid *grid_b)
 +{
 +      float vol_a = eevee_lightbake_grid_influence_volume(grid_a);
 +      float vol_b = eevee_lightbake_grid_influence_volume(grid_b);
 +      return (vol_a < vol_b);
 +}
 +
 +static bool eevee_lightbake_cube_comp(EEVEE_LightProbe *prb_a, EEVEE_LightProbe *prb_b)
 +{
 +      float vol_a = eevee_lightbake_cube_influence_volume(prb_a);
 +      float vol_b = eevee_lightbake_cube_influence_volume(prb_b);
 +      return (vol_a < vol_b);
 +}
 +
 +#define SORT_PROBE(elems_type, prbs, elems, elems_len, comp_fn) \
 +{ \
 +      bool sorted = false; \
 +      while (!sorted) { \
 +              sorted = true; \
 +              for (int i = 0; i < (elems_len) - 1; ++i) { \
 +                      if ((comp_fn)((elems) + i, (elems) + i+1)) { \
 +                              SWAP(elems_type, (elems)[i], (elems)[i+1]); \
 +                              SWAP(LightProbe *, (prbs)[i], (prbs)[i+1]); \
 +                              sorted = false; \
 +                      } \
 +              } \
 +      } \
 +}
 +
 +static void eevee_lightbake_gather_probes(EEVEE_LightBake *lbake)
 +{
 +      Depsgraph *depsgraph = lbake->depsgraph;
 +      Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
 +      LightCache *lcache = scene_eval->eevee.light_cache;
 +
 +      /* At least one for the world */
 +      int grid_len = 1;
 +      int cube_len = 1;
 +      int total_irr_samples = 1;
 +
 +      /* Convert all lightprobes to tight UBO data from all lightprobes in the scene.
 +       * This allows a large number of probe to be precomputed (even dupli ones). */
 +      DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob)
 +      {
 +              if (!BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_FOR_RENDER)) {
 +                      continue;
 +              }
 +
 +              if (ob->type == OB_LIGHTPROBE) {
 +                      LightProbe *prb = (LightProbe *)ob->data;
 +
 +                      if (prb->type == LIGHTPROBE_TYPE_GRID) {
 +                              lbake->grid_prb[grid_len] = prb;
 +                              EEVEE_LightGrid *egrid = &lcache->grid_data[grid_len++];
 +                              EEVEE_lightprobes_grid_data_from_object(ob, egrid, &total_irr_samples);
 +                      }
 +                      else if (prb->type == LIGHTPROBE_TYPE_CUBE) {
 +                              lbake->cube_prb[cube_len] = prb;
 +                              EEVEE_LightProbe *eprobe = &lcache->cube_data[cube_len++];
 +                              EEVEE_lightprobes_cube_data_from_object(ob, eprobe);
 +                      }
 +              }
 +      }
 +      DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
 +
 +      SORT_PROBE(EEVEE_LightGrid, lbake->grid_prb + 1, lcache->grid_data + 1, lbake->grid_len - 1, eevee_lightbake_grid_comp);
 +      SORT_PROBE(EEVEE_LightProbe, lbake->cube_prb + 1, lcache->cube_data + 1, lbake->cube_len - 1, eevee_lightbake_cube_comp);
 +
 +      lbake->total = lbake->total_irr_samples * lbake->bounce_len + lbake->cube_len;
 +      lbake->done = 0;
 +}
 +
 +void EEVEE_lightbake_update(void *custom_data)
 +{
 +      EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
 +      Scene *scene_orig = lbake->scene;
 +
 +      /* If a new lightcache was created, free the old one and reference the new. */
 +      if (lbake->lcache && scene_orig->eevee.light_cache != lbake->lcache) {
 +              if (scene_orig->eevee.light_cache != NULL) {
 +                      EEVEE_lightcache_free(scene_orig->eevee.light_cache);
 +              }
 +              scene_orig->eevee.light_cache = lbake->lcache;
 +              lbake->own_light_cache = false;
 +      }
 +
 +      EEVEE_lightcache_info_update(&lbake->scene->eevee);
 +
 +      DEG_id_tag_update(&scene_orig->id, DEG_TAG_COPY_ON_WRITE);
 +}
 +
 +static bool lightbake_do_sample(EEVEE_LightBake *lbake, void (*render_callback)(void *ved, void *user_data))
 +{
 +      if (G.is_break == true || *lbake->stop) {
 +              return false;
 +      }
 +
 +      Depsgraph *depsgraph = lbake->depsgraph;
 +
 +      /* TODO: make DRW manager instanciable (and only lock on drawing) */
 +      eevee_lightbake_context_enable(lbake);
 +      DRW_custom_pipeline(&draw_engine_eevee_type, depsgraph, render_callback, lbake);
 +      lbake->done += 1;
 +      *lbake->progress = lbake->done / (float)lbake->total;
 +      *lbake->do_update = 1;
 +      eevee_lightbake_context_disable(lbake);
 +
 +      return true;
 +}
 +
 +void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress)
 +{
 +      EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
 +      Depsgraph *depsgraph = lbake->depsgraph;
 +      int frame = 0; /* TODO make it user param. */
 +
 +      DEG_graph_relations_update(depsgraph, lbake->bmain, lbake->scene, lbake->view_layer_input);
 +      DEG_evaluate_on_framechange(lbake->bmain, depsgraph, frame);
 +
 +      lbake->view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +      lbake->stop = stop;
 +      lbake->do_update = do_update;
 +      lbake->progress = progress;
 +
 +      /* Count lightprobes */
 +      eevee_lightbake_count_probes(lbake);
 +
 +      /* We need to create the FBOs in the right context.
 +       * We cannot do it in the main thread. */
 +      eevee_lightbake_context_enable(lbake);
 +      eevee_lightbake_create_resources(lbake);
 +      eevee_lightbake_create_render_target(lbake, lbake->rt_res);
 +      eevee_lightbake_context_disable(lbake);
 +
 +      /* Gather all probes data */
 +      eevee_lightbake_gather_probes(lbake);
 +
 +      LightCache *lcache = lbake->lcache;
 +
 +      /* HACK: Sleep to delay the first rendering operation
 +       * that causes a small freeze (caused by VBO generation)
 +       * because this step is locking at this moment. */
 +      /* TODO remove this. */
 +      if (lbake->delay) {
 +              PIL_sleep_ms(lbake->delay);
 +      }
 +
 +      /* Render world irradiance and reflection first */
 +      if (lcache->flag & LIGHTCACHE_UPDATE_WORLD) {
 +              lbake->probe = NULL;
 +              lightbake_do_sample(lbake, eevee_lightbake_render_world_sample);
 +      }
 +
 +      /* Render irradiance grids */
 +      if (lcache->flag & LIGHTCACHE_UPDATE_GRID) {
 +              for (lbake->bounce_curr = 0; lbake->bounce_curr < lbake->bounce_len; ++lbake->bounce_curr) {
 +                      /* Bypass world, start at 1. */
 +                      lbake->probe = lbake->grid_prb + 1;
 +                      lbake->grid = lcache->grid_data + 1;
 +                      for (lbake->grid_curr = 1;
 +                           lbake->grid_curr < lbake->grid_len;
 +                           ++lbake->grid_curr, ++lbake->probe, ++lbake->grid)
 +                      {
 +                              LightProbe *prb = *lbake->probe;
 +                              lbake->grid_sample_len = prb->grid_resolution_x *
 +                                                       prb->grid_resolution_y *
 +                                                       prb->grid_resolution_z;
 +                              for (lbake->grid_sample = 0;
 +                                   lbake->grid_sample < lbake->grid_sample_len;
 +                                   ++lbake->grid_sample)
 +                              {
 +                                      lightbake_do_sample(lbake, eevee_lightbake_render_grid_sample);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /* Render reflections */
 +      if (lcache->flag & LIGHTCACHE_UPDATE_CUBE) {
 +              /* Bypass world, start at 1. */
 +              lbake->probe = lbake->cube_prb + 1;
 +              lbake->cube = lcache->cube_data + 1;
 +              for (lbake->cube_offset = 1;
 +                   lbake->cube_offset < lbake->cube_len;
 +                   ++lbake->cube_offset, ++lbake->probe, ++lbake->cube)
 +              {
 +                      lightbake_do_sample(lbake, eevee_lightbake_render_probe_sample);
 +              }
 +      }
 +
 +      /* Read the resulting lighting data to save it to file/disk. */
 +      eevee_lightbake_context_enable(lbake);
 +      eevee_lightbake_readback_irradiance(lcache);
 +      eevee_lightbake_readback_reflections(lcache);
 +      eevee_lightbake_context_disable(lbake);
 +
 +      lcache->flag |=  LIGHTCACHE_BAKED;
 +      lcache->flag &= ~LIGHTCACHE_BAKING;
 +
 +      /* Assume that if lbake->gl_context is NULL
 +       * we are not running in this in a job, so update
 +       * the scene lightcache pointer before deleting it. */
 +      if (lbake->gl_context == NULL) {
 +              BLI_assert(BLI_thread_is_main());
 +              EEVEE_lightbake_update(lbake);
 +      }
 +
 +      eevee_lightbake_delete_resources(lbake);
 +}
 +
 +/* This is to update the world irradiance and reflection contribution from
 + * within the viewport drawing (does not have the overhead of a full light cache rebuild.) */
 +void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, const Scene *scene)
 +{
 +      LightCache *lcache = vedata->stl->g_data->light_cache;
 +
 +      EEVEE_LightBake lbake = {
 +              .resource_only = true
 +      };
 +
 +      /* Create resources. */
 +      eevee_lightbake_create_render_target(&lbake, scene->eevee.gi_cubemap_resolution);
 +
 +      EEVEE_lightbake_cache_init(sldata, vedata, lbake.rt_color, lbake.rt_depth);
 +
 +      EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb);
 +      EEVEE_lightbake_filter_glossy(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f, lcache->mips_len);
 +      EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f);
 +
 +      /* Don't hide grids if they are already rendered. */
 +      lcache->grid_len = max_ii(1, lcache->grid_len);
 +      lcache->cube_len = 1;
 +
 +      lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY;
 +      lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD;
 +
 +      eevee_lightbake_delete_resources(&lbake);
 +}
 +
 +/** \} */
index 87f90f3,0000000..d318dea
mode 100644,000000..100644
--- /dev/null
@@@ -1,1382 -1,0 +1,1382 @@@
- /* Only init the passes usefull for rendering the light cache. */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_lightprobes.c
 + *  \ingroup draw_engine
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_string_utils.h"
 +#include "BLI_rand.h"
 +
 +#include "DNA_world_types.h"
 +#include "DNA_texture_types.h"
 +#include "DNA_image_types.h"
 +#include "DNA_lightprobe_types.h"
 +#include "DNA_view3d_types.h"
 +
 +#include "BKE_collection.h"
 +#include "BKE_object.h"
 +#include "MEM_guardedalloc.h"
 +
 +#include "GPU_material.h"
 +#include "GPU_texture.h"
 +#include "GPU_glew.h"
 +
 +#include "DEG_depsgraph_query.h"
 +
 +#include "eevee_engine.h"
 +#include "eevee_lightcache.h"
 +#include "eevee_private.h"
 +
 +#include "ED_screen.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#define HAMMERSLEY_SIZE 1024
 +
 +static struct {
 +      struct GPUShader *probe_default_sh;
 +      struct GPUShader *probe_default_studiolight_sh;
 +      struct GPUShader *probe_filter_glossy_sh;
 +      struct GPUShader *probe_filter_diffuse_sh;
 +      struct GPUShader *probe_filter_visibility_sh;
 +      struct GPUShader *probe_grid_fill_sh;
 +      struct GPUShader *probe_grid_display_sh;
 +      struct GPUShader *probe_planar_display_sh;
 +      struct GPUShader *probe_planar_downsample_sh;
 +      struct GPUShader *probe_cube_display_sh;
 +
 +      struct GPUTexture *hammersley;
 +      struct GPUTexture *planar_pool_placeholder;
 +      struct GPUTexture *depth_placeholder;
 +      struct GPUTexture *depth_array_placeholder;
 +      struct GPUTexture *cube_face_minmaxz;
 +
 +      struct GPUVertFormat *format_probe_display_cube;
 +      struct GPUVertFormat *format_probe_display_planar;
 +} e_data = {NULL}; /* Engine data */
 +
 +extern char datatoc_background_vert_glsl[];
 +extern char datatoc_default_world_frag_glsl[];
 +extern char datatoc_lightprobe_filter_glossy_frag_glsl[];
 +extern char datatoc_lightprobe_filter_diffuse_frag_glsl[];
 +extern char datatoc_lightprobe_filter_visibility_frag_glsl[];
 +extern char datatoc_lightprobe_geom_glsl[];
 +extern char datatoc_lightprobe_vert_glsl[];
 +extern char datatoc_lightprobe_planar_display_frag_glsl[];
 +extern char datatoc_lightprobe_planar_display_vert_glsl[];
 +extern char datatoc_lightprobe_planar_downsample_frag_glsl[];
 +extern char datatoc_lightprobe_planar_downsample_geom_glsl[];
 +extern char datatoc_lightprobe_planar_downsample_vert_glsl[];
 +extern char datatoc_lightprobe_cube_display_frag_glsl[];
 +extern char datatoc_lightprobe_cube_display_vert_glsl[];
 +extern char datatoc_lightprobe_grid_display_frag_glsl[];
 +extern char datatoc_lightprobe_grid_display_vert_glsl[];
 +extern char datatoc_lightprobe_grid_fill_frag_glsl[];
 +extern char datatoc_irradiance_lib_glsl[];
 +extern char datatoc_lightprobe_lib_glsl[];
 +extern char datatoc_octahedron_lib_glsl[];
 +extern char datatoc_bsdf_common_lib_glsl[];
 +extern char datatoc_common_uniforms_lib_glsl[];
 +extern char datatoc_common_view_lib_glsl[];
 +extern char datatoc_bsdf_sampling_lib_glsl[];
 +
 +extern GlobalsUboStorage ts;
 +
 +/* *********** FUNCTIONS *********** */
 +
 +/* TODO find a better way than this. This does not support dupli objects if
 + * the original object is hidden. */
 +bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data)
 +{
 +      EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data;
 +
 +      /* test disabled if group is NULL */
 +      if (oed->test_data->collection == NULL)
 +              return vis_in;
 +
 +      if (oed->test_data->cached == false)
 +              oed->ob_vis_dirty = true;
 +
 +      /* early out, don't need to compute ob_vis yet. */
 +      if (vis_in == false)
 +              return vis_in;
 +
 +      if (oed->ob_vis_dirty) {
 +              oed->ob_vis_dirty = false;
 +              oed->ob_vis = BKE_collection_has_object_recursive(oed->test_data->collection, oed->ob);
 +              oed->ob_vis = (oed->test_data->invert) ? !oed->ob_vis : oed->ob_vis;
 +      }
 +
 +      return vis_in && oed->ob_vis;
 +}
 +
 +static struct GPUTexture *create_hammersley_sample_texture(int samples)
 +{
 +      struct GPUTexture *tex;
 +      float (*texels)[2] = MEM_mallocN(sizeof(float[2]) * samples, "hammersley_tex");
 +      int i;
 +
 +      for (i = 0; i < samples; i++) {
 +              double dphi;
 +              BLI_hammersley_1D(i, &dphi);
 +              float phi = (float)dphi * 2.0f * M_PI;
 +              texels[i][0] = cosf(phi);
 +              texels[i][1] = sinf(phi);
 +      }
 +
 +      tex = DRW_texture_create_1D(samples, GPU_RG16F, DRW_TEX_WRAP, (float *)texels);
 +      MEM_freeN(texels);
 +      return tex;
 +}
 +
 +static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref)
 +{
 +      EEVEE_TextureList *txl = vedata->txl;
 +
 +      /* XXX TODO OPTIMISATION : This is a complete waist of texture memory.
 +       * Instead of allocating each planar probe for each viewport,
 +       * only alloc them once using the biggest viewport resolution. */
 +      const float *viewport_size = DRW_viewport_size_get();
 +
 +      /* TODO get screen percentage from layer setting */
 +      // const DRWContextState *draw_ctx = DRW_context_state_get();
 +      // ViewLayer *view_layer = draw_ctx->view_layer;
 +      float screen_percentage = 1.0f;
 +
 +      int width = (int)(viewport_size[0] * screen_percentage);
 +      int height = (int)(viewport_size[1] * screen_percentage);
 +
 +      /* We need an Array texture so allocate it ourself */
 +      if (!txl->planar_pool) {
 +              if (num_planar_ref > 0) {
 +                      txl->planar_pool = DRW_texture_create_2D_array(width, height, max_ff(1, num_planar_ref),
 +                                                                       GPU_R11F_G11F_B10F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
 +                      txl->planar_depth = DRW_texture_create_2D_array(width, height, max_ff(1, num_planar_ref),
 +                                                                      GPU_DEPTH_COMPONENT24, 0, NULL);
 +              }
 +              else if (num_planar_ref == 0) {
 +                      /* Makes Opengl Happy : Create a placeholder texture that will never be sampled but still bound to shader. */
 +                      txl->planar_pool = DRW_texture_create_2D_array(1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
 +                      txl->planar_depth = DRW_texture_create_2D_array(1, 1, 1, GPU_DEPTH_COMPONENT24, 0, NULL);
 +              }
 +      }
 +}
 +
 +static void lightprobe_shaders_init(void)
 +{
 +      const char *filter_defines = "#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n"
 +#if defined(IRRADIANCE_SH_L2)
 +                                   "#define IRRADIANCE_SH_L2\n"
 +#elif defined(IRRADIANCE_CUBEMAP)
 +                                   "#define IRRADIANCE_CUBEMAP\n"
 +#elif defined(IRRADIANCE_HL2)
 +                                   "#define IRRADIANCE_HL2\n"
 +#endif
 +                                   "#define NOISE_SIZE 64\n";
 +
 +      char *shader_str = NULL;
 +      char *vert_str = NULL;
 +
 +      shader_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_common_uniforms_lib_glsl,
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_bsdf_sampling_lib_glsl,
 +              datatoc_lightprobe_filter_glossy_frag_glsl);
 +
 +      e_data.probe_filter_glossy_sh = DRW_shader_create(
 +              datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str, filter_defines);
 +
 +      e_data.probe_default_sh = DRW_shader_create(
 +              datatoc_background_vert_glsl, NULL, datatoc_default_world_frag_glsl, NULL);
 +
 +      e_data.probe_default_studiolight_sh = DRW_shader_create(
 +              datatoc_background_vert_glsl, NULL, datatoc_default_world_frag_glsl, "#define LOOKDEV\n");
 +
 +      MEM_freeN(shader_str);
 +
 +      shader_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_common_uniforms_lib_glsl,
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_bsdf_sampling_lib_glsl,
 +              datatoc_lightprobe_filter_diffuse_frag_glsl);
 +
 +      e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen(shader_str, filter_defines);
 +
 +      MEM_freeN(shader_str);
 +
 +      shader_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_common_uniforms_lib_glsl,
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_bsdf_sampling_lib_glsl,
 +              datatoc_lightprobe_filter_visibility_frag_glsl);
 +
 +      e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen(shader_str, filter_defines);
 +
 +      MEM_freeN(shader_str);
 +
 +      shader_str = BLI_string_joinN(
 +              datatoc_octahedron_lib_glsl,
 +              datatoc_common_view_lib_glsl,
 +              datatoc_common_uniforms_lib_glsl,
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_irradiance_lib_glsl,
 +              datatoc_lightprobe_lib_glsl,
 +              datatoc_lightprobe_grid_display_frag_glsl);
 +
 +      vert_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_lightprobe_grid_display_vert_glsl);
 +
 +      e_data.probe_grid_display_sh = DRW_shader_create(vert_str, NULL, shader_str, filter_defines);
 +
 +      MEM_freeN(vert_str);
 +      MEM_freeN(shader_str);
 +
 +      e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen(
 +              datatoc_lightprobe_grid_fill_frag_glsl, filter_defines);
 +
 +      shader_str = BLI_string_joinN(
 +              datatoc_octahedron_lib_glsl,
 +              datatoc_common_view_lib_glsl,
 +              datatoc_common_uniforms_lib_glsl,
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_lightprobe_lib_glsl,
 +              datatoc_lightprobe_cube_display_frag_glsl);
 +
 +      vert_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_lightprobe_cube_display_vert_glsl);
 +
 +      e_data.probe_cube_display_sh = DRW_shader_create(vert_str, NULL, shader_str, SHADER_DEFINES);
 +
 +      MEM_freeN(vert_str);
 +      MEM_freeN(shader_str);
 +
 +      vert_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_lightprobe_planar_display_vert_glsl);
 +
 +      shader_str = BLI_string_joinN(
 +              datatoc_common_view_lib_glsl,
 +              datatoc_lightprobe_planar_display_frag_glsl);
 +
 +      e_data.probe_planar_display_sh = DRW_shader_create(vert_str, NULL, shader_str, NULL);
 +
 +      MEM_freeN(vert_str);
 +      MEM_freeN(shader_str);
 +
 +      e_data.probe_planar_downsample_sh = DRW_shader_create(
 +              datatoc_lightprobe_planar_downsample_vert_glsl,
 +              datatoc_lightprobe_planar_downsample_geom_glsl,
 +              datatoc_lightprobe_planar_downsample_frag_glsl,
 +              NULL);
 +
 +      e_data.hammersley = create_hammersley_sample_texture(HAMMERSLEY_SIZE);
 +}
 +
 +void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
 +      EEVEE_StorageList *stl = vedata->stl;
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
 +
 +      if (!e_data.probe_filter_glossy_sh) {
 +              lightprobe_shaders_init();
 +      }
 +
 +      /* Use fallback if we don't have gpu texture allocated an we cannot restore them. */
 +      bool use_fallback_lightcache = (scene_eval->eevee.light_cache == NULL) ||
 +                                     ((scene_eval->eevee.light_cache->grid_tx.tex == NULL) &&
 +                                      (scene_eval->eevee.light_cache->grid_tx.data == NULL)) ||
 +                                     ((scene_eval->eevee.light_cache->cube_tx.tex == NULL) &&
 +                                      (scene_eval->eevee.light_cache->cube_tx.data == NULL));
 +
 +      if (use_fallback_lightcache && (sldata->fallback_lightcache == NULL)) {
 +#if defined(IRRADIANCE_SH_L2)
 +              int grid_res = 4;
 +#elif defined(IRRADIANCE_CUBEMAP)
 +              int grid_res = 8;
 +#elif defined(IRRADIANCE_HL2)
 +              int grid_res = 4;
 +#endif
 +              int cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(scene_eval->eevee.gi_cubemap_resolution);
 +              int vis_res = scene_eval->eevee.gi_visibility_resolution;
 +              sldata->fallback_lightcache = EEVEE_lightcache_create(1, 1, cube_res, vis_res, (int[3]){grid_res, grid_res, 1});
 +      }
 +
 +      stl->g_data->light_cache = (use_fallback_lightcache) ? sldata->fallback_lightcache : scene_eval->eevee.light_cache;
 +
 +      EEVEE_lightcache_load(stl->g_data->light_cache);
 +
 +      if (!sldata->probes) {
 +              sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo");
 +              sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL);
 +              sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL);
 +              sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR, NULL);
 +      }
 +
 +      common_data->prb_num_planar = 0;
 +      common_data->prb_num_render_cube = 1;
 +      common_data->prb_num_render_grid = 1;
 +
 +      common_data->spec_toggle = true;
 +      common_data->ssr_toggle = true;
 +      common_data->sss_toggle = true;
 +
 +      /* Placeholder planar pool: used when rendering planar reflections (avoid dependency loop). */
 +      if (!e_data.planar_pool_placeholder) {
 +              e_data.planar_pool_placeholder = DRW_texture_create_2D_array(1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER, NULL);
 +      }
 +}
 +
-                                               /* TODO (fclem): remove thoses (need to clean the GLSL files). */
++/* Only init the passes useful for rendering the light cache. */
 +void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, GPUTexture *rt_color, GPUTexture *rt_depth)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      LightCache *light_cache = vedata->stl->g_data->light_cache;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +
 +      {
 +              psl->probe_glossy_compute = DRW_pass_create("LightProbe Glossy Compute", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_filter_glossy_sh, psl->probe_glossy_compute);
 +              DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1);
 +              DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
 +              DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1);
 +              DRW_shgroup_uniform_float(grp, "roughnessSquared", &pinfo->roughness, 1);
 +              DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1);
 +              DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1);
 +              DRW_shgroup_uniform_float(grp, "texelSize", &pinfo->texel_size, 1);
 +              DRW_shgroup_uniform_float(grp, "paddingSize", &pinfo->padding_size, 1);
 +              DRW_shgroup_uniform_int(grp, "Layer", &pinfo->layer, 1);
 +              DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
 +              // DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter);
 +              DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color);
 +              DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +
 +              struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +              DRW_shgroup_call_add(grp, geom, NULL);
 +      }
 +
 +      {
 +              psl->probe_diffuse_compute = DRW_pass_create("LightProbe Diffuse Compute", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_filter_diffuse_sh, psl->probe_diffuse_compute);
 +#ifdef IRRADIANCE_SH_L2
 +              DRW_shgroup_uniform_int(grp, "probeSize", &pinfo->shres, 1);
 +#else
 +              DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
 +              DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1);
 +              DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1);
 +              DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1);
 +              DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
 +#endif
 +              DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1);
 +              DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color);
 +              DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +
 +              struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +              DRW_shgroup_call_add(grp, geom, NULL);
 +      }
 +
 +      {
 +              psl->probe_visibility_compute = DRW_pass_create("LightProbe Visibility Compute", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_filter_visibility_sh, psl->probe_visibility_compute);
 +              DRW_shgroup_uniform_int(grp, "outputSize", &pinfo->shres, 1);
 +              DRW_shgroup_uniform_float(grp, "visibilityRange", &pinfo->visibility_range, 1);
 +              DRW_shgroup_uniform_float(grp, "visibilityBlur", &pinfo->visibility_blur, 1);
 +              DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
 +              DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1);
 +              DRW_shgroup_uniform_float(grp, "storedTexelSize", &pinfo->texel_size, 1);
 +              DRW_shgroup_uniform_float(grp, "nearClip", &pinfo->near_clip, 1);
 +              DRW_shgroup_uniform_float(grp, "farClip", &pinfo->far_clip, 1);
 +              DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
 +              DRW_shgroup_uniform_texture(grp, "probeDepth", rt_depth);
 +              DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +
 +              struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +              DRW_shgroup_call_add(grp, geom, NULL);
 +      }
 +
 +      {
 +              psl->probe_grid_fill = DRW_pass_create("LightProbe Grid Floodfill", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_grid_fill_sh, psl->probe_grid_fill);
 +              DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &light_cache->grid_tx.tex);
 +
 +              struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +              DRW_shgroup_call_add(grp, geom, NULL);
 +      }
 +}
 +
 +void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      LightCache *lcache = stl->g_data->light_cache;
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
 +
 +      pinfo->num_planar = 0;
 +      pinfo->vis_data.collection = NULL;
 +      pinfo->do_grid_update = false;
 +      pinfo->do_cube_update = false;
 +
 +      {
 +              psl->probe_background = DRW_pass_create("World Probe Background Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
 +
 +              struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +              DRWShadingGroup *grp = NULL;
 +
 +              Scene *scene = draw_ctx->scene;
 +              World *wo = scene->world;
 +
 +              float *col = ts.colorBackground;
 +
 +              /* LookDev */
 +              EEVEE_lookdev_cache_init(vedata, &grp, e_data.probe_default_studiolight_sh, psl->probe_background, wo, pinfo);
 +              /* END */
 +              if (!grp && wo) {
 +                      col = &wo->horr;
 +
 +                      if (wo->use_nodes && wo->nodetree) {
 +                              static float error_col[3] = {1.0f, 0.0f, 1.0f};
 +                              struct GPUMaterial *gpumat = EEVEE_material_world_lightprobe_get(scene, wo);
 +
 +                              GPUMaterialStatus status = GPU_material_status(gpumat);
 +
 +                              switch (status) {
 +                                      case GPU_MAT_SUCCESS:
 +                                              grp = DRW_shgroup_material_create(gpumat, psl->probe_background);
 +                                              DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
-                       /* TODO (fclem) get rid of thoses UBO. */
++                                              /* TODO (fclem): remove those (need to clean the GLSL files). */
 +                                              DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
 +                                              DRW_shgroup_call_add(grp, geom, NULL);
 +                                              break;
 +                                      default:
 +                                              col = error_col;
 +                                              break;
 +                              }
 +                      }
 +              }
 +
 +              /* Fallback if shader fails or if not using nodetree. */
 +              if (grp == NULL) {
 +                      grp = DRW_shgroup_create(e_data.probe_default_sh, psl->probe_background);
 +                      DRW_shgroup_uniform_vec3(grp, "color", col, 1);
 +                      DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
 +                      DRW_shgroup_call_add(grp, geom, NULL);
 +              }
 +      }
 +
 +      if (DRW_state_draw_support() && !LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) {
 +              DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK;
 +              psl->probe_display = DRW_pass_create("LightProbe Display", state);
 +
 +              /* Cube Display */
 +              if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS && lcache->cube_len > 1) {
 +                      int cube_len = lcache->cube_len - 1; /* don't count the world. */
 +                      DRWShadingGroup *grp = DRW_shgroup_empty_tri_batch_create(e_data.probe_cube_display_sh,
 +                                                                                psl->probe_display, cube_len * 2);
 +                      DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex);
 +                      DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
 +                      DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +                      DRW_shgroup_uniform_vec3(grp, "screen_vecs[0]", DRW_viewport_screenvecs_get(), 2);
 +                      DRW_shgroup_uniform_float_copy(grp, "sphere_size", scene_eval->eevee.gi_cubemap_draw_size * 0.5f);
-                               /* TODO (fclem) get rid of thoses UBO. */
++                      /* TODO (fclem) get rid of those UBO. */
 +                      DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
 +                      DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
 +              }
 +
 +              /* Grid Display */
 +              if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) {
 +                      EEVEE_LightGrid *egrid = lcache->grid_data + 1;
 +                      for (int p = 1; p < lcache->grid_len; ++p, egrid++) {
 +                              DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.probe_grid_display_sh, psl->probe_display);
 +                              DRW_shgroup_uniform_int(shgrp, "offset", &egrid->offset, 1);
 +                              DRW_shgroup_uniform_ivec3(shgrp, "grid_resolution", egrid->resolution, 1);
 +                              DRW_shgroup_uniform_vec3(shgrp, "corner", egrid->corner, 1);
 +                              DRW_shgroup_uniform_vec3(shgrp, "increment_x", egrid->increment_x, 1);
 +                              DRW_shgroup_uniform_vec3(shgrp, "increment_y", egrid->increment_y, 1);
 +                              DRW_shgroup_uniform_vec3(shgrp, "increment_z", egrid->increment_z, 1);
 +                              DRW_shgroup_uniform_vec3(shgrp, "screen_vecs[0]", DRW_viewport_screenvecs_get(), 2);
 +                              DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
 +                              DRW_shgroup_uniform_float_copy(shgrp, "sphere_size", scene_eval->eevee.gi_irradiance_draw_size * 0.5f);
-        * b) it's easier than fixing the nodetree shaders (for view dependant effects). */
++                              /* TODO (fclem) get rid of those UBO. */
 +                              DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo);
 +                              DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo);
 +                              DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo);
 +                              DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo);
 +                              int tri_count = egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2] * 2;
 +                              DRW_shgroup_call_procedural_triangles_add(shgrp, tri_count, NULL);
 +                      }
 +              }
 +
 +              /* Planar Display */
 +              DRW_shgroup_instance_format(e_data.format_probe_display_planar, {
 +                  {"probe_id", DRW_ATTRIB_INT, 1},
 +                  {"probe_mat", DRW_ATTRIB_FLOAT, 16},
 +              });
 +
 +              DRWShadingGroup *grp = DRW_shgroup_instance_create(
 +                      e_data.probe_planar_display_sh,
 +                      psl->probe_display,
 +                      DRW_cache_quad_get(),
 +                      e_data.format_probe_display_planar);
 +              stl->g_data->planar_display_shgrp = grp;
 +              DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool);
 +      }
 +      else {
 +              stl->g_data->planar_display_shgrp = NULL;
 +      }
 +
 +      {
 +              psl->probe_planar_downsample_ps = DRW_pass_create("LightProbe Planar Downsample", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_planar_downsample_sh, psl->probe_planar_downsample_ps);
 +              DRW_shgroup_uniform_texture_ref(grp, "source", &txl->planar_pool);
 +              DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
 +              DRW_shgroup_call_instances_add(grp, DRW_cache_fullscreen_quad_get(), NULL, (uint *)&pinfo->num_planar);
 +      }
 +}
 +
 +static bool eevee_lightprobes_culling_test(Object *ob)
 +{
 +      LightProbe *probe = (LightProbe *)ob->data;
 +
 +      switch (probe->type) {
 +              case LIGHTPROBE_TYPE_PLANAR:
 +              {
 +                      /* See if this planar probe is inside the view frustum. If not, no need to update it. */
 +                      /* NOTE: this could be bypassed if we want feedback loop mirrors for rendering. */
 +                      BoundBox bbox; float tmp[4][4];
 +                      const float min[3] = {-1.0f, -1.0f, -1.0f};
 +                      const float max[3] = { 1.0f,  1.0f,  1.0f};
 +                      BKE_boundbox_init_from_minmax(&bbox, min, max);
 +
 +                      copy_m4_m4(tmp, ob->obmat);
 +                      normalize_v3(tmp[2]);
 +                      mul_v3_fl(tmp[2], probe->distinf);
 +
 +                      for (int v = 0; v < 8; ++v) {
 +                              mul_m4_v3(tmp, bbox.vec[v]);
 +                      }
 +                      return DRW_culling_box_test(&bbox);
 +              }
 +              case LIGHTPROBE_TYPE_CUBE:
 +                      return true; /* TODO */
 +              case LIGHTPROBE_TYPE_GRID:
 +                      return true; /* TODO */
 +      }
 +      BLI_assert(0);
 +      return true;
 +}
 +
 +void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob)
 +{
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      LightProbe *probe = (LightProbe *)ob->data;
 +
 +      if ((probe->type == LIGHTPROBE_TYPE_CUBE && pinfo->num_cube >= MAX_PROBE) ||
 +          (probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= MAX_PROBE) ||
 +          (probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_planar >= MAX_PLANAR))
 +      {
 +              printf("Too many probes in the view !!!\n");
 +              return;
 +      }
 +
 +      if (probe->type == LIGHTPROBE_TYPE_PLANAR) {
 +              if (!eevee_lightprobes_culling_test(ob)) {
 +                      return; /* Culled */
 +              }
 +              EEVEE_lightprobes_planar_data_from_object(ob,
 +                                                        &pinfo->planar_data[pinfo->num_planar],
 +                                                        &pinfo->planar_vis_tests[pinfo->num_planar]);
 +              /* Debug Display */
 +              DRWShadingGroup *grp = vedata->stl->g_data->planar_display_shgrp;
 +              if (grp && (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) {
 +                      DRW_shgroup_call_dynamic_add(grp, &pinfo->num_planar, ob->obmat);
 +              }
 +
 +              pinfo->num_planar++;
 +      }
 +      else {
 +              EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob);
 +              if (ped->need_update) {
 +                      if (probe->type == LIGHTPROBE_TYPE_GRID) {
 +                              pinfo->do_grid_update = true;
 +                      }
 +                      else {
 +                              pinfo->do_cube_update = true;
 +                      }
 +                      ped->need_update = false;
 +              }
 +      }
 +}
 +
 +void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset)
 +{
 +      LightProbe *probe = (LightProbe *)ob->data;
 +
 +      copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x);
 +
 +      /* Save current offset and advance it for the next grid. */
 +      egrid->offset = *offset;
 +      *offset += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2];
 +
 +      /* Add one for level 0 */
 +      float fac = 1.0f / max_ff(1e-8f, probe->falloff);
 +      egrid->attenuation_scale = fac / max_ff(1e-8f, probe->distinf);
 +      egrid->attenuation_bias = fac;
 +
 +      /* Update transforms */
 +      float cell_dim[3], half_cell_dim[3];
 +      cell_dim[0] = 2.0f / egrid->resolution[0];
 +      cell_dim[1] = 2.0f / egrid->resolution[1];
 +      cell_dim[2] = 2.0f / egrid->resolution[2];
 +
 +      mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f);
 +
 +      /* Matrix converting world space to cell ranges. */
 +      invert_m4_m4(egrid->mat, ob->obmat);
 +
 +      /* First cell. */
 +      copy_v3_fl(egrid->corner, -1.0f);
 +      add_v3_v3(egrid->corner, half_cell_dim);
 +      mul_m4_v3(ob->obmat, egrid->corner);
 +
 +      /* Opposite neighbor cell. */
 +      copy_v3_fl3(egrid->increment_x, cell_dim[0], 0.0f, 0.0f);
 +      add_v3_v3(egrid->increment_x, half_cell_dim);
 +      add_v3_fl(egrid->increment_x, -1.0f);
 +      mul_m4_v3(ob->obmat, egrid->increment_x);
 +      sub_v3_v3(egrid->increment_x, egrid->corner);
 +
 +      copy_v3_fl3(egrid->increment_y, 0.0f, cell_dim[1], 0.0f);
 +      add_v3_v3(egrid->increment_y, half_cell_dim);
 +      add_v3_fl(egrid->increment_y, -1.0f);
 +      mul_m4_v3(ob->obmat, egrid->increment_y);
 +      sub_v3_v3(egrid->increment_y, egrid->corner);
 +
 +      copy_v3_fl3(egrid->increment_z, 0.0f, 0.0f, cell_dim[2]);
 +      add_v3_v3(egrid->increment_z, half_cell_dim);
 +      add_v3_fl(egrid->increment_z, -1.0f);
 +      mul_m4_v3(ob->obmat, egrid->increment_z);
 +      sub_v3_v3(egrid->increment_z, egrid->corner);
 +
 +      /* Visibility bias */
 +      egrid->visibility_bias = 0.05f * probe->vis_bias;
 +      egrid->visibility_bleed = probe->vis_bleedbias;
 +      egrid->visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(egrid->increment_x),
 +                                                     len_squared_v3(egrid->increment_y),
 +                                                     len_squared_v3(egrid->increment_z)));
 +}
 +
 +void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe)
 +{
 +      LightProbe *probe = (LightProbe *)ob->data;
 +
 +      /* Update transforms */
 +      copy_v3_v3(eprobe->position, ob->obmat[3]);
 +
 +      /* Attenuation */
 +      eprobe->attenuation_type = probe->attenuation_type;
 +      eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff);
 +
 +      unit_m4(eprobe->attenuationmat);
 +      scale_m4_fl(eprobe->attenuationmat, probe->distinf);
 +      mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat);
 +      invert_m4(eprobe->attenuationmat);
 +
 +      /* Parallax */
 +      unit_m4(eprobe->parallaxmat);
 +
 +      if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) {
 +              eprobe->parallax_type = probe->parallax_type;
 +              scale_m4_fl(eprobe->parallaxmat, probe->distpar);
 +      }
 +      else {
 +              eprobe->parallax_type = probe->attenuation_type;
 +              scale_m4_fl(eprobe->parallaxmat, probe->distinf);
 +      }
 +
 +      mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat);
 +      invert_m4(eprobe->parallaxmat);
 +}
 +
 +void EEVEE_lightprobes_planar_data_from_object(Object *ob, EEVEE_PlanarReflection *eplanar, EEVEE_LightProbeVisTest *vis_test)
 +{
 +      LightProbe *probe = (LightProbe *)ob->data;
 +      float normat[4][4], imat[4][4];
 +
 +      vis_test->collection = probe->visibility_grp;
 +      vis_test->invert = probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP;
 +      vis_test->cached = false;
 +
 +      /* Computing mtx : matrix that mirror position around object's XY plane. */
 +      normalize_m4_m4(normat, ob->obmat);  /* object > world */
 +      invert_m4_m4(imat, normat); /* world > object */
 +      /* XY reflection plane */
 +      imat[0][2] = -imat[0][2];
 +      imat[1][2] = -imat[1][2];
 +      imat[2][2] = -imat[2][2];
 +      imat[3][2] = -imat[3][2]; /* world > object > mirrored obj */
 +      mul_m4_m4m4(eplanar->mtx, normat, imat); /* world > object > mirrored obj > world */
 +
 +      /* Compute clip plane equation / normal. */
 +      copy_v3_v3(eplanar->plane_equation, ob->obmat[2]);
 +      normalize_v3(eplanar->plane_equation); /* plane normal */
 +      eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, ob->obmat[3]);
 +      eplanar->clipsta = probe->clipsta;
 +
 +      /* Compute XY clip planes. */
 +      normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]);
 +      normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]);
 +
 +      float vec[3] = {0.0f, 0.0f, 0.0f};
 +      vec[0] = 1.0f; vec[1] = 0.0f; vec[2] = 0.0f;
 +      mul_m4_v3(ob->obmat, vec); /* Point on the edge */
 +      eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec);
 +
 +      vec[0] = 0.0f; vec[1] = 1.0f; vec[2] = 0.0f;
 +      mul_m4_v3(ob->obmat, vec); /* Point on the edge */
 +      eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec);
 +
 +      vec[0] = -1.0f; vec[1] = 0.0f; vec[2] = 0.0f;
 +      mul_m4_v3(ob->obmat, vec); /* Point on the edge */
 +      eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec);
 +
 +      vec[0] = 0.0f; vec[1] = -1.0f; vec[2] = 0.0f;
 +      mul_m4_v3(ob->obmat, vec); /* Point on the edge */
 +      eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec);
 +
 +      /* Facing factors */
 +      float max_angle = max_ff(1e-2f, probe->falloff) * M_PI * 0.5f;
 +      float min_angle = 0.0f;
 +      eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle));
 +      eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale;
 +
 +      /* Distance factors */
 +      float max_dist = probe->distinf;
 +      float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf;
 +      eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist);
 +      eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale;
 +}
 +
 +static void lightbake_planar_compute_render_matrices(
 +        EEVEE_PlanarReflection *eplanar, DRWMatrixState *r_matstate, const float viewmat[4][4])
 +{
 +      /* Reflect Camera Matrix. */
 +      mul_m4_m4m4(r_matstate->viewmat, viewmat, eplanar->mtx);
 +      /* TODO FOV margin */
 +      /* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */
 +      DRW_viewport_matrix_get(r_matstate->winmat, DRW_MAT_WIN);
 +      /* Apply Projection Matrix. */
 +      mul_m4_m4m4(r_matstate->persmat, r_matstate->winmat, r_matstate->viewmat);
 +
 +      /* This is the matrix used to reconstruct texture coordinates.
 +       * We use the original view matrix because it does not create
 +       * visual artifacts if receiver is not perfectly aligned with
 +       * the planar reflection probe. */
 +      mul_m4_m4m4(eplanar->reflectionmat, r_matstate->winmat, viewmat); /* TODO FOV margin */
 +      /* Convert from [-1, 1] to [0, 1] (NDC to Texture coord). */
 +      mul_m4_m4m4(eplanar->reflectionmat, texcomat, eplanar->reflectionmat);
 +}
 +
 +static void eevee_lightprobes_extract_from_cache(EEVEE_LightProbesInfo *pinfo, LightCache *lcache)
 +{
 +      /* copy the entire cache for now (up to MAX_PROBE) */
 +      /* TODO Frutum cull to only add visible probes. */
 +      memcpy(pinfo->probe_data, lcache->cube_data, sizeof(EEVEE_LightProbe) * max_ii(1, min_ii(lcache->cube_len, MAX_PROBE)));
 +      /* TODO compute the max number of grid based on sample count. */
 +      memcpy(pinfo->grid_data, lcache->grid_data, sizeof(EEVEE_LightGrid) * max_ii(1, min_ii(lcache->grid_len, MAX_GRID)));
 +}
 +
 +void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_StorageList *stl = vedata->stl;
 +      LightCache *light_cache = stl->g_data->light_cache;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +
 +      eevee_lightprobes_extract_from_cache(sldata->probes, light_cache);
 +
 +      DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data);
 +      DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data);
 +
 +      /* For shading, save max level of the octahedron map */
 +      sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len - 1.0f;
 +      sldata->common_data.prb_lod_planar_max = (float)MAX_PLANAR_LOD_LEVEL;
 +      sldata->common_data.prb_irradiance_vis_size = light_cache->vis_res;
 +      sldata->common_data.prb_num_render_cube = max_ii(1, light_cache->cube_len);
 +      sldata->common_data.prb_num_render_grid = max_ii(1, light_cache->grid_len);
 +      sldata->common_data.prb_num_planar = pinfo->num_planar;
 +
 +      if (pinfo->num_planar != pinfo->cache_num_planar) {
 +              DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool);
 +              DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_depth);
 +              pinfo->cache_num_planar = pinfo->num_planar;
 +      }
 +      planar_pool_ensure_alloc(vedata, pinfo->num_planar);
 +
 +      /* If lightcache auto-update is enable we tag the relevant part
 +       * of the cache to update and fire up a baking job. */
 +      if (!DRW_state_is_image_render() && !DRW_state_is_opengl_render() &&
 +          (pinfo->do_grid_update || pinfo->do_cube_update))
 +      {
 +              const DRWContextState *draw_ctx = DRW_context_state_get();
 +              BLI_assert(draw_ctx->evil_C);
 +
 +              if (draw_ctx->scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) {
 +                      Scene *scene_orig = DEG_get_input_scene(draw_ctx->depsgraph);
 +                      if (scene_orig->eevee.light_cache != NULL) {
 +                              if (pinfo->do_grid_update) {
 +                                      scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_GRID;
 +                              }
 +                              /* If we update grid we need to update the cubemaps too.
 +                               * So always refresh cubemaps. */
 +                              scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_CUBE;
 +                              /* Tag the lightcache to auto update. */
 +                              scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_AUTO;
 +                              /* Use a notifier to trigger the operator after drawing. */
 +                              WM_event_add_notifier(draw_ctx->evil_C, NC_LIGHTPROBE, scene_orig);
 +                      }
 +              }
 +      }
 +}
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Rendering
 + * \{ */
 +
 +typedef struct EEVEE_BakeRenderData {
 +      EEVEE_Data *vedata;
 +      EEVEE_ViewLayerData *sldata;
 +      struct GPUFrameBuffer **face_fb; /* should contain 6 framebuffer */
 +} EEVEE_BakeRenderData;
 +
 +static void render_cubemap(
 +        void (*callback)(int face, EEVEE_BakeRenderData *user_data), EEVEE_BakeRenderData *user_data,
 +        const float pos[3], float clipsta, float clipend)
 +{
 +      DRWMatrixState matstate;
 +
 +      /* Move to capture position */
 +      float posmat[4][4];
 +      unit_m4(posmat);
 +      negate_v3_v3(posmat[3], pos);
 +
 +      perspective_m4(matstate.winmat, -clipsta, clipsta, -clipsta, clipsta, clipsta, clipend);
 +      invert_m4_m4(matstate.wininv, matstate.winmat);
 +
 +      /* 1 - Render to each cubeface individually.
 +       * We do this instead of using geometry shader because a) it's faster,
++       * b) it's easier than fixing the nodetree shaders (for view dependent effects). */
 +      for (int i = 0; i < 6; ++i) {
 +              /* Setup custom matrices */
 +              mul_m4_m4m4(matstate.viewmat, cubefacemat[i], posmat);
 +              mul_m4_m4m4(matstate.persmat, matstate.winmat, matstate.viewmat);
 +              invert_m4_m4(matstate.persinv, matstate.persmat);
 +              invert_m4_m4(matstate.viewinv, matstate.viewmat);
 +              invert_m4_m4(matstate.wininv, matstate.winmat);
 +
 +              DRW_viewport_matrix_override_set_all(&matstate);
 +
 +              callback(i, user_data);
 +      }
 +}
 +
 +static void render_reflections(
 +        void (*callback)(int face, EEVEE_BakeRenderData *user_data), EEVEE_BakeRenderData *user_data,
 +        EEVEE_PlanarReflection *planar_data, int ref_count)
 +{
 +      DRWMatrixState matstate;
 +
 +      float original_viewmat[4][4];
 +      DRW_viewport_matrix_get(original_viewmat, DRW_MAT_VIEW);
 +
 +      for (int i = 0; i < ref_count; ++i) {
 +              /* Setup custom matrices */
 +              lightbake_planar_compute_render_matrices(planar_data + i, &matstate, original_viewmat);
 +              invert_m4_m4(matstate.persinv, matstate.persmat);
 +              invert_m4_m4(matstate.viewinv, matstate.viewmat);
 +              invert_m4_m4(matstate.wininv, matstate.winmat);
 +              DRW_viewport_matrix_override_set_all(&matstate);
 +
 +              callback(i, user_data);
 +      }
 +}
 +
 +static void lightbake_render_world_face(int face, EEVEE_BakeRenderData *user_data)
 +{
 +      EEVEE_PassList *psl = user_data->vedata->psl;
 +      struct GPUFrameBuffer **face_fb = user_data->face_fb;
 +
 +      /* For world probe, we don't need to clear the color buffer
 +       * since we render the background directly. */
 +      GPU_framebuffer_bind(face_fb[face]);
 +      GPU_framebuffer_clear_depth(face_fb[face], 1.0f);
 +      DRW_draw_pass(psl->probe_background);
 +}
 +
 +void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6])
 +{
 +      EEVEE_BakeRenderData brdata = {
 +              .vedata = vedata,
 +              .face_fb = face_fb
 +      };
 +
 +      render_cubemap(lightbake_render_world_face, &brdata, (float[3]){0.0f}, 1.0f, 10.0f);
 +}
 +
 +static void lightbake_render_scene_face(int face, EEVEE_BakeRenderData *user_data)
 +{
 +      EEVEE_ViewLayerData *sldata = user_data->sldata;
 +      EEVEE_PassList *psl = user_data->vedata->psl;
 +      struct GPUFrameBuffer **face_fb = user_data->face_fb;
 +
 +      /* Be sure that cascaded shadow maps are updated. */
 +      EEVEE_draw_shadows(sldata, psl);
 +
 +      GPU_framebuffer_bind(face_fb[face]);
 +      GPU_framebuffer_clear_depth(face_fb[face], 1.0f);
 +
 +      DRW_draw_pass(psl->depth_pass);
 +      DRW_draw_pass(psl->depth_pass_cull);
 +      DRW_draw_pass(psl->probe_background);
 +      DRW_draw_pass(psl->material_pass);
 +      DRW_draw_pass(psl->sss_pass); /* Only output standard pass */
 +      EEVEE_draw_default_passes(psl);
 +}
 +
 +/* Render the scene to the probe_rt texture. */
 +void EEVEE_lightbake_render_scene(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6],
 +        const float pos[3], float near_clip, float far_clip)
 +{
 +      EEVEE_BakeRenderData brdata = {
 +              .vedata = vedata,
 +              .sldata = sldata,
 +              .face_fb = face_fb
 +      };
 +
 +      render_cubemap(lightbake_render_scene_face, &brdata, pos, near_clip, far_clip);
 +}
 +
 +static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *user_data)
 +{
 +      EEVEE_Data *vedata = user_data->vedata;
 +      EEVEE_ViewLayerData *sldata = user_data->sldata;
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_FramebufferList *fbl = vedata->fbl;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      EEVEE_PlanarReflection *eplanar = pinfo->planar_data + layer;
 +
 +      GPU_framebuffer_ensure_config(&fbl->planarref_fb, {
 +              GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer),
 +              GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer)
 +      });
 +
 +      /* Use visibility info for this planar reflection. */
 +      pinfo->vis_data = pinfo->planar_vis_tests[layer];
 +
 +      /* Avoid using the texture attached to framebuffer when rendering. */
 +      /* XXX */
 +      GPUTexture *tmp_planar_pool = txl->planar_pool;
 +      GPUTexture *tmp_planar_depth = txl->planar_depth;
 +      txl->planar_pool = e_data.planar_pool_placeholder;
 +      txl->planar_depth = e_data.depth_array_placeholder;
 +
 +      /* Be sure that cascaded shadow maps are updated. */
 +      DRW_stats_group_start("Planar Reflection");
 +
 +      /* Be sure that cascaded shadow maps are updated. */
 +      EEVEE_draw_shadows(sldata, psl);
 +      /* Since we are rendering with an inverted view matrix, we need
 +       * to invert the facing for backface culling to be the same. */
 +      DRW_state_invert_facing();
 +      /* Compute offset plane equation (fix missing texels near reflection plane). */
 +      copy_v4_v4(sldata->clip_data.clip_planes[0], eplanar->plane_equation);
 +      sldata->clip_data.clip_planes[0][3] += eplanar->clipsta;
 +      /* Set clipping plane */
 +      DRW_uniformbuffer_update(sldata->clip_ubo, &sldata->clip_data);
 +      DRW_state_clip_planes_count_set(1);
 +
 +      GPU_framebuffer_bind(fbl->planarref_fb);
 +      GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0);
 +
 +      /* Slight modification: we handle refraction as normal
 +       * shading and don't do SSRefraction. */
 +
 +      DRW_draw_pass(psl->depth_pass_clip);
 +      DRW_draw_pass(psl->depth_pass_clip_cull);
 +      DRW_draw_pass(psl->refract_depth_pass);
 +      DRW_draw_pass(psl->refract_depth_pass_cull);
 +
 +      DRW_draw_pass(psl->probe_background);
 +      EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer);
 +      EEVEE_occlusion_compute(sldata, vedata, tmp_planar_depth, layer);
 +
 +      GPU_framebuffer_bind(fbl->planarref_fb);
 +
 +      /* Shading pass */
 +      EEVEE_draw_default_passes(psl);
 +      DRW_draw_pass(psl->material_pass);
 +      DRW_draw_pass(psl->sss_pass); /* Only output standard pass */
 +      DRW_draw_pass(psl->refract_pass);
 +
 +      /* Transparent */
 +      if (DRW_state_is_image_render()) {
 +              /* Do the reordering only for offline because it can be costly. */
 +              DRW_pass_sort_shgroup_z(psl->transparent_pass);
 +      }
 +      DRW_draw_pass(psl->transparent_pass);
 +
 +      DRW_state_invert_facing();
 +      DRW_state_clip_planes_reset();
 +
 +      DRW_stats_group_end();
 +
 +      /* Restore */
 +      txl->planar_pool = tmp_planar_pool;
 +      txl->planar_depth = tmp_planar_depth;
 +}
 +
 +static void eevee_lightbake_render_scene_to_planars(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_BakeRenderData brdata = {
 +              .vedata = vedata,
 +              .sldata = sldata,
 +      };
 +
 +      render_reflections(lightbake_render_scene_reflected, &brdata, sldata->probes->planar_data, sldata->probes->num_planar);
 +}
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Filtering
 + * \{ */
 +
 +/* Glossy filter rt_color to light_cache->cube_tx.tex at index probe_idx */
 +void EEVEE_lightbake_filter_glossy(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        struct GPUTexture *rt_color, struct GPUFrameBuffer *fb,
 +        int probe_idx, float intensity, int maxlevel)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      LightCache *light_cache = vedata->stl->g_data->light_cache;
 +
 +      float target_size = (float)GPU_texture_width(rt_color);
 +
 +      /* Max lod used from the render target probe */
 +      pinfo->lod_rt_max = floorf(log2f(target_size)) - 2.0f;
 +      pinfo->intensity_fac = intensity;
 +
 +      /* Start fresh */
 +      GPU_framebuffer_ensure_config(&fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_NONE
 +      });
 +
 +      /* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */
 +      /* Bind next framebuffer to be able to gen. mips for probe_rt. */
 +      EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max));
 +
 +      /* 3 - Render to probe array to the specified layer, do prefiltering. */
 +      int mipsize = GPU_texture_width(light_cache->cube_tx.tex);
 +      for (int i = 0; i < maxlevel + 1; i++) {
 +              float bias = (i == 0) ? -1.0f : 1.0f;
 +              pinfo->texel_size = 1.0f / (float)mipsize;
 +              pinfo->padding_size = (float)(1 << (maxlevel - i - 1));
 +              pinfo->padding_size *= pinfo->texel_size;
 +              pinfo->layer = probe_idx;
 +              pinfo->roughness = i / (float)maxlevel;
 +              pinfo->roughness *= pinfo->roughness; /* Disney Roughness */
 +              pinfo->roughness *= pinfo->roughness; /* Distribute Roughness accros lod more evenly */
 +              CLAMP(pinfo->roughness, 1e-8f, 0.99999f); /* Avoid artifacts */
 +
 +#if 1 /* Variable Sample count (fast) */
 +              switch (i) {
 +                      case 0: pinfo->samples_len = 1.0f; break;
 +                      case 1: pinfo->samples_len = 16.0f; break;
 +                      case 2: pinfo->samples_len = 32.0f; break;
 +                      case 3: pinfo->samples_len = 64.0f; break;
 +                      default: pinfo->samples_len = 128.0f; break;
 +              }
 +#else /* Constant Sample count (slow) */
 +              pinfo->samples_len = 1024.0f;
 +#endif
 +
 +              pinfo->samples_len_inv = 1.0f / pinfo->samples_len;
 +              pinfo->lodfactor = bias + 0.5f * log((float)(target_size * target_size) * pinfo->samples_len_inv) / log(2);
 +
 +              GPU_framebuffer_ensure_config(&fb, {
 +                      GPU_ATTACHMENT_NONE,
 +                      GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i)
 +              });
 +              GPU_framebuffer_bind(fb);
 +              GPU_framebuffer_viewport_set(fb, 0, 0, mipsize, mipsize);
 +              DRW_draw_pass(psl->probe_glossy_compute);
 +
 +              mipsize /= 2;
 +              CLAMP_MIN(mipsize, 1);
 +      }
 +}
 +
 +/* Diffuse filter rt_color to light_cache->grid_tx.tex at index grid_offset */
 +void EEVEE_lightbake_filter_diffuse(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        struct GPUTexture *rt_color, struct GPUFrameBuffer *fb,
 +        int grid_offset, float intensity)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      LightCache *light_cache = vedata->stl->g_data->light_cache;
 +
 +      float target_size = (float)GPU_texture_width(rt_color);
 +
 +      pinfo->intensity_fac = intensity;
 +
 +      /* find cell position on the virtual 3D texture */
 +      /* NOTE : Keep in sync with load_irradiance_cell() */
 +#if defined(IRRADIANCE_SH_L2)
 +      int size[2] = {3, 3};
 +#elif defined(IRRADIANCE_CUBEMAP)
 +      int size[2] = {8, 8};
 +      pinfo->samples_len = 1024.0f;
 +#elif defined(IRRADIANCE_HL2)
 +      int size[2] = {3, 2};
 +      pinfo->samples_len = 1024.0f;
 +#endif
 +
 +      int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / size[0];
 +      int x = size[0] * (grid_offset % cell_per_row);
 +      int y = size[1] * (grid_offset / cell_per_row);
 +
 +#ifndef IRRADIANCE_SH_L2
 +      /* Tweaking parameters to balance perf. vs precision */
 +      const float bias = 0.0f;
 +      pinfo->samples_len_inv = 1.0f / pinfo->samples_len;
 +      pinfo->lodfactor = bias + 0.5f * log((float)(target_size * target_size) * pinfo->samples_len_inv) / log(2);
 +      pinfo->lod_rt_max = floorf(log2f(target_size)) - 2.0f;
 +#else
 +      pinfo->shres = 32; /* Less texture fetches & reduce branches */
 +      pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */
 +#endif
 +
 +      /* Start fresh */
 +      GPU_framebuffer_ensure_config(&fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_NONE
 +      });
 +
 +      /* 4 - Compute diffuse irradiance */
 +      EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max));
 +
 +      GPU_framebuffer_ensure_config(&fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, 0)
 +      });
 +      GPU_framebuffer_bind(fb);
 +      GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]);
 +      DRW_draw_pass(psl->probe_diffuse_compute);
 +}
 +
 +/* Filter rt_depth to light_cache->grid_tx.tex at index grid_offset */
 +void EEVEE_lightbake_filter_visibility(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        struct GPUTexture *UNUSED(rt_depth), struct GPUFrameBuffer *fb,
 +        int grid_offset, float clipsta, float clipend,
 +        float vis_range, float vis_blur, int vis_size)
 +{
 +      EEVEE_PassList *psl = vedata->psl;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      LightCache *light_cache = vedata->stl->g_data->light_cache;
 +
 +      pinfo->samples_len = 512.0f; /* TODO refine */
 +      pinfo->samples_len_inv = 1.0f / pinfo->samples_len;
 +      pinfo->shres = vis_size;
 +      pinfo->visibility_range = vis_range;
 +      pinfo->visibility_blur = vis_blur;
 +      pinfo->near_clip = -clipsta;
 +      pinfo->far_clip = -clipend;
 +      pinfo->texel_size = 1.0f / (float)vis_size;
 +
 +      int cell_per_col = GPU_texture_height(light_cache->grid_tx.tex) / vis_size;
 +      int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / vis_size;
 +      int x = vis_size * (grid_offset % cell_per_row);
 +      int y = vis_size * ((grid_offset / cell_per_row) % cell_per_col);
 +      int layer = 1 + ((grid_offset / cell_per_row) / cell_per_col);
 +
 +      GPU_framebuffer_ensure_config(&fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, layer)
 +      });
 +      GPU_framebuffer_bind(fb);
 +      GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size);
 +      DRW_draw_pass(psl->probe_visibility_compute);
 +}
 +
 +/* Actually a simple downsampling */
 +static void downsample_planar(void *vedata, int level)
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +
 +      const float *size = DRW_viewport_size_get();
 +      copy_v2_v2(stl->g_data->planar_texel_size, size);
 +      for (int i = 0; i < level - 1; ++i) {
 +              stl->g_data->planar_texel_size[0] /= 2.0f;
 +              stl->g_data->planar_texel_size[1] /= 2.0f;
 +              min_ff(floorf(stl->g_data->planar_texel_size[0]), 1.0f);
 +              min_ff(floorf(stl->g_data->planar_texel_size[1]), 1.0f);
 +      }
 +      invert_v2(stl->g_data->planar_texel_size);
 +
 +      DRW_draw_pass(psl->probe_planar_downsample_ps);
 +}
 +
 +static void EEVEE_lightbake_filter_planar(EEVEE_Data *vedata)
 +{
 +      EEVEE_TextureList *txl = vedata->txl;
 +      EEVEE_FramebufferList  *fbl = vedata->fbl;
 +
 +      DRW_stats_group_start("Planar Probe Downsample");
 +
 +      GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_TEXTURE(txl->planar_pool)
 +      });
 +
 +      GPU_framebuffer_recursive_downsample(fbl->planar_downsample_fb, MAX_PLANAR_LOD_LEVEL, &downsample_planar, vedata);
 +      DRW_stats_group_end();
 +}
 +
 +/** \} */
 +
 +void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
 +      EEVEE_LightProbesInfo *pinfo = sldata->probes;
 +      DRWMatrixState saved_mats;
 +
 +      if (pinfo->num_planar == 0) {
 +              /* Disable SSR if we cannot read previous frame */
 +              common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer;
 +              common_data->prb_num_planar = 0;
 +              return;
 +      }
 +
 +      /* We need to save the Matrices before overidding them */
 +      DRW_viewport_matrix_get_all(&saved_mats);
 +
 +      /* Temporary Remove all planar reflections (avoid lag effect). */
 +      common_data->prb_num_planar = 0;
 +      /* Turn off ssr to avoid black specular */
 +      common_data->ssr_toggle = false;
 +      common_data->sss_toggle = false;
 +
 +      DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
 +
 +      /* Rendering happens here! */
 +      eevee_lightbake_render_scene_to_planars(sldata, vedata);
 +
 +      /* Make sure no aditionnal visibility check runs after this. */
 +      pinfo->vis_data.collection = NULL;
 +
 +      DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data);
 +
 +      /* Restore */
 +      common_data->prb_num_planar = pinfo->num_planar;
 +      common_data->ssr_toggle = true;
 +      common_data->sss_toggle = true;
 +
 +      /* Prefilter for SSR */
 +      if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) {
 +              EEVEE_lightbake_filter_planar(vedata);
 +      }
 +
 +      DRW_viewport_matrix_override_set_all(&saved_mats);
 +
 +      if (DRW_state_is_image_render()) {
 +              /* Sort transparents because planar reflections could have re-sorted them. */
 +              DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass);
 +      }
 +
 +      /* Disable SSR if we cannot read previous frame */
 +      common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer;
 +}
 +
 +void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
 +      LightCache *light_cache = vedata->stl->g_data->light_cache;
 +
 +      if (light_cache->flag & LIGHTCACHE_UPDATE_WORLD) {
 +              DRWMatrixState saved_mats;
 +              DRW_viewport_matrix_get_all(&saved_mats);
 +              EEVEE_lightbake_update_world_quick(sldata, vedata, scene_eval);
 +              DRW_viewport_matrix_override_set_all(&saved_mats);
 +      }
 +}
 +
 +void EEVEE_lightprobes_free(void)
 +{
 +      MEM_SAFE_FREE(e_data.format_probe_display_cube);
 +      MEM_SAFE_FREE(e_data.format_probe_display_planar);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_default_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_default_studiolight_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh);
 +      DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh);
 +      DRW_TEXTURE_FREE_SAFE(e_data.hammersley);
 +      DRW_TEXTURE_FREE_SAFE(e_data.planar_pool_placeholder);
 +      DRW_TEXTURE_FREE_SAFE(e_data.depth_placeholder);
 +      DRW_TEXTURE_FREE_SAFE(e_data.depth_array_placeholder);
 +}
index b8a7789,0000000..a458be0
mode 100644,000000..100644
--- /dev/null
@@@ -1,1304 -1,0 +1,1304 @@@
-                       /* Scale power to account for the lower area of the ellipse compared to the surrouding rectangle. */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_lights.c
 + *  \ingroup DNA
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BLI_dynstr.h"
 +#include "BLI_rect.h"
 +
 +#include "BKE_object.h"
 +
 +#include "DEG_depsgraph_query.h"
 +
 +#include "eevee_engine.h"
 +#include "eevee_private.h"
 +
 +#define SHADOW_CASTER_ALLOC_CHUNK 16
 +
 +// #define DEBUG_CSM
 +
 +static struct {
 +      struct GPUShader *shadow_sh;
 +      struct GPUShader *shadow_store_cube_sh[SHADOW_METHOD_MAX];
 +      struct GPUShader *shadow_store_cube_high_sh[SHADOW_METHOD_MAX];
 +      struct GPUShader *shadow_store_cascade_sh[SHADOW_METHOD_MAX];
 +      struct GPUShader *shadow_store_cascade_high_sh[SHADOW_METHOD_MAX];
 +      struct GPUShader *shadow_copy_cube_sh[SHADOW_METHOD_MAX];
 +      struct GPUShader *shadow_copy_cascade_sh[SHADOW_METHOD_MAX];
 +} e_data = {NULL}; /* Engine data */
 +
 +extern char datatoc_shadow_vert_glsl[];
 +extern char datatoc_shadow_frag_glsl[];
 +extern char datatoc_shadow_store_frag_glsl[];
 +extern char datatoc_shadow_copy_frag_glsl[];
 +extern char datatoc_concentric_samples_lib_glsl[];
 +
 +/* Prototype */
 +static void eevee_light_setup(Object *ob, EEVEE_Light *evli);
 +
 +/* *********** LIGHT BITS *********** */
 +static void lightbits_set_single(EEVEE_LightBits *bitf, uint idx, bool val)
 +{
 +      if (val) {
 +              bitf->fields[idx / 8] |=  (1 << (idx % 8));
 +      }
 +      else {
 +              bitf->fields[idx / 8] &= ~(1 << (idx % 8));
 +      }
 +}
 +
 +static void lightbits_set_all(EEVEE_LightBits *bitf, bool val)
 +{
 +      memset(bitf, (val) ? 0xFF : 0x00, sizeof(EEVEE_LightBits));
 +}
 +
 +static void lightbits_or(EEVEE_LightBits *r, const EEVEE_LightBits *v)
 +{
 +      for (int i = 0; i < MAX_LIGHTBITS_FIELDS; ++i) {
 +              r->fields[i] |= v->fields[i];
 +      }
 +}
 +
 +static bool lightbits_get(const EEVEE_LightBits *r, uint idx)
 +{
 +      return r->fields[idx / 8] & (1 << (idx % 8));
 +}
 +
 +static void lightbits_convert(
 +        EEVEE_LightBits *r, const EEVEE_LightBits *bitf, const int *light_bit_conv_table, uint table_length)
 +{
 +      for (int i = 0; i < table_length; ++i) {
 +              if (lightbits_get(bitf, i) != 0) {
 +                      if (light_bit_conv_table[i] >= 0) {
 +                              r->fields[i / 8] |= (1 << (i % 8));
 +                      }
 +              }
 +      }
 +}
 +
 +/* *********** FUNCTIONS *********** */
 +
 +void EEVEE_lights_init(EEVEE_ViewLayerData *sldata)
 +{
 +      const uint shadow_ubo_size = sizeof(EEVEE_Shadow) * MAX_SHADOW +
 +                                           sizeof(EEVEE_ShadowCube) * MAX_SHADOW_CUBE +
 +                                           sizeof(EEVEE_ShadowCascade) * MAX_SHADOW_CASCADE;
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
 +
 +      if (!e_data.shadow_sh) {
 +              e_data.shadow_sh = DRW_shader_create(
 +                      datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, NULL);
 +      }
 +
 +      if (!sldata->lamps) {
 +              sldata->lamps              = MEM_callocN(sizeof(EEVEE_LampsInfo), "EEVEE_LampsInfo");
 +              sldata->light_ubo          = DRW_uniformbuffer_create(sizeof(EEVEE_Light) * MAX_LIGHT, NULL);
 +              sldata->shadow_ubo         = DRW_uniformbuffer_create(shadow_ubo_size, NULL);
 +              sldata->shadow_render_ubo  = DRW_uniformbuffer_create(sizeof(EEVEE_ShadowRender), NULL);
 +
 +              for (int i = 0; i < 2; ++i) {
 +                      sldata->shcasters_buffers[i].shadow_casters = MEM_callocN(sizeof(EEVEE_ShadowCaster) * SHADOW_CASTER_ALLOC_CHUNK, "EEVEE_ShadowCaster buf");
 +                      sldata->shcasters_buffers[i].flags = MEM_callocN(sizeof(sldata->shcasters_buffers[0].flags) * SHADOW_CASTER_ALLOC_CHUNK, "EEVEE_shcast_buffer flags buf");
 +                      sldata->shcasters_buffers[i].alloc_count = SHADOW_CASTER_ALLOC_CHUNK;
 +                      sldata->shcasters_buffers[i].count = 0;
 +              }
 +
 +              sldata->lamps->shcaster_frontbuffer = &sldata->shcasters_buffers[0];
 +              sldata->lamps->shcaster_backbuffer = &sldata->shcasters_buffers[1];
 +      }
 +
 +      /* Flip buffers */
 +      SWAP(EEVEE_ShadowCasterBuffer *, sldata->lamps->shcaster_frontbuffer, sldata->lamps->shcaster_backbuffer);
 +
 +      const int sh_method = scene_eval->eevee.shadow_method;
 +      int sh_cube_size = scene_eval->eevee.shadow_cube_size;
 +      int sh_cascade_size = scene_eval->eevee.shadow_cascade_size;
 +      const bool sh_high_bitdepth = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) != 0;
 +
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      if ((linfo->shadow_cube_size != sh_cube_size) ||
 +          (linfo->shadow_method != sh_method) ||
 +          (linfo->shadow_high_bitdepth != sh_high_bitdepth))
 +      {
 +              BLI_assert((sh_cube_size > 0) && (sh_cube_size <= 4096));
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_target);
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur);
 +
 +              /* Compute adequate size for the octahedral map. */
 +              linfo->shadow_cube_store_size = OCTAHEDRAL_SIZE_FROM_CUBESIZE(sh_cube_size);
 +
 +              CLAMP(linfo->shadow_cube_store_size, 1, 4096);
 +              CLAMP(sh_cube_size, 1, 4096);
 +
 +              linfo->shadow_render_data.cube_texel_size = 1.0f / sh_cube_size;
 +      }
 +
 +      if ((linfo->shadow_cascade_size != sh_cascade_size) ||
 +          (linfo->shadow_method != sh_method) ||
 +          (linfo->shadow_high_bitdepth != sh_high_bitdepth))
 +      {
 +              BLI_assert((sh_cascade_size > 0) && (sh_cascade_size <= 4096));
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_target);
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_blur);
 +
 +              CLAMP(sh_cascade_size, 1, 4096);
 +      }
 +
 +      linfo->shadow_high_bitdepth = sh_high_bitdepth;
 +      linfo->shadow_method = sh_method;
 +      linfo->shadow_cube_size = sh_cube_size;
 +      linfo->shadow_cascade_size = sh_cascade_size;
 +
 +      /* only compile the ones needed. reduce startup time. */
 +      if ((sh_method == SHADOW_ESM) && !e_data.shadow_copy_cube_sh[SHADOW_ESM]) {
 +              e_data.shadow_copy_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
 +                      datatoc_shadow_copy_frag_glsl,
 +                      "#define ESM\n"
 +                      "#define COPY\n");
 +              e_data.shadow_copy_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
 +                      datatoc_shadow_copy_frag_glsl,
 +                      "#define ESM\n"
 +                      "#define COPY\n"
 +                      "#define CSM\n");
 +      }
 +      else if ((sh_method == SHADOW_VSM) && !e_data.shadow_copy_cube_sh[SHADOW_VSM]) {
 +              e_data.shadow_copy_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
 +                      datatoc_shadow_copy_frag_glsl,
 +                      "#define VSM\n"
 +                      "#define COPY\n");
 +              e_data.shadow_copy_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
 +                      datatoc_shadow_copy_frag_glsl,
 +                      "#define VSM\n"
 +                      "#define COPY\n"
 +                      "#define CSM\n");
 +      }
 +}
 +
 +static GPUShader *eevee_lights_get_store_sh(int shadow_method, bool high_blur, bool cascade)
 +{
 +      GPUShader **shader;
 +
 +      if (cascade) {
 +              shader = (high_blur) ? &e_data.shadow_store_cascade_high_sh[shadow_method]
 +                                   : &e_data.shadow_store_cascade_sh[shadow_method];
 +      }
 +      else {
 +              shader = (high_blur) ? &e_data.shadow_store_cube_high_sh[shadow_method]
 +                                   : &e_data.shadow_store_cube_sh[shadow_method];
 +      }
 +
 +      if (*shader == NULL) {
 +              DynStr *ds_frag = BLI_dynstr_new();
 +              BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
 +              BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
 +              char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
 +              BLI_dynstr_free(ds_frag);
 +
 +              ds_frag = BLI_dynstr_new();
 +              BLI_dynstr_append(ds_frag, (shadow_method == SHADOW_VSM) ? "#define VSM\n" : "#define ESM\n");
 +              if (high_blur) BLI_dynstr_append(ds_frag, "#define HIGH_BLUR\n");
 +              if (cascade)   BLI_dynstr_append(ds_frag, "#define CSM\n");
 +              char *define_str = BLI_dynstr_get_cstring(ds_frag);
 +              BLI_dynstr_free(ds_frag);
 +
 +              *shader = DRW_shader_create_fullscreen(
 +                      store_shadow_shader_str, define_str);
 +
 +              MEM_freeN(store_shadow_shader_str);
 +              MEM_freeN(define_str);
 +      }
 +
 +      return *shader;
 +}
 +
 +static DRWPass *eevee_lights_cube_store_pass_get(EEVEE_PassList *psl, EEVEE_ViewLayerData *sldata, int shadow_method, int shadow_samples_len)
 +{
 +      bool high_blur = shadow_samples_len > 16;
 +      DRWPass **pass = (high_blur) ? &psl->shadow_cube_store_pass : &psl->shadow_cube_store_high_pass;
 +      if (*pass == NULL) {
 +              EEVEE_LampsInfo *linfo = sldata->lamps;
 +              *pass = DRW_pass_create("Shadow Cube Storage Pass", DRW_STATE_WRITE_COLOR);
 +              GPUShader *shader = eevee_lights_get_store_sh(shadow_method, high_blur, false);
 +              DRWShadingGroup *grp = DRW_shgroup_create(shader, *pass);
 +              DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cube_blur);
 +              DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 +              DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 +              DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 +      }
 +      return *pass;
 +}
 +
 +static DRWPass *eevee_lights_cascade_store_pass_get(EEVEE_PassList *psl, EEVEE_ViewLayerData *sldata, int shadow_method, int shadow_samples_len)
 +{
 +      bool high_blur = shadow_samples_len > 16;
 +      DRWPass **pass = (high_blur) ? &psl->shadow_cascade_store_pass : &psl->shadow_cascade_store_high_pass;
 +      if (*pass == NULL) {
 +              EEVEE_LampsInfo *linfo = sldata->lamps;
 +              *pass = DRW_pass_create("Shadow Cascade Storage Pass", DRW_STATE_WRITE_COLOR);
 +              GPUShader *shader = eevee_lights_get_store_sh(shadow_method, high_blur, true);
 +              DRWShadingGroup *grp = DRW_shgroup_create(shader, *pass);
 +              DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cascade_blur);
 +              DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 +              DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
 +              DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 +              DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 +      }
 +      return *pass;
 +}
 +
 +void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      EEVEE_StorageList *stl = vedata->stl;
 +      EEVEE_PassList *psl = vedata->psl;
 +
 +      linfo->shcaster_frontbuffer->count = 0;
 +      linfo->num_light = 0;
 +      linfo->num_cube_layer = 0;
 +      linfo->num_cascade_layer = 0;
 +      linfo->gpu_cube_len = linfo->gpu_cascade_len = linfo->gpu_shadow_len = 0;
 +      linfo->cpu_cube_len = linfo->cpu_cascade_len = 0;
 +      memset(linfo->light_ref, 0, sizeof(linfo->light_ref));
 +      memset(linfo->shadow_cube_ref, 0, sizeof(linfo->shadow_cube_ref));
 +      memset(linfo->shadow_cascade_ref, 0, sizeof(linfo->shadow_cascade_ref));
 +      memset(linfo->new_shadow_id, -1, sizeof(linfo->new_shadow_id));
 +
 +      /* Shadow Casters: Reset flags. */
 +      memset(linfo->shcaster_backbuffer->flags, (char)SHADOW_CASTER_PRUNED, linfo->shcaster_backbuffer->alloc_count);
 +      memset(linfo->shcaster_frontbuffer->flags, 0x00, linfo->shcaster_frontbuffer->alloc_count);
 +
 +      psl->shadow_cube_store_pass = NULL;
 +      psl->shadow_cube_store_high_pass = NULL;
 +      psl->shadow_cascade_store_pass = NULL;
 +      psl->shadow_cascade_store_high_pass = NULL;
 +
 +      {
 +              psl->shadow_cube_copy_pass = DRW_pass_create("Shadow Copy Pass", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(
 +                      e_data.shadow_copy_cube_sh[linfo->shadow_method], psl->shadow_cube_copy_pass);
 +              DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cube_target);
 +              DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 +              DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 +              DRW_shgroup_uniform_int(grp, "faceId", &linfo->current_shadow_face, 1);
 +              DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 +      }
 +
 +      {
 +              psl->shadow_cascade_copy_pass = DRW_pass_create("Shadow Cascade Copy Pass", DRW_STATE_WRITE_COLOR);
 +
 +              DRWShadingGroup *grp = DRW_shgroup_create(
 +                      e_data.shadow_copy_cascade_sh[linfo->shadow_method], psl->shadow_cascade_copy_pass);
 +              DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cascade_target);
 +              DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 +              DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 +              DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
 +              DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 +      }
 +
 +      {
 +              DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
 +              psl->shadow_pass = DRW_pass_create("Shadow Pass", state);
 +
 +              stl->g_data->shadow_shgrp = DRW_shgroup_create(e_data.shadow_sh, psl->shadow_pass);
 +      }
 +}
 +
 +void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob)
 +{
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +
 +      /* Step 1 find all lamps in the scene and setup them */
 +      if (linfo->num_light >= MAX_LIGHT) {
 +              printf("Too many lights in the scene !!!\n");
 +      }
 +      else {
 +              Lamp *la = (Lamp *)ob->data;
 +              EEVEE_Light *evli = linfo->light_data + linfo->num_light;
 +              eevee_light_setup(ob, evli);
 +
 +              /* We do not support shadowmaps for dupli lamps. */
 +              if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
 +                      linfo->num_light++;
 +                      return;
 +              }
 +
 +              EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
 +
 +              /* Save previous shadow id. */
 +              int prev_cube_sh_id = led->prev_cube_shadow_id;
 +
 +              /* Default light without shadows */
 +              led->data.ld.shadow_id = -1;
 +              led->prev_cube_shadow_id = -1;
 +
 +              if (la->mode & LA_SHADOW) {
 +                      if (la->type == LA_SUN) {
 +                              int sh_nbr = 1; /* TODO : MSM */
 +                              int cascade_nbr = la->cascade_count;
 +
 +                              if ((linfo->gpu_cascade_len + sh_nbr) <= MAX_SHADOW_CASCADE) {
 +                                      /* Save Light object. */
 +                                      linfo->shadow_cascade_ref[linfo->cpu_cascade_len] = ob;
 +
 +                                      /* Store indices. */
 +                                      EEVEE_ShadowCascadeData *data = &led->data.scad;
 +                                      data->shadow_id = linfo->gpu_shadow_len;
 +                                      data->cascade_id = linfo->gpu_cascade_len;
 +                                      data->layer_id = linfo->num_cascade_layer;
 +
 +                                      /* Increment indices. */
 +                                      linfo->gpu_shadow_len += 1;
 +                                      linfo->gpu_cascade_len += sh_nbr;
 +                                      linfo->num_cascade_layer += sh_nbr * cascade_nbr;
 +
 +                                      linfo->cpu_cascade_len += 1;
 +                              }
 +                      }
 +                      else if (la->type == LA_SPOT || la->type == LA_LOCAL || la->type == LA_AREA) {
 +                              int sh_nbr = 1; /* TODO : MSM */
 +
 +                              if ((linfo->gpu_cube_len + sh_nbr) <= MAX_SHADOW_CUBE) {
 +                                      /* Save Light object. */
 +                                      linfo->shadow_cube_ref[linfo->cpu_cube_len] = ob;
 +
 +                                      /* For light update tracking. */
 +                                      if ((prev_cube_sh_id >= 0) &&
 +                                          (prev_cube_sh_id < linfo->shcaster_backbuffer->count))
 +                                      {
 +                                              linfo->new_shadow_id[prev_cube_sh_id] = linfo->cpu_cube_len;
 +                                      }
 +                                      led->prev_cube_shadow_id = linfo->cpu_cube_len;
 +
 +                                      /* Saving lamp bounds for later. */
 +                                      BLI_assert(linfo->cpu_cube_len >= 0 && linfo->cpu_cube_len < MAX_LIGHT);
 +                                      copy_v3_v3(linfo->shadow_bounds[linfo->cpu_cube_len].center, ob->obmat[3]);
 +                                      linfo->shadow_bounds[linfo->cpu_cube_len].radius = la->clipend;
 +
 +                                      EEVEE_ShadowCubeData *data = &led->data.scd;
 +                                      /* Store indices. */
 +                                      data->shadow_id = linfo->gpu_shadow_len;
 +                                      data->cube_id = linfo->gpu_cube_len;
 +                                      data->layer_id = linfo->num_cube_layer;
 +
 +                                      /* Increment indices. */
 +                                      linfo->gpu_shadow_len += 1;
 +                                      linfo->gpu_cube_len += sh_nbr;
 +                                      linfo->num_cube_layer += sh_nbr;
 +
 +                                      linfo->cpu_cube_len += 1;
 +                              }
 +                      }
 +              }
 +
 +              led->data.ld.light_id = linfo->num_light;
 +              linfo->light_ref[linfo->num_light] = ob;
 +              linfo->num_light++;
 +      }
 +}
 +
 +/* Add a shadow caster to the shadowpasses */
 +void EEVEE_lights_cache_shcaster_add(
 +        EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_StorageList *stl, struct GPUBatch *geom, Object *ob)
 +{
 +      DRW_shgroup_call_object_add(
 +              stl->g_data->shadow_shgrp,
 +              geom, ob);
 +}
 +
 +void EEVEE_lights_cache_shcaster_material_add(
 +      EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl, struct GPUMaterial *gpumat,
 +      struct GPUBatch *geom, struct Object *ob, float *alpha_threshold)
 +{
 +      /* TODO / PERF : reuse the same shading group for objects with the same material */
 +      DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, psl->shadow_pass);
 +
 +      if (grp == NULL) return;
 +
 +      /* Grrr needed for correctness but not 99% of the time not needed.
 +       * TODO detect when needed? */
 +      DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
 +      DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
 +      DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
 +      DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
 +      DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
 +      DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +
 +      if (alpha_threshold != NULL)
 +              DRW_shgroup_uniform_float(grp, "alphaThreshold", alpha_threshold, 1);
 +
 +      DRW_shgroup_call_object_add(grp, geom, ob);
 +}
 +
 +/* Make that object update shadow casting lamps inside its influence bounding box. */
 +void EEVEE_lights_cache_shcaster_object_add(EEVEE_ViewLayerData *sldata, Object *ob)
 +{
 +      if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
 +              /* TODO: Special case for dupli objects because we cannot save the object pointer. */
 +              return;
 +      }
 +
 +      EEVEE_ObjectEngineData *oedata = EEVEE_object_data_ensure(ob);
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
 +      EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
 +      int past_id = oedata->shadow_caster_id;
 +
 +      /* Update flags in backbuffer. */
 +      if (past_id > -1 && past_id < backbuffer->count) {
 +              backbuffer->flags[past_id] &= ~SHADOW_CASTER_PRUNED;
 +
 +              if (oedata->need_update) {
 +                      backbuffer->flags[past_id] |= SHADOW_CASTER_UPDATED;
 +              }
 +      }
 +
 +      /* Update id. */
 +      oedata->shadow_caster_id = frontbuffer->count++;
 +
 +      /* Make sure shadow_casters is big enough. */
 +      if (oedata->shadow_caster_id >= frontbuffer->alloc_count) {
 +              frontbuffer->alloc_count += SHADOW_CASTER_ALLOC_CHUNK;
 +              frontbuffer->shadow_casters = MEM_reallocN(frontbuffer->shadow_casters, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
 +              frontbuffer->flags = MEM_reallocN(frontbuffer->flags, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
 +      }
 +
 +      EEVEE_ShadowCaster *shcaster = frontbuffer->shadow_casters + oedata->shadow_caster_id;
 +
 +      if (oedata->need_update) {
 +              frontbuffer->flags[oedata->shadow_caster_id] = SHADOW_CASTER_UPDATED;
 +      }
 +
 +      /* Update World AABB in frontbuffer. */
 +      BoundBox *bb = BKE_object_boundbox_get(ob);
 +      float min[3], max[3];
 +      INIT_MINMAX(min, max);
 +      for (int i = 0; i < 8; ++i) {
 +              float vec[3];
 +              copy_v3_v3(vec, bb->vec[i]);
 +              mul_m4_v3(ob->obmat, vec);
 +              minmax_v3v3_v3(min, max, vec);
 +      }
 +
 +      EEVEE_BoundBox *aabb = &shcaster->bbox;
 +      add_v3_v3v3(aabb->center, min, max);
 +      mul_v3_fl(aabb->center, 0.5f);
 +      sub_v3_v3v3(aabb->halfdim, aabb->center, max);
 +
 +      aabb->halfdim[0] = fabsf(aabb->halfdim[0]);
 +      aabb->halfdim[1] = fabsf(aabb->halfdim[1]);
 +      aabb->halfdim[2] = fabsf(aabb->halfdim[2]);
 +
 +      oedata->need_update = false;
 +}
 +
 +void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata)
 +{
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      GPUTextureFormat shadow_pool_format = GPU_R32F;
 +
 +      sldata->common_data.la_num_light = linfo->num_light;
 +
 +      /* Setup enough layers. */
 +      /* Free textures if number mismatch. */
 +      if (linfo->num_cube_layer != linfo->cache_num_cube_layer) {
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
 +              linfo->cache_num_cube_layer = linfo->num_cube_layer;
 +              linfo->update_flag |= LIGHT_UPDATE_SHADOW_CUBE;
 +      }
 +
 +      if (linfo->num_cascade_layer != linfo->cache_num_cascade_layer) {
 +              DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
 +              linfo->cache_num_cascade_layer = linfo->num_cascade_layer;
 +      }
 +
 +      switch (linfo->shadow_method) {
 +              case SHADOW_ESM: shadow_pool_format = ((linfo->shadow_high_bitdepth) ? GPU_R32F : GPU_R16F); break;
 +              case SHADOW_VSM: shadow_pool_format = ((linfo->shadow_high_bitdepth) ? GPU_RG32F : GPU_RG16F); break;
 +              default:
 +                      BLI_assert(!"Incorrect Shadow Method");
 +                      break;
 +      }
 +
 +      /* Cubemaps */
 +      if (!sldata->shadow_cube_target) {
 +              sldata->shadow_cube_target = DRW_texture_create_cube(
 +                      linfo->shadow_cube_size, GPU_DEPTH_COMPONENT24, 0, NULL);
 +              sldata->shadow_cube_blur = DRW_texture_create_cube(
 +                      linfo->shadow_cube_size, shadow_pool_format, DRW_TEX_FILTER, NULL);
 +      }
 +      if (!sldata->shadow_cube_pool) {
 +              sldata->shadow_cube_pool = DRW_texture_create_2D_array(
 +                      linfo->shadow_cube_store_size, linfo->shadow_cube_store_size, max_ii(1, linfo->num_cube_layer),
 +                      shadow_pool_format, DRW_TEX_FILTER, NULL);
 +      }
 +      GPU_framebuffer_ensure_config(&sldata->shadow_cube_target_fb, {
 +              GPU_ATTACHMENT_TEXTURE(sldata->shadow_cube_target)
 +      });
 +      GPU_framebuffer_ensure_config(&sldata->shadow_cube_store_fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_TEXTURE(sldata->shadow_cube_pool)
 +      });
 +
 +      /* CSM */
 +      if (!sldata->shadow_cascade_target) {
 +              sldata->shadow_cascade_target = DRW_texture_create_2D_array(
 +                      linfo->shadow_cascade_size, linfo->shadow_cascade_size, MAX_CASCADE_NUM, GPU_DEPTH_COMPONENT24, 0, NULL);
 +              sldata->shadow_cascade_blur = DRW_texture_create_2D_array(
 +                      linfo->shadow_cascade_size, linfo->shadow_cascade_size, MAX_CASCADE_NUM, shadow_pool_format, DRW_TEX_FILTER, NULL);
 +      }
 +      if (!sldata->shadow_cascade_pool) {
 +              sldata->shadow_cascade_pool = DRW_texture_create_2D_array(
 +                      linfo->shadow_cascade_size, linfo->shadow_cascade_size, max_ii(1, linfo->num_cascade_layer),
 +                      shadow_pool_format, DRW_TEX_FILTER, NULL);
 +      }
 +      GPU_framebuffer_ensure_config(&sldata->shadow_cascade_target_fb, {
 +              GPU_ATTACHMENT_TEXTURE(sldata->shadow_cascade_target)
 +      });
 +      GPU_framebuffer_ensure_config(&sldata->shadow_cascade_store_fb, {
 +              GPU_ATTACHMENT_NONE,
 +              GPU_ATTACHMENT_TEXTURE(sldata->shadow_cascade_pool)
 +      });
 +
 +      /* Update Lamps UBOs. */
 +      EEVEE_lights_update(sldata);
 +}
 +
 +/* Update buffer with lamp data */
 +static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
 +{
 +      Lamp *la = (Lamp *)ob->data;
 +      float mat[4][4], scale[3], power;
 +
 +      /* Position */
 +      copy_v3_v3(evli->position, ob->obmat[3]);
 +
 +      /* Color */
 +      copy_v3_v3(evli->color, &la->r);
 +
 +      evli->spec = la->spec_fac;
 +
 +      /* Influence Radius */
 +      evli->dist = la->dist;
 +
 +      /* Vectors */
 +      normalize_m4_m4_ex(mat, ob->obmat, scale);
 +      copy_v3_v3(evli->forwardvec, mat[2]);
 +      normalize_v3(evli->forwardvec);
 +      negate_v3(evli->forwardvec);
 +
 +      copy_v3_v3(evli->rightvec, mat[0]);
 +      normalize_v3(evli->rightvec);
 +
 +      copy_v3_v3(evli->upvec, mat[1]);
 +      normalize_v3(evli->upvec);
 +
 +      /* Spot size & blend */
 +      if (la->type == LA_SPOT) {
 +              evli->sizex = scale[0] / scale[2];
 +              evli->sizey = scale[1] / scale[2];
 +              evli->spotsize = cosf(la->spotsize * 0.5f);
 +              evli->spotblend = (1.0f - evli->spotsize) * la->spotblend;
 +              evli->radius = max_ff(0.001f, la->area_size);
 +      }
 +      else if (la->type == LA_AREA) {
 +              evli->sizex = max_ff(0.0001f, la->area_size * scale[0] * 0.5f);
 +              if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) {
 +                      evli->sizey = max_ff(0.0001f, la->area_sizey * scale[1] * 0.5f);
 +              }
 +              else {
 +                      evli->sizey = max_ff(0.0001f, la->area_size * scale[1] * 0.5f);
 +              }
 +      }
 +      else {
 +              evli->radius = max_ff(0.001f, la->area_size);
 +      }
 +
 +      /* Lamp Type */
 +      evli->lamptype = (float)la->type;
 +
 +      /* Make illumination power constant */
 +      if (la->type == LA_AREA) {
 +              power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */
 +                      80.0f; /* XXX : Empirical, Fit cycles power */
 +              if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
 +                      evli->lamptype = LAMPTYPE_AREA_ELLIPSE;
-                * outweight the instancing overhead. which is rarelly the case. */
++                      /* Scale power to account for the lower area of the ellipse compared to the surrounding rectangle. */
 +                      power *= 4.0f / M_PI;
 +              }
 +      }
 +      else if (la->type == LA_SPOT || la->type == LA_LOCAL) {
 +              power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI) * /* 1/(4*r²*Pi²) */
 +                      M_PI * M_PI * 10.0; /* XXX : Empirical, Fit cycles power */
 +
 +              /* for point lights (a.k.a radius == 0.0) */
 +              // power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */
 +      }
 +      else {
 +              power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI) * /* 1/(r²*Pi) */
 +                      12.5f; /* XXX : Empirical, Fit cycles power */
 +      }
 +      mul_v3_fl(evli->color, power * la->energy);
 +
 +      /* No shadow by default */
 +      evli->shadowid = -1.0f;
 +}
 +
 +static void eevee_shadow_cube_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_LampEngineData *led)
 +{
 +      EEVEE_ShadowCubeData *sh_data = &led->data.scd;
 +      EEVEE_Light *evli = linfo->light_data + sh_data->light_id;
 +      EEVEE_Shadow *ubo_data = linfo->shadow_data + sh_data->shadow_id;
 +      EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + sh_data->cube_id;
 +      Lamp *la = (Lamp *)ob->data;
 +
 +      int sh_nbr = 1; /* TODO: MSM */
 +
 +      for (int i = 0; i < sh_nbr; ++i) {
 +              /* TODO : choose MSM sample point here. */
 +              copy_v3_v3(cube_data->position, ob->obmat[3]);
 +      }
 +
 +      ubo_data->bias = 0.05f * la->bias;
 +      ubo_data->near = la->clipsta;
 +      ubo_data->far = la->clipend;
 +      ubo_data->exp = (linfo->shadow_method == SHADOW_VSM) ? la->bleedbias : la->bleedexp;
 +
 +      evli->shadowid = (float)(sh_data->shadow_id);
 +      ubo_data->shadow_start = (float)(sh_data->layer_id);
 +      ubo_data->data_start = (float)(sh_data->cube_id);
 +      ubo_data->multi_shadow_count = (float)(sh_nbr);
 +      ubo_data->shadow_blur = la->soft * 0.02f; /* Used by translucence shadowmap blur */
 +
 +      ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
 +      ubo_data->contact_bias = 0.05f * la->contact_bias;
 +      ubo_data->contact_spread = la->contact_spread;
 +      ubo_data->contact_thickness = la->contact_thickness;
 +}
 +
 +#define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
 +
 +static double round_to_digits(double value, int digits)
 +{
 +      double factor = pow(10.0, digits - ceil(log10(fabs(value))));
 +      return round(value * factor) / factor;
 +}
 +
 +static void frustum_min_bounding_sphere(const float corners[8][3], float r_center[3], float *r_radius)
 +{
 +#if 0 /* Simple solution but waste too much space. */
 +      float minvec[3], maxvec[3];
 +
 +      /* compute the bounding box */
 +      INIT_MINMAX(minvec, maxvec);
 +      for (int i = 0; i < 8; ++i) {
 +              minmax_v3v3_v3(minvec, maxvec, corners[i]);
 +      }
 +
 +      /* compute the bounding sphere of this box */
 +      r_radius = len_v3v3(minvec, maxvec) * 0.5f;
 +      add_v3_v3v3(r_center, minvec, maxvec);
 +      mul_v3_fl(r_center, 0.5f);
 +#else
 +      /* Find averaged center. */
 +      zero_v3(r_center);
 +      for (int i = 0; i < 8; ++i) {
 +              add_v3_v3(r_center, corners[i]);
 +      }
 +      mul_v3_fl(r_center, 1.0f / 8.0f);
 +
 +      /* Search the largest distance from the sphere center. */
 +      *r_radius = 0.0f;
 +      for (int i = 0; i < 8; ++i) {
 +              float rad = len_squared_v3v3(corners[i], r_center);
 +              if (rad > *r_radius) {
 +                      *r_radius = rad;
 +              }
 +      }
 +
 +      /* TODO try to reduce the radius further by moving the center.
 +       * Remember we need a __stable__ solution! */
 +
 +      /* Try to reduce float imprecision leading to shimmering. */
 +      *r_radius = (float)round_to_digits(sqrtf(*r_radius), 3);
 +#endif
 +}
 +
 +static void eevee_shadow_cascade_setup(
 +        Object *ob, EEVEE_LampsInfo *linfo, EEVEE_LampEngineData *led,
 +        DRWMatrixState *saved_mats, float view_near, float view_far)
 +{
 +      Lamp *la = (Lamp *)ob->data;
 +
 +      /* Camera Matrices */
 +      float (*persinv)[4] = saved_mats->mat[DRW_MAT_PERSINV];
 +      float (*vp_projmat)[4] = saved_mats->mat[DRW_MAT_WIN];
 +      bool is_persp = DRW_viewport_is_persp_get();
 +
 +      /* Lamps Matrices */
 +      int sh_nbr = 1; /* TODO : MSM */
 +      int cascade_nbr = la->cascade_count;
 +
 +      EEVEE_ShadowCascadeData *sh_data = &led->data.scad;
 +      EEVEE_Light *evli = linfo->light_data + sh_data->light_id;
 +      EEVEE_Shadow *ubo_data = linfo->shadow_data + sh_data->shadow_id;
 +      EEVEE_ShadowCascade *cascade_data = linfo->shadow_cascade_data + sh_data->cascade_id;
 +
 +      /* obmat = Object Space > World Space */
 +      /* viewmat = World Space > View Space */
 +      float (*viewmat)[4] = sh_data->viewmat;
 +#if 0 /* done at culling time */
 +      normalize_m4_m4(viewmat, ob->obmat);
 +#endif
 +      invert_m4(viewmat);
 +      invert_m4_m4(sh_data->viewinv, viewmat);
 +
 +      /* The technique consists into splitting
 +       * the view frustum into several sub-frustum
 +       * that are individually receiving one shadow map */
 +
 +      float csm_start, csm_end;
 +
 +      if (is_persp) {
 +              csm_start = view_near;
 +              csm_end = max_ff(view_far, -la->cascade_max_dist);
 +              /* Avoid artifacts */
 +              csm_end = min_ff(view_near, csm_end);
 +      }
 +      else {
 +              csm_start = -view_far;
 +              csm_end = view_far;
 +      }
 +
 +      /* init near/far */
 +      for (int c = 0; c < MAX_CASCADE_NUM; ++c) {
 +              cascade_data->split_start[c] = csm_end;
 +              cascade_data->split_end[c] = csm_end;
 +      }
 +
 +      /* Compute split planes */
 +      float splits_start_ndc[MAX_CASCADE_NUM];
 +      float splits_end_ndc[MAX_CASCADE_NUM];
 +
 +      {
 +              /* Nearest plane */
 +              float p[4] = {1.0f, 1.0f, csm_start, 1.0f};
 +              /* TODO: we don't need full m4 multiply here */
 +              mul_m4_v4(vp_projmat, p);
 +              splits_start_ndc[0] = p[2];
 +              if (is_persp) {
 +                      splits_start_ndc[0] /= p[3];
 +              }
 +      }
 +
 +      {
 +              /* Farthest plane */
 +              float p[4] = {1.0f, 1.0f, csm_end, 1.0f};
 +              /* TODO: we don't need full m4 multiply here */
 +              mul_m4_v4(vp_projmat, p);
 +              splits_end_ndc[cascade_nbr - 1] = p[2];
 +              if (is_persp) {
 +                      splits_end_ndc[cascade_nbr - 1] /= p[3];
 +              }
 +      }
 +
 +      cascade_data->split_start[0] = csm_start;
 +      cascade_data->split_end[cascade_nbr - 1] = csm_end;
 +
 +      for (int c = 1; c < cascade_nbr; ++c) {
 +              /* View Space */
 +              float linear_split = LERP(((float)(c) / (float)cascade_nbr), csm_start, csm_end);
 +              float exp_split = csm_start * powf(csm_end / csm_start, (float)(c) / (float)cascade_nbr);
 +
 +              if (is_persp) {
 +                      cascade_data->split_start[c] = LERP(la->cascade_exponent, linear_split, exp_split);
 +              }
 +              else {
 +                      cascade_data->split_start[c] = linear_split;
 +              }
 +              cascade_data->split_end[c - 1] = cascade_data->split_start[c];
 +
 +              /* Add some overlap for smooth transition */
 +              cascade_data->split_start[c] = LERP(la->cascade_fade, cascade_data->split_end[c - 1],
 +                                                  (c > 1) ? cascade_data->split_end[c - 2] : cascade_data->split_start[0]);
 +
 +              /* NDC Space */
 +              {
 +                      float p[4] = {1.0f, 1.0f, cascade_data->split_start[c], 1.0f};
 +                      /* TODO: we don't need full m4 multiply here */
 +                      mul_m4_v4(vp_projmat, p);
 +                      splits_start_ndc[c] = p[2];
 +
 +                      if (is_persp) {
 +                              splits_start_ndc[c] /= p[3];
 +                      }
 +              }
 +
 +              {
 +                      float p[4] = {1.0f, 1.0f, cascade_data->split_end[c - 1], 1.0f};
 +                      /* TODO: we don't need full m4 multiply here */
 +                      mul_m4_v4(vp_projmat, p);
 +                      splits_end_ndc[c - 1] = p[2];
 +
 +                      if (is_persp) {
 +                              splits_end_ndc[c - 1] /= p[3];
 +                      }
 +              }
 +      }
 +
 +      /* Set last cascade split fade distance into the first split_start. */
 +      float prev_split = (cascade_nbr > 1) ? cascade_data->split_end[cascade_nbr - 2] : cascade_data->split_start[0];
 +      cascade_data->split_start[0] = LERP(la->cascade_fade, cascade_data->split_end[cascade_nbr - 1], prev_split);
 +
 +      /* For each cascade */
 +      for (int c = 0; c < cascade_nbr; ++c) {
 +              float (*projmat)[4] = sh_data->projmat[c];
 +              /* Given 8 frustum corners */
 +              float corners[8][3] = {
 +                      /* Near Cap */
 +                      { 1.0f, -1.0f, splits_start_ndc[c]},
 +                      {-1.0f, -1.0f, splits_start_ndc[c]},
 +                      {-1.0f,  1.0f, splits_start_ndc[c]},
 +                      { 1.0f,  1.0f, splits_start_ndc[c]},
 +                      /* Far Cap */
 +                      { 1.0f, -1.0f, splits_end_ndc[c]},
 +                      {-1.0f, -1.0f, splits_end_ndc[c]},
 +                      {-1.0f,  1.0f, splits_end_ndc[c]},
 +                      { 1.0f,  1.0f, splits_end_ndc[c]}
 +              };
 +
 +              /* Transform them into world space */
 +              for (int i = 0; i < 8; ++i) {
 +                      mul_project_m4_v3(persinv, corners[i]);
 +              }
 +
 +              float center[3];
 +              frustum_min_bounding_sphere(corners, center, &(sh_data->radius[c]));
 +
 +#ifdef DEBUG_CSM
 +              float dbg_col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 +              if (c < 3) {
 +                      dbg_col[c] = 1.0f;
 +              }
 +              DRW_debug_bbox((BoundBox *)&corners, dbg_col);
 +              DRW_debug_sphere(center, sh_data->radius[c], dbg_col);
 +#endif
 +
 +              /* Project into lightspace */
 +              mul_m4_v3(viewmat, center);
 +
 +              /* Snap projection center to nearest texel to cancel shimmering. */
 +              float shadow_origin[2], shadow_texco[2];
 +              /* Light to texture space. */
 +              mul_v2_v2fl(shadow_origin, center, linfo->shadow_cascade_size / (2.0f * sh_data->radius[c]));
 +
 +              /* Find the nearest texel. */
 +              shadow_texco[0] = roundf(shadow_origin[0]);
 +              shadow_texco[1] = roundf(shadow_origin[1]);
 +
 +              /* Compute offset. */
 +              sub_v2_v2(shadow_texco, shadow_origin);
 +              mul_v2_fl(shadow_texco, (2.0f * sh_data->radius[c]) / linfo->shadow_cascade_size); /* Texture to light space. */
 +
 +              /* Apply offset. */
 +              add_v2_v2(center, shadow_texco);
 +
 +              /* Expand the projection to cover frustum range */
 +              rctf rect_cascade;
 +              BLI_rctf_init_pt_radius(&rect_cascade, center, sh_data->radius[c]);
 +              orthographic_m4(projmat,
 +                              rect_cascade.xmin, rect_cascade.xmax,
 +                              rect_cascade.ymin, rect_cascade.ymax,
 +                              la->clipsta, la->clipend);
 +
 +              mul_m4_m4m4(sh_data->viewprojmat[c], projmat, viewmat);
 +              mul_m4_m4m4(cascade_data->shadowmat[c], texcomat, sh_data->viewprojmat[c]);
 +
 +#ifdef DEBUG_CSM
 +              DRW_debug_m4_as_bbox(sh_data->viewprojmat[c], dbg_col, true);
 +#endif
 +      }
 +
 +      ubo_data->bias = 0.05f * la->bias;
 +      ubo_data->near = la->clipsta;
 +      ubo_data->far = la->clipend;
 +      ubo_data->exp = (linfo->shadow_method == SHADOW_VSM) ? la->bleedbias : la->bleedexp;
 +
 +      evli->shadowid = (float)(sh_data->shadow_id);
 +      ubo_data->shadow_start = (float)(sh_data->layer_id);
 +      ubo_data->data_start = (float)(sh_data->cascade_id);
 +      ubo_data->multi_shadow_count = (float)(sh_nbr);
 +      ubo_data->shadow_blur = la->soft * 0.02f; /* Used by translucence shadowmap blur */
 +
 +      ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
 +      ubo_data->contact_bias = 0.05f * la->contact_bias;
 +      ubo_data->contact_spread = la->contact_spread;
 +      ubo_data->contact_thickness = la->contact_thickness;
 +}
 +
 +/* Used for checking if object is inside the shadow volume. */
 +static bool sphere_bbox_intersect(const EEVEE_BoundSphere *bs, const EEVEE_BoundBox *bb)
 +{
 +      /* We are testing using a rougher AABB vs AABB test instead of full AABB vs Sphere. */
 +      /* TODO test speed with AABB vs Sphere. */
 +      bool x = fabsf(bb->center[0] - bs->center[0]) <= (bb->halfdim[0] + bs->radius);
 +      bool y = fabsf(bb->center[1] - bs->center[1]) <= (bb->halfdim[1] + bs->radius);
 +      bool z = fabsf(bb->center[2] - bs->center[2]) <= (bb->halfdim[2] + bs->radius);
 +
 +      return x && y && z;
 +}
 +
 +void EEVEE_lights_update(EEVEE_ViewLayerData *sldata)
 +{
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      Object *ob;
 +      int i;
 +      char *flag;
 +      EEVEE_ShadowCaster *shcaster;
 +      EEVEE_BoundSphere *bsphere;
 +      EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
 +      EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
 +
 +      EEVEE_LightBits update_bits = {{0}};
 +      if ((linfo->update_flag & LIGHT_UPDATE_SHADOW_CUBE) != 0) {
 +              /* Update all lights. */
 +              lightbits_set_all(&update_bits, true);
 +      }
 +      else {
 +              /* Search for deleted shadow casters and if shcaster WAS in shadow radius. */
 +              /* No need to run this if we already update all lamps. */
 +              EEVEE_LightBits past_bits = {{0}};
 +              EEVEE_LightBits curr_bits = {{0}};
 +              shcaster = backbuffer->shadow_casters;
 +              flag = backbuffer->flags;
 +              for (i = 0; i < backbuffer->count; ++i, ++flag, ++shcaster) {
 +                      /* If the shadowcaster has been deleted or updated. */
 +                      if (*flag != 0) {
 +                              /* Add the lamps that were intersecting with its BBox. */
 +                              lightbits_or(&past_bits, &shcaster->bits);
 +                      }
 +              }
 +              /* Convert old bits to new bits and add result to final update bits. */
 +              /* NOTE: This might be overkill since all lights are tagged to refresh if
 +               * the light count changes. */
 +              lightbits_convert(&curr_bits, &past_bits, linfo->new_shadow_id, MAX_LIGHT);
 +              lightbits_or(&update_bits, &curr_bits);
 +      }
 +
 +      /* Search for updates in current shadow casters. */
 +      shcaster = frontbuffer->shadow_casters;
 +      flag = frontbuffer->flags;
 +      for (i = 0; i < frontbuffer->count; i++, flag++, shcaster++) {
 +              /* Run intersection checks to fill the bitfields. */
 +              bsphere = linfo->shadow_bounds;
 +              for (int j = 0; j < linfo->cpu_cube_len; j++, bsphere++) {
 +                      bool iter = sphere_bbox_intersect(bsphere, &shcaster->bbox);
 +                      lightbits_set_single(&shcaster->bits, j, iter);
 +              }
 +              /* Only add to final bits if objects has been updated. */
 +              if (*flag != 0) {
 +                      lightbits_or(&update_bits, &shcaster->bits);
 +              }
 +      }
 +
 +      /* Setup shadow cube in UBO and tag for update if necessary. */
 +      for (i = 0; (i < MAX_SHADOW_CUBE) && (ob = linfo->shadow_cube_ref[i]); i++) {
 +              EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
 +
 +              eevee_shadow_cube_setup(ob, linfo, led);
 +              if (lightbits_get(&update_bits, i) != 0) {
 +                      led->need_update = true;
 +              }
 +      }
 +
 +      /* Resize shcasters buffers if too big. */
 +      if (frontbuffer->alloc_count - frontbuffer->count > SHADOW_CASTER_ALLOC_CHUNK) {
 +              frontbuffer->alloc_count  = (frontbuffer->count / SHADOW_CASTER_ALLOC_CHUNK) * SHADOW_CASTER_ALLOC_CHUNK;
 +              frontbuffer->alloc_count += (frontbuffer->count % SHADOW_CASTER_ALLOC_CHUNK != 0) ? SHADOW_CASTER_ALLOC_CHUNK : 0;
 +              frontbuffer->shadow_casters = MEM_reallocN(frontbuffer->shadow_casters, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
 +              frontbuffer->flags = MEM_reallocN(frontbuffer->flags, sizeof(EEVEE_ShadowCaster) * frontbuffer->alloc_count);
 +      }
 +}
 +
 +/* this refresh lamps shadow buffers */
 +void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 +{
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      Object *ob;
 +      int i;
 +
 +      DRWMatrixState saved_mats;
 +
 +      /* Precompute all shadow/view test before rendering and trashing the culling cache. */
 +      bool cube_visible[MAX_SHADOW_CUBE];
 +      for (i = 0; (ob = linfo->shadow_cube_ref[i]) && (i < MAX_SHADOW_CUBE); i++) {
 +              Lamp *la = (Lamp *)ob->data;
 +              BoundSphere bsphere = {
 +                      .center = {ob->obmat[3][0], ob->obmat[3][1], ob->obmat[3][2]},
 +                      .radius = la->dist
 +              };
 +              cube_visible[i] = DRW_culling_sphere_test(&bsphere);
 +      }
 +      bool cascade_visible[MAX_SHADOW_CASCADE];
 +      for (i = 0; (ob = linfo->shadow_cascade_ref[i]) && (i < MAX_SHADOW_CASCADE); i++) {
 +              EEVEE_LampEngineData *led = EEVEE_lamp_data_get(ob);
 +              EEVEE_ShadowCascadeData *sh_data = &led->data.scad;
 +              float plane[4];
 +              normalize_m4_m4(sh_data->viewmat, ob->obmat);
 +              plane_from_point_normal_v3(plane, sh_data->viewmat[3], sh_data->viewmat[2]);
 +              /* TODO: check against near/far instead of "local Z = 0" plane.
 +               * Or even the cascades AABB. */
 +              cascade_visible[i] = DRW_culling_plane_test(plane);
 +      }
 +
 +      /* We need to save the Matrices before overidding them */
 +      DRW_viewport_matrix_get_all(&saved_mats);
 +
 +      /* Cube Shadow Maps */
 +      DRW_stats_group_start("Cube Shadow Maps");
 +      /* Render each shadow to one layer of the array */
 +      for (i = 0; (ob = linfo->shadow_cube_ref[i]) && (i < MAX_SHADOW_CUBE); i++) {
 +              EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
 +              Lamp *la = (Lamp *)ob->data;
 +
 +              if (!led->need_update || !cube_visible[i]) {
 +                      continue;
 +              }
 +
 +              DRWMatrixState render_mats;
 +              float (*winmat)[4] = render_mats.mat[DRW_MAT_WIN];
 +              float (*viewmat)[4] = render_mats.mat[DRW_MAT_VIEW];
 +              float (*persmat)[4] = render_mats.mat[DRW_MAT_PERS];
 +
 +              EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
 +              EEVEE_ShadowCubeData *evscd = &led->data.scd;
 +
 +              perspective_m4(winmat, -la->clipsta, la->clipsta, -la->clipsta, la->clipsta, la->clipsta, la->clipend);
 +
 +              srd->clip_near = la->clipsta;
 +              srd->clip_far = la->clipend;
 +              copy_v3_v3(srd->position, ob->obmat[3]);
 +
 +              srd->stored_texel_size = 1.0 / (float)linfo->shadow_cube_store_size;
 +
 +              DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
 +
 +              /* Render shadow cube */
 +              /* Render 6 faces separately: seems to be faster for the general case.
 +               * The only time it's more beneficial is when the CPU culling overhead
-                * outweight the instancing overhead. which is rarelly the case. */
++               * outweigh the instancing overhead. which is rarely the case. */
 +              for (int j = 0; j < 6; j++) {
 +                      /* TODO optimize */
 +                      float tmp[4][4];
 +                      unit_m4(tmp);
 +                      negate_v3_v3(tmp[3], srd->position);
 +                      mul_m4_m4m4(viewmat, cubefacemat[j], tmp);
 +                      mul_m4_m4m4(persmat, winmat, viewmat);
 +                      invert_m4_m4(render_mats.mat[DRW_MAT_WININV], winmat);
 +                      invert_m4_m4(render_mats.mat[DRW_MAT_VIEWINV], viewmat);
 +                      invert_m4_m4(render_mats.mat[DRW_MAT_PERSINV], persmat);
 +
 +                      DRW_viewport_matrix_override_set_all(&render_mats);
 +
 +                      GPU_framebuffer_texture_cubeface_attach(sldata->shadow_cube_target_fb,
 +                                                              sldata->shadow_cube_target, 0, j, 0);
 +                      GPU_framebuffer_bind(sldata->shadow_cube_target_fb);
 +                      GPU_framebuffer_clear_depth(sldata->shadow_cube_target_fb, 1.0f);
 +                      DRW_draw_pass(psl->shadow_pass);
 +              }
 +
 +              /* 0.001f is arbitrary, but it should be relatively small so that filter size is not too big. */
 +              float filter_texture_size = la->soft * 0.001f;
 +              float filter_pixel_size = ceil(filter_texture_size / srd->cube_texel_size);
 +              linfo->filter_size = srd->cube_texel_size * ((filter_pixel_size > 1.0f) ? 1.5f : 0.0f);
 +
 +              /* TODO: OPTI: Filter all faces in one/two draw call */
 +              /* TODO: OPTI: Don't do this intermediate step if no filter is needed. */
 +              for (linfo->current_shadow_face = 0;
 +                   linfo->current_shadow_face < 6;
 +                   linfo->current_shadow_face++)
 +              {
 +                      /* Copy using a small 3x3 box filter */
 +                      GPU_framebuffer_texture_cubeface_attach(sldata->shadow_cube_store_fb, sldata->shadow_cube_blur, 0,
 +                                                              linfo->current_shadow_face, 0);
 +                      GPU_framebuffer_bind(sldata->shadow_cube_store_fb);
 +                      DRW_draw_pass(psl->shadow_cube_copy_pass);
 +              }
 +
 +              /* Push it to shadowmap array */
 +
 +              /* Adjust constants if concentric samples change. */
 +              const float max_filter_size = 7.5f;
 +              const float magic = 4.5f; /* Dunno why but that works. */
 +              const int max_sample = 256;
 +
 +              if (filter_pixel_size > 2.0f) {
 +                      linfo->filter_size = srd->cube_texel_size * max_filter_size * magic;
 +                      filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
 +                      /* Compute number of concentric samples. Depends directly on filter size. */
 +                      float pix_size_sqr = filter_pixel_size * filter_pixel_size;
 +                      srd->shadow_samples_len = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
 +              }
 +              else {
 +                      linfo->filter_size = 0.0f;
 +                      srd->shadow_samples_len = 4;
 +              }
 +              srd->shadow_samples_len_inv = 1.0f / (float)srd->shadow_samples_len;
 +              DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
 +
 +              GPU_framebuffer_texture_layer_attach(sldata->shadow_cube_store_fb, sldata->shadow_cube_pool, 0, evscd->layer_id, 0);
 +              GPU_framebuffer_bind(sldata->shadow_cube_store_fb);
 +
 +              DRWPass *store_pass = eevee_lights_cube_store_pass_get(psl, sldata, linfo->shadow_method, srd->shadow_samples_len);
 +              DRW_draw_pass(store_pass);
 +
 +              led->need_update = false;
 +      }
 +      linfo->update_flag &= ~LIGHT_UPDATE_SHADOW_CUBE;
 +      DRW_stats_group_end();
 +
 +      DRW_viewport_matrix_override_set_all(&saved_mats);
 +      float near = DRW_viewport_near_distance_get();
 +      float far = DRW_viewport_far_distance_get();
 +
 +      /* Cascaded Shadow Maps */
 +      DRW_stats_group_start("Cascaded Shadow Maps");
 +      for (i = 0; (ob = linfo->shadow_cascade_ref[i]) && (i < MAX_SHADOW_CASCADE); i++) {
 +              if (!cascade_visible[i]) {
 +                      continue;
 +              }
 +
 +              EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
 +              Lamp *la = (Lamp *)ob->data;
 +
 +              EEVEE_ShadowCascadeData *evscd = &led->data.scad;
 +              EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
 +
 +              DRWMatrixState render_mats;
 +              float (*winmat)[4] = render_mats.mat[DRW_MAT_WIN];
 +              float (*viewmat)[4] = render_mats.mat[DRW_MAT_VIEW];
 +              float (*persmat)[4] = render_mats.mat[DRW_MAT_PERS];
 +
 +              eevee_shadow_cascade_setup(ob, linfo, led, &saved_mats, near, far);
 +
 +              srd->clip_near = la->clipsta;
 +              srd->clip_far = la->clipend;
 +              srd->stored_texel_size = 1.0 / (float)linfo->shadow_cascade_size;
 +
 +              DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
 +
 +              copy_m4_m4(viewmat, evscd->viewmat);
 +              invert_m4_m4(render_mats.mat[DRW_MAT_VIEWINV], viewmat);
 +
 +              /* Render shadow cascades */
 +              /* Render cascade separately: seems to be faster for the general case.
 +               * The only time it's more beneficial is when the CPU culling overhead
++               * outweigh the instancing overhead. which is rarely the case. */
 +              for (int j = 0; j < la->cascade_count; j++) {
 +                      copy_m4_m4(winmat, evscd->projmat[j]);
 +                      copy_m4_m4(persmat, evscd->viewprojmat[j]);
 +                      invert_m4_m4(render_mats.mat[DRW_MAT_WININV], winmat);
 +                      invert_m4_m4(render_mats.mat[DRW_MAT_PERSINV], persmat);
 +
 +                      DRW_viewport_matrix_override_set_all(&render_mats);
 +
 +                      GPU_framebuffer_texture_layer_attach(sldata->shadow_cascade_target_fb,
 +                                                           sldata->shadow_cascade_target, 0, j, 0);
 +                      GPU_framebuffer_bind(sldata->shadow_cascade_target_fb);
 +                      GPU_framebuffer_clear_depth(sldata->shadow_cascade_target_fb, 1.0f);
 +                      DRW_draw_pass(psl->shadow_pass);
 +              }
 +
 +              /* TODO: OPTI: Filter all cascade in one/two draw call */
 +              for (linfo->current_shadow_cascade = 0;
 +                   linfo->current_shadow_cascade < la->cascade_count;
 +                   ++linfo->current_shadow_cascade)
 +              {
 +                      /* 0.01f factor to convert to percentage */
 +                      float filter_texture_size = la->soft * 0.01f / evscd->radius[linfo->current_shadow_cascade];
 +                      float filter_pixel_size = ceil(linfo->shadow_cascade_size * filter_texture_size);
 +
 +                      /* Copy using a small 3x3 box filter */
 +                      /* NOTE: We always do it in the case of CSM because of artifacts in the farthest cascade. */
 +                      linfo->filter_size = srd->stored_texel_size;
 +                      GPU_framebuffer_texture_layer_attach(
 +                              sldata->shadow_cascade_store_fb, sldata->shadow_cascade_blur, 0, linfo->current_shadow_cascade, 0);
 +                      GPU_framebuffer_bind(sldata->shadow_cascade_store_fb);
 +                      DRW_draw_pass(psl->shadow_cascade_copy_pass);
 +
 +                      /* Push it to shadowmap array and blur more */
 +
 +                      /* Adjust constants if concentric samples change. */
 +                      const float max_filter_size = 7.5f;
 +                      const float magic = 3.2f; /* Arbitrary: less banding */
 +                      const int max_sample = 256;
 +
 +                      if (filter_pixel_size > 2.0f) {
 +                              linfo->filter_size = srd->stored_texel_size * max_filter_size * magic;
 +                              filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
 +                              /* Compute number of concentric samples. Depends directly on filter size. */
 +                              float pix_size_sqr = filter_pixel_size * filter_pixel_size;
 +                              srd->shadow_samples_len = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
 +                      }
 +                      else {
 +                              linfo->filter_size = 0.0f;
 +                              srd->shadow_samples_len = 4;
 +                      }
 +                      srd->shadow_samples_len_inv = 1.0f / (float)srd->shadow_samples_len;
 +                      DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
 +
 +                      int layer = evscd->layer_id + linfo->current_shadow_cascade;
 +                      GPU_framebuffer_texture_layer_attach(sldata->shadow_cascade_store_fb, sldata->shadow_cascade_pool, 0, layer, 0);
 +                      GPU_framebuffer_bind(sldata->shadow_cascade_store_fb);
 +
 +                      DRWPass *store_pass = eevee_lights_cascade_store_pass_get(psl, sldata, linfo->shadow_method, srd->shadow_samples_len);
 +                      DRW_draw_pass(store_pass);
 +              }
 +      }
 +
 +      DRW_stats_group_end();
 +
 +      DRW_viewport_matrix_override_set_all(&saved_mats);
 +
 +      DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data);
 +      DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
 +}
 +
 +void EEVEE_lights_free(void)
 +{
 +      DRW_SHADER_FREE_SAFE(e_data.shadow_sh);
 +      for (int i = 0; i < SHADOW_METHOD_MAX; ++i) {
 +              DRW_SHADER_FREE_SAFE(e_data.shadow_store_cube_sh[i]);
 +              DRW_SHADER_FREE_SAFE(e_data.shadow_store_cube_high_sh[i]);
 +              DRW_SHADER_FREE_SAFE(e_data.shadow_store_cascade_sh[i]);
 +              DRW_SHADER_FREE_SAFE(e_data.shadow_store_cascade_high_sh[i]);
 +              DRW_SHADER_FREE_SAFE(e_data.shadow_copy_cube_sh[i]);
 +              DRW_SHADER_FREE_SAFE(e_data.shadow_copy_cascade_sh[i]);
 +      }
 +}
index 6ec20b2,0000000..2d27bb8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1786 -1,0 +1,1786 @@@
-                                               /* TODO (fclem): remove thoses (need to clean the GLSL files). */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file eevee_materials.c
 + *  \ingroup draw_engine
 + */
 +
 +#include "DRW_render.h"
 +
 +#include "BLI_dynstr.h"
 +#include "BLI_ghash.h"
 +#include "BLI_alloca.h"
 +#include "BLI_rand.h"
 +#include "BLI_string_utils.h"
 +
 +#include "BKE_particle.h"
 +#include "BKE_paint.h"
 +#include "BKE_pbvh.h"
 +#include "BKE_studiolight.h"
 +
 +#include "DNA_world_types.h"
 +#include "DNA_modifier_types.h"
 +#include "DNA_view3d_types.h"
 +
 +#include "GPU_material.h"
 +
 +#include "eevee_engine.h"
 +#include "eevee_lut.h"
 +#include "eevee_private.h"
 +
 +/* *********** STATIC *********** */
 +static struct {
 +      char *frag_shader_lib;
 +      char *vert_shader_str;
 +      char *volume_shader_lib;
 +
 +      struct GPUShader *default_prepass_sh;
 +      struct GPUShader *default_prepass_clip_sh;
 +      struct GPUShader *default_hair_prepass_sh;
 +      struct GPUShader *default_hair_prepass_clip_sh;
 +      struct GPUShader *default_lit[VAR_MAT_MAX];
 +      struct GPUShader *default_background;
 +      struct GPUShader *default_studiolight_background;
 +      struct GPUShader *update_noise_sh;
 +
 +      /* 64*64 array texture containing all LUTs and other utilitarian arrays.
 +       * Packing enables us to same precious textures slots. */
 +      struct GPUTexture *util_tex;
 +      struct GPUTexture *noise_tex;
 +
 +      uint sss_count;
 +
 +      float alpha_hash_offset;
 +      float noise_offsets[3];
 +} e_data = {NULL}; /* Engine data */
 +
 +extern char datatoc_lamps_lib_glsl[];
 +extern char datatoc_lightprobe_lib_glsl[];
 +extern char datatoc_ambient_occlusion_lib_glsl[];
 +extern char datatoc_prepass_frag_glsl[];
 +extern char datatoc_prepass_vert_glsl[];
 +extern char datatoc_default_frag_glsl[];
 +extern char datatoc_default_world_frag_glsl[];
 +extern char datatoc_ltc_lib_glsl[];
 +extern char datatoc_bsdf_lut_frag_glsl[];
 +extern char datatoc_btdf_lut_frag_glsl[];
 +extern char datatoc_bsdf_common_lib_glsl[];
 +extern char datatoc_bsdf_sampling_lib_glsl[];
 +extern char datatoc_common_uniforms_lib_glsl[];
 +extern char datatoc_common_hair_lib_glsl[];
 +extern char datatoc_common_view_lib_glsl[];
 +extern char datatoc_irradiance_lib_glsl[];
 +extern char datatoc_octahedron_lib_glsl[];
 +extern char datatoc_lit_surface_frag_glsl[];
 +extern char datatoc_lit_surface_vert_glsl[];
 +extern char datatoc_raytrace_lib_glsl[];
 +extern char datatoc_ssr_lib_glsl[];
 +extern char datatoc_shadow_vert_glsl[];
 +extern char datatoc_lightprobe_geom_glsl[];
 +extern char datatoc_lightprobe_vert_glsl[];
 +extern char datatoc_background_vert_glsl[];
 +extern char datatoc_update_noise_frag_glsl[];
 +extern char datatoc_volumetric_vert_glsl[];
 +extern char datatoc_volumetric_geom_glsl[];
 +extern char datatoc_volumetric_frag_glsl[];
 +extern char datatoc_volumetric_lib_glsl[];
 +
 +extern char datatoc_gpu_shader_uniform_color_frag_glsl[];
 +
 +extern Material defmaterial;
 +extern GlobalsUboStorage ts;
 +
 +/* *********** FUNCTIONS *********** */
 +
 +#if 0 /* Used only to generate the LUT values */
 +static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h))
 +{
 +      struct GPUTexture *tex;
 +      struct GPUFrameBuffer *fb = NULL;
 +      static float samples_len = 8192.0f;
 +      static float inv_samples_len = 1.0f / 8192.0f;
 +
 +      char *lib_str = BLI_string_joinN(
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_bsdf_sampling_lib_glsl);
 +
 +      struct GPUShader *sh = DRW_shader_create_with_lib(
 +              datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, datatoc_bsdf_lut_frag_glsl, lib_str,
 +              "#define HAMMERSLEY_SIZE 8192\n"
 +              "#define BRDF_LUT_SIZE 64\n"
 +              "#define NOISE_SIZE 64\n");
 +
 +      DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
 +      DRWShadingGroup *grp = DRW_shgroup_create(sh, pass);
 +      DRW_shgroup_uniform_float(grp, "sampleCount", &samples_len, 1);
 +      DRW_shgroup_uniform_float(grp, "invSampleCount", &inv_samples_len, 1);
 +      DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
 +      DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter);
 +
 +      struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +      DRW_shgroup_call_add(grp, geom, NULL);
 +
 +      float *texels = MEM_mallocN(sizeof(float[2]) * w * h, "lut");
 +
 +      tex = DRW_texture_create_2D(w, h, GPU_RG16F, DRW_TEX_FILTER, (float *)texels);
 +
 +      DRWFboTexture tex_filter = {&tex, GPU_RG16F, DRW_TEX_FILTER};
 +      GPU_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
 +
 +      GPU_framebuffer_bind(fb);
 +      DRW_draw_pass(pass);
 +
 +      float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
 +      glReadBuffer(GL_COLOR_ATTACHMENT0);
 +      glReadPixels(0, 0, w, h, GL_RGB, GL_FLOAT, data);
 +
 +      printf("{");
 +      for (int i = 0; i < w*h * 3; i+=3) {
 +              printf("%ff, %ff, ", data[i],  data[i+1]); i+=3;
 +              printf("%ff, %ff, ", data[i],  data[i+1]); i+=3;
 +              printf("%ff, %ff, ", data[i],  data[i+1]); i+=3;
 +              printf("%ff, %ff, \n", data[i],  data[i+1]);
 +      }
 +      printf("}");
 +
 +      MEM_freeN(texels);
 +      MEM_freeN(data);
 +
 +      return tex;
 +}
 +
 +static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h)
 +{
 +      struct GPUTexture *tex;
 +      struct GPUTexture *hammersley = create_hammersley_sample_texture(8192);
 +      struct GPUFrameBuffer *fb = NULL;
 +      static float samples_len = 8192.0f;
 +      static float a2 = 0.0f;
 +      static float inv_samples_len = 1.0f / 8192.0f;
 +
 +      char *frag_str = BLI_string_joinN(
 +              datatoc_bsdf_common_lib_glsl,
 +              datatoc_bsdf_sampling_lib_glsl,
 +              datatoc_btdf_lut_frag_glsl);
 +
 +      struct GPUShader *sh = DRW_shader_create_fullscreen(frag_str,
 +              "#define HAMMERSLEY_SIZE 8192\n"
 +              "#define BRDF_LUT_SIZE 64\n"
 +              "#define NOISE_SIZE 64\n"
 +              "#define LUT_SIZE 64\n");
 +
 +      MEM_freeN(frag_str);
 +
 +      DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
 +      DRWShadingGroup *grp = DRW_shgroup_create(sh, pass);
 +      DRW_shgroup_uniform_float(grp, "a2", &a2, 1);
 +      DRW_shgroup_uniform_float(grp, "sampleCount", &samples_len, 1);
 +      DRW_shgroup_uniform_float(grp, "invSampleCount", &inv_samples_len, 1);
 +      DRW_shgroup_uniform_texture(grp, "texHammersley", hammersley);
 +      DRW_shgroup_uniform_texture(grp, "utilTex", e_data.util_tex);
 +
 +      struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +      DRW_shgroup_call_add(grp, geom, NULL);
 +
 +      float *texels = MEM_mallocN(sizeof(float[2]) * w * h, "lut");
 +
 +      tex = DRW_texture_create_2D(w, h, GPU_R16F, DRW_TEX_FILTER, (float *)texels);
 +
 +      DRWFboTexture tex_filter = {&tex, GPU_R16F, DRW_TEX_FILTER};
 +      GPU_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
 +
 +      GPU_framebuffer_bind(fb);
 +
 +      float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
 +
 +      float inc = 1.0f / 31.0f;
 +      float roughness = 1e-8f - inc;
 +      FILE *f = BLI_fopen("btdf_split_sum_ggx.h", "w");
 +      fprintf(f, "static float btdf_split_sum_ggx[32][64 * 64] = {\n");
 +      do {
 +              roughness += inc;
 +              CLAMP(roughness, 1e-4f, 1.0f);
 +              a2 = powf(roughness, 4.0f);
 +              DRW_draw_pass(pass);
 +
 +              GPU_framebuffer_read_data(0, 0, w, h, 3, 0, data);
 +
 +#if 1
 +              fprintf(f, "\t{\n\t\t");
 +              for (int i = 0; i < w*h * 3; i+=3) {
 +                      fprintf(f, "%ff,", data[i]);
 +                      if (((i/3)+1) % 12 == 0) fprintf(f, "\n\t\t");
 +                      else fprintf(f, " ");
 +              }
 +              fprintf(f, "\n\t},\n");
 +#else
 +              for (int i = 0; i < w*h * 3; i+=3) {
 +                      if (data[i] < 0.01) printf(" ");
 +                      else if (data[i] < 0.3) printf(".");
 +                      else if (data[i] < 0.6) printf("+");
 +                      else if (data[i] < 0.9) printf("%%");
 +                      else printf("#");
 +                      if ((i/3+1) % 64 == 0) printf("\n");
 +              }
 +#endif
 +
 +      } while (roughness < 1.0f);
 +      fprintf(f, "\n};\n");
 +
 +      fclose(f);
 +
 +      MEM_freeN(texels);
 +      MEM_freeN(data);
 +
 +      return tex;
 +}
 +#endif
 +/* XXX TODO define all shared resources in a shared place without duplication */
 +struct GPUTexture *EEVEE_materials_get_util_tex(void)
 +{
 +      return e_data.util_tex;
 +}
 +
 +static int eevee_material_shadow_option(int shadow_method)
 +{
 +      switch (shadow_method) {
 +              case SHADOW_ESM: return VAR_MAT_ESM;
 +              case SHADOW_VSM: return VAR_MAT_VSM;
 +              default:
 +                      BLI_assert(!"Incorrect Shadow Method");
 +                      break;
 +      }
 +
 +      return 0;
 +}
 +
 +static char *eevee_get_defines(int options)
 +{
 +      char *str = NULL;
 +
 +      DynStr *ds = BLI_dynstr_new();
 +      BLI_dynstr_appendf(ds, SHADER_DEFINES);
 +
 +      if ((options & VAR_MAT_MESH) != 0) {
 +              BLI_dynstr_appendf(ds, "#define MESH_SHADER\n");
 +      }
 +      if ((options & VAR_MAT_HAIR) != 0) {
 +              BLI_dynstr_appendf(ds, "#define HAIR_SHADER\n");
 +      }
 +      if ((options & VAR_MAT_PROBE) != 0) {
 +              BLI_dynstr_appendf(ds, "#define PROBE_CAPTURE\n");
 +      }
 +      if ((options & VAR_MAT_FLAT) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_FLAT_NORMAL\n");
 +      }
 +      if ((options & VAR_MAT_CLIP) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_ALPHA_CLIP\n");
 +      }
 +      if ((options & VAR_MAT_SHADOW) != 0) {
 +              BLI_dynstr_appendf(ds, "#define SHADOW_SHADER\n");
 +      }
 +      if ((options & VAR_MAT_HASH) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_ALPHA_HASH\n");
 +      }
 +      if ((options & VAR_MAT_BLEND) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_ALPHA_BLEND\n");
 +      }
 +      if ((options & VAR_MAT_MULT) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_MULTIPLY\n");
 +      }
 +      if ((options & VAR_MAT_REFRACT) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_REFRACTION\n");
 +      }
 +      if ((options & VAR_MAT_SSS) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_SSS\n");
 +      }
 +      if ((options & VAR_MAT_SSSALBED) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_SSS_ALBEDO\n");
 +      }
 +      if ((options & VAR_MAT_TRANSLUC) != 0) {
 +              BLI_dynstr_appendf(ds, "#define USE_TRANSLUCENCY\n");
 +      }
 +      if ((options & VAR_MAT_VSM) != 0) {
 +              BLI_dynstr_appendf(ds, "#define SHADOW_VSM\n");
 +      }
 +      if ((options & VAR_MAT_ESM) != 0) {
 +              BLI_dynstr_appendf(ds, "#define SHADOW_ESM\n");
 +      }
 +      if (((options & VAR_MAT_VOLUME) != 0) && ((options & VAR_MAT_BLEND) != 0)) {
 +              BLI_dynstr_appendf(ds, "#define USE_ALPHA_BLEND_VOLUMETRICS\n");
 +      }
 +      if ((options & VAR_MAT_LOOKDEV) != 0) {
 +              BLI_dynstr_appendf(ds, "#define LOOKDEV\n");
 +      }
 +
 +      str = BLI_dynstr_get_cstring(ds);
 +      BLI_dynstr_free(ds);
 +
 +      return str;
 +}
 +
 +static char *eevee_get_volume_defines(int options)
 +{
 +      char *str = NULL;
 +
 +      DynStr *ds = BLI_dynstr_new();
 +      BLI_dynstr_appendf(ds, SHADER_DEFINES);
 +      BLI_dynstr_appendf(ds, "#define VOLUMETRICS\n");
 +
 +      if ((options & VAR_MAT_VOLUME) != 0) {
 +              BLI_dynstr_appendf(ds, "#define MESH_SHADER\n");
 +      }
 +
 +      str = BLI_dynstr_get_cstring(ds);
 +      BLI_dynstr_free(ds);
 +
 +      return str;
 +}
 +
 +/**
 + * ssr_id can be null to disable ssr contribution.
 + **/
 +static void add_standard_uniforms(
 +        DRWShadingGroup *shgrp, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        int *ssr_id, float *refract_depth,
 +        bool use_diffuse, bool use_glossy, bool use_refract,
 +        bool use_ssrefraction, bool use_alpha_blend)
 +{
 +      LightCache *lcache = vedata->stl->g_data->light_cache;
 +
 +      if (ssr_id == NULL) {
 +              static int no_ssr = -1.0f;
 +              ssr_id = &no_ssr;
 +      }
 +
 +      DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo);
 +      DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo);
 +      DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo);
 +      DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo);
 +      DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo);
 +      DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo);
 +      DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
 +
 +      if (use_diffuse || use_glossy || use_refract) {
 +              DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex);
 +              DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool);
 +              DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
 +              DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
 +      }
 +      if ((use_diffuse || use_glossy) && !use_refract) {
 +              if ((vedata->stl->effects->enabled_effects & EFFECT_GTAO) != 0) {
 +                      DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &vedata->stl->effects->gtao_horizons);
 +              }
 +              else {
 +                      /* Use maxzbuffer as fallback to avoid sampling problem on certain platform, see: T52593 */
 +                      DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &vedata->txl->maxzbuffer);
 +              }
 +      }
 +      if (use_diffuse) {
 +              DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
 +      }
 +      if (use_glossy || use_refract) {
 +              DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex);
 +      }
 +      if (use_glossy) {
 +              DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool);
 +              DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1);
 +      }
 +      if (use_refract && use_ssrefraction) {
 +              BLI_assert(refract_depth != NULL);
 +              DRW_shgroup_uniform_float(shgrp, "refractionDepth", refract_depth, 1);
 +              DRW_shgroup_uniform_texture_ref(shgrp, "colorBuffer", &vedata->txl->refract_color);
 +      }
 +
 +      if ((vedata->stl->effects->enabled_effects & EFFECT_VOLUMETRIC) != 0 &&
 +           use_alpha_blend)
 +      {
 +              /* Do not use history buffers as they already have been swapped */
 +              DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &vedata->txl->volume_scatter);
 +              DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &vedata->txl->volume_transmittance);
 +      }
 +}
 +
 +static void create_default_shader(int options)
 +{
 +      char *frag_str = BLI_string_joinN(
 +              e_data.frag_shader_lib,
 +              datatoc_default_frag_glsl);
 +
 +      char *defines = eevee_get_defines(options);
 +
 +      e_data.default_lit[options] = DRW_shader_create(e_data.vert_shader_str, NULL, frag_str, defines);
 +
 +      MEM_freeN(defines);
 +      MEM_freeN(frag_str);
 +}
 +
 +static void eevee_init_noise_texture(void)
 +{
 +      e_data.noise_tex = DRW_texture_create_2D(64, 64, GPU_RGBA16F, 0, (float *)blue_noise);
 +}
 +
 +static void eevee_init_util_texture(void)
 +{
 +      const int layers = 3 + 16;
 +      float (*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels");
 +      float (*texels_layer)[4] = texels;
 +
 +      /* Copy ltc_mat_ggx into 1st layer */
 +      memcpy(texels_layer, ltc_mat_ggx, sizeof(float[4]) * 64 * 64);
 +      texels_layer += 64 * 64;
 +
 +      /* Copy bsdf_split_sum_ggx into 2nd layer red and green channels.
 +         Copy ltc_mag_ggx into 2nd layer blue channel. */
 +      for (int i = 0; i < 64 * 64; i++) {
 +              texels_layer[i][0] = bsdf_split_sum_ggx[i * 2 + 0];
 +              texels_layer[i][1] = bsdf_split_sum_ggx[i * 2 + 1];
 +              texels_layer[i][2] = ltc_mag_ggx[i];
 +              texels_layer[i][3] = ltc_disk_integral[i];
 +      }
 +      texels_layer += 64 * 64;
 +
 +      /* Copy blue noise in 3rd layer  */
 +      for (int i = 0; i < 64 * 64; i++) {
 +              texels_layer[i][0] = blue_noise[i][0];
 +              texels_layer[i][1] = blue_noise[i][2];
 +              texels_layer[i][2] = cosf(blue_noise[i][1] * 2.0f * M_PI);
 +              texels_layer[i][3] = sinf(blue_noise[i][1] * 2.0f * M_PI);
 +      }
 +      texels_layer += 64 * 64;
 +
 +      /* Copy Refraction GGX LUT in layer 4 - 20 */
 +      for (int j = 0; j < 16; ++j) {
 +              for (int i = 0; i < 64 * 64; i++) {
 +                      texels_layer[i][0] = btdf_split_sum_ggx[j * 2][i];
 +                      texels_layer[i][1] = btdf_split_sum_ggx[j * 2][i];
 +                      texels_layer[i][2] = btdf_split_sum_ggx[j * 2][i];
 +                      texels_layer[i][3] = btdf_split_sum_ggx[j * 2][i];
 +              }
 +              texels_layer += 64 * 64;
 +      }
 +
 +      e_data.util_tex = DRW_texture_create_2D_array(
 +              64, 64, layers, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_WRAP, (float *)texels);
 +
 +      MEM_freeN(texels);
 +}
 +
 +void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3])
 +{
 +      e_data.noise_offsets[0] = offsets[0];
 +      e_data.noise_offsets[1] = offsets[1];
 +      e_data.noise_offsets[2] = offsets[2];
 +
 +      /* Attach & detach because we don't currently support multiple FB per texture,
 +       * and this would be the case for multiple viewport. */
 +      GPU_framebuffer_bind(fbl->update_noise_fb);
 +      DRW_draw_pass(psl->update_noise_pass);
 +}
 +
 +static void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4])
 +{
 +      /* view vectors for the corners of the view frustum.
 +       * Can be used to recreate the world space position easily */
 +      float view_vecs[4][4] = {
 +          {-1.0f, -1.0f, -1.0f, 1.0f},
 +          { 1.0f, -1.0f, -1.0f, 1.0f},
 +          {-1.0f,  1.0f, -1.0f, 1.0f},
 +          {-1.0f, -1.0f,  1.0f, 1.0f}
 +      };
 +
 +      /* convert the view vectors to view space */
 +      const bool is_persp = (winmat[3][3] == 0.0f);
 +      for (int i = 0; i < 4; i++) {
 +              mul_project_m4_v3(invproj, view_vecs[i]);
 +              /* normalized trick see:
 +               * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
 +              if (is_persp) {
 +                      /* Divide XY by Z. */
 +                      mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]);
 +              }
 +      }
 +
 +      /**
 +       * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and
 +       *            view_vecs[1] is the vector going from the near-bottom-left corner to
 +       *            the far-top-right corner.
 +       * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner
 +       *            when Z = 1, and top-left corner if Z = 1.
 +       *            view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed)
 +       *            distance from the near plane to the far clip plane.
 +       **/
 +      copy_v4_v4(r_viewvecs[0], view_vecs[0]);
 +
 +      /* we need to store the differences */
 +      r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0];
 +      r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1];
 +      r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2];
 +}
 +
 +void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl, EEVEE_FramebufferList *fbl)
 +{
 +      if (!e_data.frag_shader_lib) {
 +              /* Shaders */
 +              e_data.frag_shader_lib = BLI_string_joinN(
 +                      datatoc_common_view_lib_glsl,
 +                      datatoc_common_uniforms_lib_glsl,
 +                      datatoc_bsdf_common_lib_glsl,
 +                      datatoc_bsdf_sampling_lib_glsl,
 +                      datatoc_ambient_occlusion_lib_glsl,
 +                      datatoc_raytrace_lib_glsl,
 +                      datatoc_ssr_lib_glsl,
 +                      datatoc_octahedron_lib_glsl,
 +                      datatoc_irradiance_lib_glsl,
 +                      datatoc_lightprobe_lib_glsl,
 +                      datatoc_ltc_lib_glsl,
 +                      datatoc_lamps_lib_glsl,
 +                      /* Add one for each Closure */
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_lit_surface_frag_glsl,
 +                      datatoc_volumetric_lib_glsl);
 +
 +              e_data.volume_shader_lib = BLI_string_joinN(
 +                      datatoc_common_view_lib_glsl,
 +                      datatoc_common_uniforms_lib_glsl,
 +                      datatoc_bsdf_common_lib_glsl,
 +                      datatoc_ambient_occlusion_lib_glsl,
 +                      datatoc_octahedron_lib_glsl,
 +                      datatoc_irradiance_lib_glsl,
 +                      datatoc_lightprobe_lib_glsl,
 +                      datatoc_ltc_lib_glsl,
 +                      datatoc_lamps_lib_glsl,
 +                      datatoc_volumetric_lib_glsl,
 +                      datatoc_volumetric_frag_glsl);
 +
 +              e_data.vert_shader_str = BLI_string_joinN(
 +                      datatoc_common_view_lib_glsl,
 +                      datatoc_common_hair_lib_glsl,
 +                      datatoc_lit_surface_vert_glsl);
 +
 +              e_data.default_background = DRW_shader_create(
 +                      datatoc_background_vert_glsl, NULL, datatoc_default_world_frag_glsl,
 +                      NULL);
 +
 +              e_data.default_studiolight_background = DRW_shader_create(
 +                      datatoc_background_vert_glsl, NULL, datatoc_default_world_frag_glsl,
 +                      "#define LOOKDEV\n");
 +
 +              e_data.default_prepass_sh = DRW_shader_create(
 +                      datatoc_prepass_vert_glsl, NULL, datatoc_prepass_frag_glsl,
 +                      NULL);
 +
 +              e_data.default_prepass_clip_sh = DRW_shader_create(
 +                      datatoc_prepass_vert_glsl, NULL, datatoc_prepass_frag_glsl,
 +                      "#define CLIP_PLANES\n");
 +
 +              char *vert_str = BLI_string_joinN(
 +                      datatoc_common_view_lib_glsl,
 +                      datatoc_common_hair_lib_glsl,
 +                      datatoc_prepass_vert_glsl);
 +
 +              e_data.default_hair_prepass_sh = DRW_shader_create(
 +                      vert_str, NULL, datatoc_prepass_frag_glsl,
 +                      "#define HAIR_SHADER\n");
 +
 +              e_data.default_hair_prepass_clip_sh = DRW_shader_create(
 +                      vert_str, NULL, datatoc_prepass_frag_glsl,
 +                      "#define HAIR_SHADER\n"
 +                      "#define CLIP_PLANES\n");
 +
 +              MEM_freeN(vert_str);
 +
 +              e_data.update_noise_sh = DRW_shader_create_fullscreen(
 +                      datatoc_update_noise_frag_glsl, NULL);
 +
 +              eevee_init_util_texture();
 +              eevee_init_noise_texture();
 +      }
 +
 +      if (!DRW_state_is_image_render() &&
 +          ((stl->effects->enabled_effects & EFFECT_TAA) == 0))
 +      {
 +              e_data.alpha_hash_offset = 0.0f;
 +      }
 +      else {
 +              double r;
 +              BLI_halton_1D(5, 0.0, stl->effects->taa_current_sample - 1, &r);
 +              e_data.alpha_hash_offset = (float)r;
 +      }
 +
 +      {
 +              /* Update view_vecs */
 +              float invproj[4][4], winmat[4][4];
 +              DRW_viewport_matrix_get(winmat, DRW_MAT_WIN);
 +              DRW_viewport_matrix_get(invproj, DRW_MAT_WININV);
 +
 +              EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs);
 +      }
 +
 +      {
 +              /* Update noise Framebuffer. */
 +              GPU_framebuffer_ensure_config(&fbl->update_noise_fb, {
 +                      GPU_ATTACHMENT_NONE,
 +                      GPU_ATTACHMENT_TEXTURE_LAYER(e_data.util_tex, 2)
 +              });
 +      }
 +}
 +
 +struct GPUMaterial *EEVEE_material_world_lightprobe_get(struct Scene *scene, World *wo)
 +{
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      const int options = VAR_WORLD_PROBE;
 +
 +      GPUMaterial *mat = DRW_shader_find_from_world(wo, engine, options, false);
 +      if (mat != NULL) {
 +              return mat;
 +      }
 +      return DRW_shader_create_from_world(
 +              scene, wo, engine, options,
 +              datatoc_background_vert_glsl, NULL, e_data.frag_shader_lib,
 +              SHADER_DEFINES "#define PROBE_CAPTURE\n", false);
 +}
 +
 +struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, World *wo)
 +{
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      int options = VAR_WORLD_BACKGROUND;
 +
 +      GPUMaterial *mat = DRW_shader_find_from_world(wo, engine, options, true);
 +      if (mat != NULL) {
 +              return mat;
 +      }
 +      return DRW_shader_create_from_world(
 +              scene, wo, engine, options,
 +              datatoc_background_vert_glsl, NULL, e_data.frag_shader_lib,
 +              SHADER_DEFINES "#define WORLD_BACKGROUND\n", true);
 +}
 +
 +struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, World *wo)
 +{
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      int options = VAR_WORLD_VOLUME;
 +
 +      GPUMaterial *mat = DRW_shader_find_from_world(wo, engine, options, true);
 +      if (mat != NULL) {
 +              return mat;
 +      }
 +
 +      char *defines = eevee_get_volume_defines(options);
 +
 +      mat = DRW_shader_create_from_world(
 +              scene, wo, engine, options,
 +              datatoc_volumetric_vert_glsl, datatoc_volumetric_geom_glsl, e_data.volume_shader_lib,
 +              defines, true);
 +
 +      MEM_freeN(defines);
 +
 +      return mat;
 +}
 +
 +struct GPUMaterial *EEVEE_material_mesh_get(
 +        struct Scene *scene, Material *ma, EEVEE_Data *vedata,
 +        bool use_blend, bool use_multiply, bool use_refract, bool use_sss, bool use_translucency, int shadow_method)
 +{
 +      EEVEE_EffectsInfo *effects = vedata->stl->effects;
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      int options = VAR_MAT_MESH;
 +
 +      if (use_blend) options |= VAR_MAT_BLEND;
 +      if (use_multiply) options |= VAR_MAT_MULT;
 +      if (use_refract) options |= VAR_MAT_REFRACT;
 +      if (use_sss) options |= VAR_MAT_SSS;
 +      if (use_sss && effects->sss_separate_albedo) options |= VAR_MAT_SSSALBED;
 +      if (use_translucency) options |= VAR_MAT_TRANSLUC;
 +      if (((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) && use_blend) options |= VAR_MAT_VOLUME;
 +
 +      options |= eevee_material_shadow_option(shadow_method);
 +
 +      GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
 +      if (mat) {
 +              return mat;
 +      }
 +
 +      char *defines = eevee_get_defines(options);
 +
 +      mat = DRW_shader_create_from_material(
 +              scene, ma, engine, options,
 +              e_data.vert_shader_str, NULL, e_data.frag_shader_lib,
 +              defines, true);
 +
 +      MEM_freeN(defines);
 +
 +      return mat;
 +}
 +
 +struct GPUMaterial *EEVEE_material_mesh_volume_get(struct Scene *scene, Material *ma)
 +{
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      int options = VAR_MAT_VOLUME;
 +
 +      GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
 +      if (mat != NULL) {
 +              return mat;
 +      }
 +
 +      char *defines = eevee_get_volume_defines(options);
 +
 +      mat = DRW_shader_create_from_material(
 +              scene, ma, engine, options,
 +              datatoc_volumetric_vert_glsl, datatoc_volumetric_geom_glsl, e_data.volume_shader_lib,
 +              defines, true);
 +
 +      MEM_freeN(defines);
 +
 +      return mat;
 +}
 +
 +struct GPUMaterial *EEVEE_material_mesh_depth_get(
 +        struct Scene *scene, Material *ma,
 +        bool use_hashed_alpha, bool is_shadow)
 +{
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      int options = VAR_MAT_MESH;
 +
 +      if (use_hashed_alpha) {
 +              options |= VAR_MAT_HASH;
 +      }
 +      else {
 +              options |= VAR_MAT_CLIP;
 +      }
 +
 +      if (is_shadow)
 +              options |= VAR_MAT_SHADOW;
 +
 +      GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
 +      if (mat) {
 +              return mat;
 +      }
 +
 +      char *defines = eevee_get_defines(options);
 +
 +      char *frag_str = BLI_string_joinN(
 +              e_data.frag_shader_lib,
 +              datatoc_prepass_frag_glsl);
 +
 +      mat = DRW_shader_create_from_material(
 +              scene, ma, engine, options,
 +              (is_shadow) ? datatoc_shadow_vert_glsl : e_data.vert_shader_str,
 +              NULL,
 +              frag_str,
 +              defines,
 +              true);
 +
 +      MEM_freeN(frag_str);
 +      MEM_freeN(defines);
 +
 +      return mat;
 +}
 +
 +struct GPUMaterial *EEVEE_material_hair_get(
 +        struct Scene *scene, Material *ma, int shadow_method)
 +{
 +      const void *engine = &DRW_engine_viewport_eevee_type;
 +      int options = VAR_MAT_MESH | VAR_MAT_HAIR;
 +
 +      options |= eevee_material_shadow_option(shadow_method);
 +
 +      GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
 +      if (mat) {
 +              return mat;
 +      }
 +
 +      char *defines = eevee_get_defines(options);
 +
 +      mat = DRW_shader_create_from_material(
 +              scene, ma, engine, options,
 +              e_data.vert_shader_str, NULL, e_data.frag_shader_lib,
 +              defines, true);
 +
 +      MEM_freeN(defines);
 +
 +      return mat;
 +}
 +
 +/**
 + * Create a default shading group inside the given pass.
 + **/
 +static struct DRWShadingGroup *EEVEE_default_shading_group_create(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWPass *pass,
 +        bool is_hair, bool is_flat_normal, bool use_blend, bool use_ssr, int shadow_method)
 +{
 +      EEVEE_EffectsInfo *effects = vedata->stl->effects;
 +      static int ssr_id;
 +      ssr_id = (use_ssr) ? 1 : -1;
 +      int options = VAR_MAT_MESH;
 +
 +      if (is_hair) options |= VAR_MAT_HAIR;
 +      if (is_flat_normal) options |= VAR_MAT_FLAT;
 +      if (use_blend) options |= VAR_MAT_BLEND;
 +      if (((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) && use_blend) options |= VAR_MAT_VOLUME;
 +
 +      options |= eevee_material_shadow_option(shadow_method);
 +
 +      if (e_data.default_lit[options] == NULL) {
 +              create_default_shader(options);
 +      }
 +
 +      DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit[options], pass);
 +      add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, use_blend);
 +
 +      return shgrp;
 +}
 +
 +/**
 + * Create a default shading group inside the default pass without standard uniforms.
 + **/
 +static struct DRWShadingGroup *EEVEE_default_shading_group_get(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        Object *ob, ParticleSystem *psys, ModifierData *md,
 +        bool is_hair, bool is_flat_normal, bool use_ssr, int shadow_method)
 +{
 +      static int ssr_id;
 +      ssr_id = (use_ssr) ? 1 : -1;
 +      int options = VAR_MAT_MESH;
 +
 +      BLI_assert(!is_hair || (ob && psys && md));
 +
 +      if (is_hair) options |= VAR_MAT_HAIR;
 +      if (is_flat_normal) options |= VAR_MAT_FLAT;
 +
 +      options |= eevee_material_shadow_option(shadow_method);
 +
 +      if (e_data.default_lit[options] == NULL) {
 +              create_default_shader(options);
 +      }
 +
 +      if (vedata->psl->default_pass[options] == NULL) {
 +              DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
 +              vedata->psl->default_pass[options] = DRW_pass_create("Default Lit Pass", state);
 +
 +              /* XXX / WATCH: This creates non persistent binds for the ubos and textures.
 +               * But it's currently OK because the following shgroups does not add any bind.
 +               * EDIT: THIS IS NOT THE CASE FOR HAIRS !!! DUMMY!!! */
 +              if (!is_hair) {
 +                      DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit[options], vedata->psl->default_pass[options]);
 +                      add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, false);
 +              }
 +      }
 +
 +      if (is_hair) {
 +              DRWShadingGroup *shgrp = DRW_shgroup_hair_create(ob, psys, md,
 +                                                               vedata->psl->default_pass[options],
 +                                                               e_data.default_lit[options]);
 +              add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, false);
 +              return shgrp;
 +      }
 +      else {
 +              return DRW_shgroup_create(e_data.default_lit[options], vedata->psl->default_pass[options]);
 +      }
 +}
 +
 +/**
 + * Create a default shading group inside the lookdev pass without standard uniforms.
 + **/
 +static struct DRWShadingGroup *EEVEE_lookdev_shading_group_get(
 +        EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        bool use_ssr, int shadow_method)
 +{
 +      static int ssr_id;
 +      ssr_id = (use_ssr) ? 1 : -1;
 +      int options = VAR_MAT_MESH | VAR_MAT_LOOKDEV;
 +
 +      options |= eevee_material_shadow_option(shadow_method);
 +
 +      if (e_data.default_lit[options] == NULL) {
 +              create_default_shader(options);
 +      }
 +
 +      if (vedata->psl->lookdev_pass == NULL) {
 +              DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_CULL_BACK;
 +              vedata->psl->lookdev_pass = DRW_pass_create("LookDev Pass", state);
 +
 +              DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit[options], vedata->psl->lookdev_pass);
 +              /* XXX / WATCH: This creates non persistent binds for the ubos and textures.
 +               * But it's currently OK because the following shgroups does not add any bind. */
 +              add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, false);
 +      }
 +
 +      return DRW_shgroup_create(e_data.default_lit[options], vedata->psl->lookdev_pass);
 +}
 +void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 +{
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +
 +      /* Create Material Ghash */
 +      {
 +              stl->g_data->material_hash = BLI_ghash_ptr_new("Eevee_material ghash");
 +      }
 +
 +      {
 +              psl->background_pass = DRW_pass_create("Background Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
 +
 +              struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
 +              DRWShadingGroup *grp = NULL;
 +
 +              const DRWContextState *draw_ctx = DRW_context_state_get();
 +              Scene *scene = draw_ctx->scene;
 +              World *wo = scene->world;
 +
 +              float *col = ts.colorBackground;
 +
 +              /* LookDev */
 +              EEVEE_lookdev_cache_init(vedata, &grp, e_data.default_studiolight_background, psl->background_pass, wo, NULL);
 +              /* END */
 +
 +              if (!grp && wo) {
 +                      col = &wo->horr;
 +
 +                      if (wo->use_nodes && wo->nodetree) {
 +                              static float error_col[3] = {1.0f, 0.0f, 1.0f};
 +                              static float compile_col[3] = {0.5f, 0.5f, 0.5f};
 +                              struct GPUMaterial *gpumat = EEVEE_material_world_background_get(scene, wo);
 +
 +                              switch (GPU_material_status(gpumat)) {
 +                                      case GPU_MAT_SUCCESS:
 +                                              grp = DRW_shgroup_material_create(gpumat, psl->background_pass);
 +                                              DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
++                                              /* TODO (fclem): remove those (need to clean the GLSL files). */
 +                                              DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
 +                                              DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
 +                                              DRW_shgroup_call_add(grp, geom, NULL);
 +                                              break;
 +                                      case GPU_MAT_QUEUED:
 +                                              /* TODO Bypass probe compilation. */
 +                                              col = compile_col;
 +                                              break;
 +                                      case GPU_MAT_FAILED:
 +                                      default:
 +                                              col = error_col;
 +                                              break;
 +                              }
 +                      }
 +              }
 +
 +              /* Fallback if shader fails or if not using nodetree. */
 +              if (grp == NULL) {
 +                      grp = DRW_shgroup_create(e_data.default_background, psl->background_pass);
 +                      DRW_shgroup_uniform_vec3(grp, "color", col, 1);
 +                      DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
 +                      DRW_shgroup_call_add(grp, geom, NULL);
 +              }
 +      }
 +
 +      {
 +              DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WIRE;
 +              psl->depth_pass = DRW_pass_create("Depth Pass", state);
 +              stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.default_prepass_sh, psl->depth_pass);
 +
 +              state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK;
 +              psl->depth_pass_cull = DRW_pass_create("Depth Pass Cull", state);
 +              stl->g_data->depth_shgrp_cull = DRW_shgroup_create(e_data.default_prepass_sh, psl->depth_pass_cull);
 +
 +              state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
 +              psl->depth_pass_clip = DRW_pass_create("Depth Pass Clip", state);
 +              stl->g_data->depth_shgrp_clip = DRW_shgroup_create(e_data.default_prepass_clip_sh, psl->depth_pass_clip);
 +              DRW_shgroup_uniform_block(stl->g_data->depth_shgrp_clip, "clip_block", sldata->clip_ubo);
 +
 +              state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_CULL_BACK;
 +              psl->depth_pass_clip_cull = DRW_pass_create("Depth Pass Cull Clip", state);
 +              stl->g_data->depth_shgrp_clip_cull = DRW_shgroup_create(
 +                      e_data.default_prepass_clip_sh, psl->depth_pass_clip_cull);
 +              DRW_shgroup_uniform_block(stl->g_data->depth_shgrp_clip_cull, "clip_block", sldata->clip_ubo);
 +      }
 +
 +      {
 +              DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
 +              psl->material_pass = DRW_pass_create("Material Shader Pass", state);
 +      }
 +
 +      {
 +              DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WIRE;
 +              psl->refract_depth_pass = DRW_pass_create("Refract Depth Pass", state);
 +              stl->g_data->refract_depth_shgrp = DRW_shgroup_create(e_data.default_prepass_sh, psl->refract_depth_pass);
 +
 +              state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK;
 +              psl->refract_depth_pass_cull = DRW_pass_create("Refract Depth Pass Cull", state);
 +              stl->g_data->refract_depth_shgrp_cull = DRW_shgroup_create(
 +                      e_data.default_prepass_sh, psl->refract_depth_pass_cull);
 +
 +              state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
 +              psl->refract_depth_pass_clip = DRW_pass_create("Refract Depth Pass Clip", state);
 +              stl->g_data->refract_depth_shgrp_clip = DRW_shgroup_create(
 +                      e_data.default_prepass_clip_sh, psl->refract_depth_pass_clip);
 +              DRW_shgroup_uniform_block(stl->g_data->refract_depth_shgrp_clip, "clip_block", sldata->clip_ubo);
 +
 +              state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_CULL_BACK;
 +              psl->refract_depth_pass_clip_cull = DRW_pass_create("Refract Depth Pass Cull Clip", state);
 +              stl->g_data->refract_depth_shgrp_clip_cull = DRW_shgroup_create(
 +                      e_data.default_prepass_clip_sh, psl->refract_depth_pass_clip_cull);
 +              DRW_shgroup_uniform_block(stl->g_data->refract_depth_shgrp_clip_cull, "clip_block", sldata->clip_ubo);
 +      }
 +
 +      {
 +              DRWState state = (
 +                      DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES |
 +                      DRW_STATE_WIRE);
 +              psl->refract_pass = DRW_pass_create("Opaque Refraction Pass", state);
 +      }
 +
 +      {
 +              DRWState state = (
 +                      DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES |
 +                      DRW_STATE_WIRE | DRW_STATE_WRITE_STENCIL);
 +              psl->sss_pass = DRW_pass_create("Subsurface Pass", state);
 +              e_data.sss_count = 0;
 +      }
 +
 +      {
 +              DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
 +              psl->transparent_pass = DRW_pass_create("Material Transparent Pass", state);
 +      }
 +
 +      {
 +              psl->update_noise_pass = DRW_pass_create("Update Noise Pass", DRW_STATE_WRITE_COLOR);
 +              DRWShadingGroup *grp = DRW_shgroup_create(e_data.update_noise_sh, psl->update_noise_pass);
 +              DRW_shgroup_uniform_texture(grp, "blueNoise", e_data.noise_tex);
 +              DRW_shgroup_uniform_vec3(grp, "offsets", e_data.noise_offsets, 1);
 +              DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 +      }
 +}
 +
 +#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) do { \
 +      if (is_sculpt_mode_draw) { \
 +              DRW_shgroup_call_sculpt_add(shgrp, ob, ob->obmat); \
 +      } \
 +      else { \
 +              if (oedata) { \
 +                      DRW_shgroup_call_object_add_with_callback(shgrp, geom, ob, EEVEE_lightprobes_obj_visibility_cb, oedata); \
 +              } \
 +              else { \
 +                      DRW_shgroup_call_object_add(shgrp, geom, ob); \
 +              } \
 +      } \
 +} while (0)
 +
 +#define ADD_SHGROUP_CALL_SAFE(shgrp, ob, geom, oedata) do { \
 +      if (shgrp) { \
 +              ADD_SHGROUP_CALL(shgrp, ob, geom, oedata); \
 +      } \
 +} while (0)
 +
 +typedef struct EeveeMaterialShadingGroups {
 +      struct DRWShadingGroup *shading_grp;
 +      struct DRWShadingGroup *depth_grp;
 +      struct DRWShadingGroup *depth_clip_grp;
 +} EeveeMaterialShadingGroups;
 +
 +static void material_opaque(
 +        Material *ma, GHash *material_hash, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        bool do_cull, bool use_flat_nor, struct GPUMaterial **gpumat, struct GPUMaterial **gpumat_depth,
 +        struct DRWShadingGroup **shgrp, struct DRWShadingGroup **shgrp_depth, struct DRWShadingGroup **shgrp_depth_clip)
 +{
 +      EEVEE_EffectsInfo *effects = vedata->stl->effects;
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      Scene *scene = draw_ctx->scene;
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +      bool use_diffuse, use_glossy, use_refract;
 +
 +      float *color_p = &ma->r;
 +      float *metal_p = &ma->metallic;
 +      float *spec_p = &ma->spec;
 +      float *rough_p = &ma->roughness;
 +
 +      const bool use_gpumat = (ma->use_nodes && ma->nodetree);
 +      const bool use_ssrefract = ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) &&
 +                               ((effects->enabled_effects & EFFECT_REFRACT) != 0);
 +      bool use_sss = ((effects->enabled_effects & EFFECT_SSS) != 0);
 +      const bool use_translucency = use_sss && ((ma->blend_flag & MA_BL_TRANSLUCENCY) != 0);
 +
 +      EeveeMaterialShadingGroups *emsg = BLI_ghash_lookup(material_hash, (const void *)ma);
 +
 +      if (emsg) {
 +              *shgrp = emsg->shading_grp;
 +              *shgrp_depth = emsg->depth_grp;
 +              *shgrp_depth_clip = emsg->depth_clip_grp;
 +
 +              /* This will have been created already, just perform a lookup. */
 +              *gpumat = (use_gpumat) ? EEVEE_material_mesh_get(
 +                      scene, ma, vedata, false, false, use_ssrefract, use_sss, use_translucency, linfo->shadow_method) : NULL;
 +              *gpumat_depth = (use_gpumat) ? EEVEE_material_mesh_depth_get(
 +                      scene, ma, (ma->blend_method == MA_BM_HASHED), false) : NULL;
 +              return;
 +      }
 +
 +      if (use_gpumat) {
 +              static float error_col[3] = {1.0f, 0.0f, 1.0f};
 +              static float compile_col[3] = {0.5f, 0.5f, 0.5f};
 +              static float half = 0.5f;
 +
 +              /* Shading */
 +              *gpumat = EEVEE_material_mesh_get(
 +                      scene, ma, vedata, false, false, use_ssrefract,
 +                      use_sss, use_translucency, linfo->shadow_method);
 +
 +              GPUMaterialStatus status_mat_surface = GPU_material_status(*gpumat);
 +
 +              /* Alpha CLipped : Discard pixel from depth pass, then
 +               * fail the depth test for shading. */
 +              if (ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED)) {
 +                      *gpumat_depth = EEVEE_material_mesh_depth_get(scene, ma, (ma->blend_method == MA_BM_HASHED), false);
 +
 +                      GPUMaterialStatus status_mat_depth = GPU_material_status(*gpumat_depth);
 +                      if (status_mat_depth != GPU_MAT_SUCCESS) {
 +                              /* Mixing both flags. If depth shader fails, show it to the user by not using
 +                               * the surface shader. */
 +                              status_mat_surface = status_mat_depth;
 +                      }
 +                      else if (use_ssrefract) {
 +                              *shgrp_depth = DRW_shgroup_material_create(
 +                                      *gpumat_depth, (do_cull) ? psl->refract_depth_pass_cull : psl->refract_depth_pass);
 +                              *shgrp_depth_clip = DRW_shgroup_material_create(
 +                                      *gpumat_depth, (do_cull) ? psl->refract_depth_pass_clip_cull : psl->refract_depth_pass_clip);
 +                      }
 +                      else {
 +                              *shgrp_depth = DRW_shgroup_material_create(
 +                                      *gpumat_depth, (do_cull) ? psl->depth_pass_cull : psl->depth_pass);
 +                              *shgrp_depth_clip = DRW_shgroup_material_create(
 +                                      *gpumat_depth, (do_cull) ? psl->depth_pass_clip_cull : psl->depth_pass_clip);
 +                      }
 +
 +                      if (*shgrp_depth != NULL) {
 +                              use_diffuse = GPU_material_flag_get(*gpumat_depth, GPU_MATFLAG_DIFFUSE);
 +                              use_glossy = GPU_material_flag_get(*gpumat_depth, GPU_MATFLAG_GLOSSY);
 +                              use_refract = GPU_material_flag_get(*gpumat_depth, GPU_MATFLAG_REFRACT);
 +
 +                              add_standard_uniforms(*shgrp_depth, sldata, vedata, NULL, NULL,
 +                                                    use_diffuse, use_glossy, use_refract, false, false);
 +                              add_standard_uniforms(*shgrp_depth_clip, sldata, vedata, NULL, NULL,
 +                                                    use_diffuse, use_glossy, use_refract, false, false);
 +
 +                              if (ma->blend_method == MA_BM_CLIP) {
 +                                      DRW_shgroup_uniform_float(*shgrp_depth, "alphaThreshold", &ma->alpha_threshold, 1);
 +                                      DRW_shgroup_uniform_float(*shgrp_depth_clip, "alphaThreshold", &ma->alpha_threshold, 1);
 +                              }
 +                              else if (ma->blend_method == MA_BM_HASHED) {
 +                                      DRW_shgroup_uniform_float(*shgrp_depth, "hashAlphaOffset", &e_data.alpha_hash_offset, 1);
 +                                      DRW_shgroup_uniform_float(*shgrp_depth_clip, "hashAlphaOffset", &e_data.alpha_hash_offset, 1);
 +                              }
 +                      }
 +              }
 +
 +              switch (status_mat_surface) {
 +                      case GPU_MAT_SUCCESS:
 +                      {
 +                              static int no_ssr = -1;
 +                              static int first_ssr = 1;
 +                              int *ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? &first_ssr : &no_ssr;
 +                              use_diffuse = GPU_material_flag_get(*gpumat, GPU_MATFLAG_DIFFUSE);
 +                              use_glossy = GPU_material_flag_get(*gpumat, GPU_MATFLAG_GLOSSY);
 +                              use_refract = GPU_material_flag_get(*gpumat, GPU_MATFLAG_REFRACT);
 +                              use_sss = use_sss && GPU_material_flag_get(*gpumat, GPU_MATFLAG_SSS);
 +
 +                              *shgrp = DRW_shgroup_material_create(
 +                                      *gpumat,
 +                                      (use_ssrefract) ? psl->refract_pass :
 +                                      (use_sss) ? psl->sss_pass : psl->material_pass);
 +
 +                              add_standard_uniforms(*shgrp, sldata, vedata, ssr_id, &ma->refract_depth,
 +                                                    use_diffuse, use_glossy, use_refract, use_ssrefract, false);
 +
 +                              if (use_sss) {
 +                                      struct GPUTexture *sss_tex_profile = NULL;
 +                                      struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get(
 +                                              *gpumat,
 +                                              stl->effects->sss_sample_count,
 +                                              &sss_tex_profile);
 +
 +                                      if (sss_profile) {
 +                                              if (use_translucency) {
 +                                                      DRW_shgroup_uniform_block(*shgrp, "sssProfile", sss_profile);
 +                                                      DRW_shgroup_uniform_texture(*shgrp, "sssTexProfile", sss_tex_profile);
 +                                              }
 +
 +                                              /* Limit of 8 bit stencil buffer. ID 255 is refraction. */
 +                                              if (e_data.sss_count < 254) {
 +                                                      DRW_shgroup_stencil_mask(*shgrp, e_data.sss_count + 1);
 +                                                      EEVEE_subsurface_add_pass(sldata, vedata, e_data.sss_count + 1, sss_profile);
 +                                                      e_data.sss_count++;
 +                                              }
 +                                              else {
 +                                                      /* TODO : display message. */
 +                                                      printf("Error: Too many different Subsurface shader in the scene.\n");
 +                                              }
 +                                      }
 +                              }
 +                              break;
 +                      }
 +                      case GPU_MAT_QUEUED:
 +                      {
 +                              color_p = compile_col;
 +                              metal_p = spec_p = rough_p = &half;
 +                              break;
 +                      }
 +                      case GPU_MAT_FAILED:
 +                      default:
 +                              color_p = error_col;
 +                              metal_p = spec_p = rough_p = &half;
 +                              break;
 +              }
 +      }
 +
 +      /* Fallback to default shader */
 +      if (*shgrp == NULL) {
 +              bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0);
 +              *shgrp = EEVEE_default_shading_group_get(sldata, vedata,
 +                                                       NULL, NULL, NULL,
 +                                                       false, use_flat_nor, use_ssr, linfo->shadow_method);
 +              DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
 +              DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
 +              DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
 +              DRW_shgroup_uniform_float(*shgrp, "roughness", rough_p, 1);
 +      }
 +
 +      /* Fallback default depth prepass */
 +      if (*shgrp_depth == NULL) {
 +              if (use_ssrefract) {
 +                      *shgrp_depth = (do_cull) ? stl->g_data->refract_depth_shgrp_cull : stl->g_data->refract_depth_shgrp;
 +                      *shgrp_depth_clip = (do_cull) ? stl->g_data->refract_depth_shgrp_clip_cull : stl->g_data->refract_depth_shgrp_clip;
 +              }
 +              else {
 +                      *shgrp_depth = (do_cull) ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp;
 +                      *shgrp_depth_clip = (do_cull) ? stl->g_data->depth_shgrp_clip_cull : stl->g_data->depth_shgrp_clip;
 +              }
 +      }
 +
 +      emsg = MEM_mallocN(sizeof(EeveeMaterialShadingGroups), "EeveeMaterialShadingGroups");
 +      emsg->shading_grp = *shgrp;
 +      emsg->depth_grp = *shgrp_depth;
 +      emsg->depth_clip_grp = *shgrp_depth_clip;
 +      BLI_ghash_insert(material_hash, ma, emsg);
 +}
 +
 +static void material_transparent(
 +        Material *ma, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
 +        bool do_cull, bool use_flat_nor,
 +        struct GPUMaterial **gpumat, struct DRWShadingGroup **shgrp, struct DRWShadingGroup **shgrp_depth)
 +{
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      Scene *scene = draw_ctx->scene;
 +      EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 +      EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
 +      EEVEE_LampsInfo *linfo = sldata->lamps;
 +
 +      const bool use_ssrefract = (
 +              ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) &&
 +              ((stl->effects->enabled_effects & EFFECT_REFRACT) != 0)
 +      );
 +      float *color_p = &ma->r;
 +      float *metal_p = &ma->metallic;
 +      float *spec_p = &ma->spec;
 +      float *rough_p = &ma->roughness;
 +
 +      if (ma->use_nodes && ma->nodetree) {
 +              static float error_col[3] = {1.0f, 0.0f, 1.0f};
 +              static float compile_col[3] = {0.5f, 0.5f, 0.5f};
 +              static float half = 0.5f;
 +
 +              /* Shading */
 +              *gpumat = EEVEE_material_mesh_get(
 +                      scene, ma, vedata, true, (ma->blend_method == MA_BM_MULTIPLY), use_ssrefract,
 +                      false, false, linfo->shadow_method);
 +
 +              switch (GPU_material_status(*gpumat)) {
 +                      case GPU_MAT_SUCCESS:
 +                      {
 +                              static int ssr_id = -1; /* TODO transparent SSR */
 +                              bool use_blend = (ma->blend_method & MA_BM_BLEND) != 0;
 +
 +                              *shgrp = DRW_shgroup_material_create(*gpumat, psl->transparent_pass);
 +
 +                              bool use_diffuse = GPU_material_flag_get(*gpumat, GPU_MATFLAG_DIFFUSE);
 +                              bool use_glossy = GPU_material_flag_get(*gpumat, GPU_MATFLAG_GLOSSY);
 +                              bool use_refract = GPU_material_flag_get(*gpumat, GPU_MATFLAG_REFRACT);
 +
 +                              add_standard_uniforms(*shgrp, sldata, vedata, &ssr_id, &ma->refract_depth,
 +                                                    use_diffuse, use_glossy, use_refract, use_ssrefract, use_blend);
 +                              break;
 +                      }
 +                      case GPU_MAT_QUEUED:
 +                      {
 +                              /* TODO Bypass probe compilation. */
 +                              color_p = compile_col;
 +                              metal_p = spec_p = rough_p = &half;
 +                              break;
 +                      }
 +                      case GPU_MAT_FAILED:
 +                      default:
 +                              color_p = error_col;
 +                              metal_p = spec_p = rough_p = &half;
 +                              break;
 +              }
 +      }
 +
 +      /* Fallback to default shader */
 +      if (*shgrp == NULL) {
 +              *shgrp = EEVEE_default_shading_group_create(
 +                      sldata, vedata, psl->transparent_pass,
 +                      false, use_flat_nor, true, false, linfo->shadow_method);
 +              DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
 +              DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
 +              DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
 +              DRW_shgroup_uniform_float(*shgrp, "roughness", rough_p, 1);
 +      }
 +
 +      const bool use_prepass = ((ma->blend_flag & MA_BL_HIDE_BACKSIDE) != 0);
 +
 +      DRWState all_state = (
 +              DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_CULL_BACK |
 +              DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_DEPTH_EQUAL |
 +              DRW_STATE_BLEND | DRW_STATE_ADDITIVE | DRW_STATE_MULTIPLY
 +      );
 +
 +      DRWState cur_state = DRW_STATE_WRITE_COLOR;
 +      cur_state |= (use_prepass) ? DRW_STATE_DEPTH_EQUAL : DRW_STATE_DEPTH_LESS_EQUAL;
 +      cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0;
 +
 +      switch (ma->blend_method) {
 +              case MA_BM_ADD:
 +                      cur_state |= DRW_STATE_ADDITIVE;
 +                      break;
 +              case MA_BM_MULTIPLY:
 +                      cur_state |= DRW_STATE_MULTIPLY;
 +                      break;
 +              case MA_BM_BLEND:
 +                      cur_state |= DRW_STATE_BLEND;
 +                      break;
 +              default:
 +                      BLI_assert(0);
 +                      break;
 +      }
 +
 +      /* Disable other blend modes and use the one we want. */
 +      DRW_shgroup_state_disable(*shgrp, all_state);
 +      DRW_shgroup_state_enable(*shgrp, cur_state);
 +
 +      /* Depth prepass */
 +      if (use_prepass) {
 +              *shgrp_depth = DRW_shgroup_create(e_data.default_prepass_clip_sh, psl->transparent_pass);
 +              DRW_shgroup_uniform_block(*shgrp_depth, "clip_block", sldata->clip_ubo);
 +
 +              cur_state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
 +              cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0;
 +
 +