Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2022-04-21 11:28:30 +0300
committerClément Foucault <foucault.clem@gmail.com>2022-04-23 00:12:05 +0300
commita60215f058845e14084132c82ddce3c3b028c80f (patch)
tree589dc5898e5db74b628d45ee2aad89e987c47d08
parent2882cbe685e7eca2a79438998354035c5d665205 (diff)
EEVEE: Rewrite: Implement nodetree support with every geometry typestmp-eevee-next-merge
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh12
-rw-r--r--source/blender/draw/CMakeLists.txt19
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.hh46
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_defines.hh43
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_engine.cc67
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_engine.h3
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.cc167
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.hh94
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_material.cc320
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_material.hh257
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_pipeline.cc220
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_pipeline.hh216
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc375
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.hh70
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader_shared.hh86
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.cc316
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.hh159
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.cc206
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.hh157
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_world.cc97
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_world.hh64
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl305
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl37
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl46
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl20
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl21
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl358
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl71
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl36
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl106
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl29
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh176
-rw-r--r--source/blender/draw/intern/draw_manager_profiling.h8
-rw-r--r--source/blender/gpu/CMakeLists.txt1
35 files changed, 4219 insertions, 7 deletions
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index a7cad5461b4..d7c41ae88a8 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -543,6 +543,18 @@ Container &move_assign_container(Container &dst, Container &&src) noexcept(
return dst;
}
+/**
+ * Returns true if the value is different and was assigned.
+ */
+template<typename T> inline bool assign_if_different(T &old_value, T new_value)
+{
+ if (old_value != new_value) {
+ old_value = std::move(new_value);
+ return true;
+ }
+ return false;
+}
+
} // namespace blender
namespace blender::detail {
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index b196d56fae2..211e4daecf6 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -133,6 +133,13 @@ set(SRC
engines/eevee/eevee_temporal_sampling.c
engines/eevee/eevee_volumes.c
engines/eevee_next/eevee_engine.cc
+ engines/eevee_next/eevee_instance.cc
+ engines/eevee_next/eevee_material.cc
+ engines/eevee_next/eevee_pipeline.cc
+ engines/eevee_next/eevee_shader.cc
+ engines/eevee_next/eevee_sync.cc
+ engines/eevee_next/eevee_view.cc
+ engines/eevee_next/eevee_world.cc
engines/workbench/workbench_data.c
engines/workbench/workbench_effect_antialiasing.c
engines/workbench/workbench_effect_cavity.c
@@ -341,6 +348,18 @@ set(GLSL_SRC
engines/eevee/shaders/volumetric_integration_frag.glsl
engines/eevee/shaders/world_vert.glsl
+ engines/eevee_next/shaders/eevee_attributes_lib.glsl
+ engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
+ engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
+ engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
+ engines/eevee_next/shaders/eevee_geom_world_vert.glsl
+ engines/eevee_next/shaders/eevee_nodetree_lib.glsl
+ engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
+ engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
+ engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
+ engines/eevee_next/shaders/eevee_surf_lib.glsl
+ engines/eevee_next/shaders/eevee_surf_world_frag.glsl
+
engines/workbench/shaders/workbench_cavity_lib.glsl
engines/workbench/shaders/workbench_common_lib.glsl
engines/workbench/shaders/workbench_composite_frag.glsl
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh
new file mode 100644
index 00000000000..3db343703e0
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+namespace blender::eevee {
+
+class Instance;
+
+static const float cubeface_mat[6][4][4] = {
+ /* Pos X */
+ {{0.0f, 0.0f, -1.0f, 0.0f},
+ {0.0f, -1.0f, 0.0f, 0.0f},
+ {-1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}},
+ /* Neg X */
+ {{0.0f, 0.0f, 1.0f, 0.0f},
+ {0.0f, -1.0f, 0.0f, 0.0f},
+ {1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}},
+ /* Pos Y */
+ {{1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, -1.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}},
+ /* Neg Y */
+ {{1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 1.0f, 0.0f},
+ {0.0f, -1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}},
+ /* Pos Z */
+ {{1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, -1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, -1.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}},
+ /* Neg Z */
+ {{-1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, -1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 1.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}},
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh
new file mode 100644
index 00000000000..35eb33671db
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid
+ * dragging larger headers into the createInfo pipeline which would cause problems.
+ */
+
+#pragma once
+
+/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */
+/* Current limiting factor is the sorting phase which is single pass and only sort within a
+ * threadgroup which maximum size is 1024. */
+#define CULLING_BATCH_SIZE 1024
+
+/**
+ * IMPORTANT: Some data packing are tweaked for these values.
+ * Be sure to update them accordingly.
+ * SHADOW_TILEMAP_RES max is 32 because of the shared bitmaps used for LOD tagging.
+ * It is also limited by the maximum thread group size (1024).
+ */
+#define SHADOW_TILEMAP_RES 16
+#define SHADOW_TILEMAP_LOD 4 /* LOG2(SHADOW_TILEMAP_RES) */
+#define SHADOW_TILEMAP_PER_ROW 64
+#define SHADOW_PAGE_COPY_GROUP_SIZE 32
+#define SHADOW_DEPTH_SCAN_GROUP_SIZE 32
+#define SHADOW_AABB_TAG_GROUP_SIZE 64
+#define SHADOW_MAX_TILEMAP 4096
+#define SHADOW_MAX_PAGE 4096
+#define SHADOW_PAGE_PER_ROW 64
+
+#define HIZ_MIP_COUNT 6u
+/* Group size is 2x smaller because we simply copy the level 0. */
+#define HIZ_GROUP_SIZE 1u << (HIZ_MIP_COUNT - 2u)
+
+#define RAYTRACE_GROUP_SIZE 16
+#define RAYTRACE_MAX_TILES (16384 / RAYTRACE_GROUP_SIZE) * (16384 / RAYTRACE_GROUP_SIZE)
+
+/* Minimum visibility size. */
+#define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16
diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc
index 390ab1f3e50..79c0af5bb8b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_engine.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc
@@ -11,38 +11,91 @@
#include "DRW_render.h"
+#include "eevee_instance.hh"
+
+using namespace blender;
+
struct EEVEE_Data {
DrawEngineType *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
- void *instance;
+ eevee::Instance *instance;
};
static void eevee_engine_init(void *vedata)
{
- UNUSED_VARS(vedata);
+ EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata);
+ if (ved->instance == nullptr) {
+ ved->instance = new eevee::Instance();
+ }
+
+ const DRWContextState *ctx_state = DRW_context_state_get();
+ Depsgraph *depsgraph = ctx_state->depsgraph;
+ Scene *scene = ctx_state->scene;
+ View3D *v3d = ctx_state->v3d;
+ const ARegion *region = ctx_state->region;
+ RegionView3D *rv3d = ctx_state->rv3d;
+
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ int2 size = int2(GPU_texture_width(dtxl->color), GPU_texture_height(dtxl->color));
+
+ const DRWView *default_view = DRW_view_default_get();
+
+ Object *camera = nullptr;
+ /* Get render borders. */
+ rcti rect;
+ BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
+ if (v3d) {
+ if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
+ camera = v3d->camera;
+ }
+
+ if (v3d->flag2 & V3D_RENDER_BORDER) {
+ if (camera) {
+ rctf viewborder;
+ /* TODO(fclem) Might be better to get it from DRW. */
+ ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false);
+ float viewborder_sizex = BLI_rctf_size_x(&viewborder);
+ float viewborder_sizey = BLI_rctf_size_y(&viewborder);
+ rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
+ rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
+ rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
+ rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
+ }
+ else {
+ rect.xmin = v3d->render_border.xmin * size[0];
+ rect.ymin = v3d->render_border.ymin * size[1];
+ rect.xmax = v3d->render_border.xmax * size[0];
+ rect.ymax = v3d->render_border.ymax * size[1];
+ }
+ }
+ }
+
+ ved->instance->init(
+ size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
}
static void eevee_draw_scene(void *vedata)
{
- UNUSED_VARS(vedata);
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ reinterpret_cast<EEVEE_Data *>(vedata)->instance->draw_viewport(dfbl);
}
static void eevee_cache_init(void *vedata)
{
- UNUSED_VARS(vedata);
+ reinterpret_cast<EEVEE_Data *>(vedata)->instance->begin_sync();
}
static void eevee_cache_populate(void *vedata, Object *object)
{
- UNUSED_VARS(vedata, object);
+ reinterpret_cast<EEVEE_Data *>(vedata)->instance->object_sync(object);
}
static void eevee_cache_finish(void *vedata)
{
- UNUSED_VARS(vedata);
+ reinterpret_cast<EEVEE_Data *>(vedata)->instance->end_sync();
}
static void eevee_engine_free()
@@ -51,7 +104,7 @@ static void eevee_engine_free()
static void eevee_instance_free(void *instance)
{
- UNUSED_VARS(instance);
+ delete reinterpret_cast<eevee::Instance *>(instance);
}
static void eevee_render_to_image(void *UNUSED(vedata),
diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.h b/source/blender/draw/engines/eevee_next/eevee_engine.h
index 9cee11bbba8..37fc74ad68b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_engine.h
+++ b/source/blender/draw/engines/eevee_next/eevee_engine.h
@@ -7,6 +7,9 @@
#pragma once
+#include "DRW_render.h"
+#include "RE_engine.h"
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc
new file mode 100644
index 00000000000..ce463ef7257
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * An instance contains all structures needed to do a complete render.
+ */
+
+#include "BKE_global.h"
+#include "BKE_object.h"
+#include "BLI_rect.h"
+#include "DEG_depsgraph_query.h"
+#include "DNA_ID.h"
+#include "DNA_lightprobe_types.h"
+#include "DNA_modifier_types.h"
+
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Init
+ *
+ * Init funcions need to be called once at the start of a frame.
+ * Active camera, render extent and enabled render passes are immutable until next init.
+ * This takes care of resizing output buffers and view in case a parameter changed.
+ * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources.
+ * Any attempt to do so will likely produce use after free situations.
+ * \{ */
+
+void Instance::init(const int2 &output_res,
+ const rcti *output_rect,
+ RenderEngine *render_,
+ Depsgraph *depsgraph_,
+ const LightProbe *light_probe_,
+ Object *camera_object_,
+ const RenderLayer *render_layer_,
+ const DRWView *drw_view_,
+ const View3D *v3d_,
+ const RegionView3D *rv3d_)
+{
+ render = render_;
+ depsgraph = depsgraph_;
+ render_layer = render_layer_;
+ drw_view = drw_view_;
+ v3d = v3d_;
+ rv3d = rv3d_;
+
+ update_eval_members();
+
+ main_view.init(output_res);
+}
+
+void Instance::update_eval_members(void)
+{
+ scene = DEG_get_evaluated_scene(depsgraph);
+ view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ // camera_eval_object = (camera_orig_object) ?
+ // DEG_get_evaluated_object(depsgraph, camera_orig_object) :
+ // nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sync
+ *
+ * Sync will gather data from the scene that can change over a time step (i.e: motion steps).
+ * IMPORTANT: xxx.sync() functions area responsible for creating DRW resources (i.e: DRWView) as
+ * well as querying temp texture pool. All DRWPasses should be ready by the end end_sync().
+ * \{ */
+
+void Instance::begin_sync()
+{
+ materials.begin_sync();
+
+ pipelines.sync();
+ main_view.sync();
+ world.sync();
+}
+
+void Instance::object_sync(Object *ob)
+{
+ const bool is_renderable_type = ELEM(ob->type, OB_MESH);
+ const int ob_visibility = DRW_object_visibility_in_active_context(ob);
+ const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
+ (ob->type == OB_MESH);
+ const bool object_is_visible = DRW_object_is_renderable(ob) &&
+ (ob_visibility & OB_VISIBLE_SELF) != 0;
+
+ if (!is_renderable_type || (!partsys_is_visible && !object_is_visible)) {
+ return;
+ }
+
+ ObjectHandle &ob_handle = sync.sync_object(ob);
+
+ if (object_is_visible) {
+ switch (ob->type) {
+ case OB_LAMP:
+ break;
+ case OB_MESH:
+ case OB_CURVES_LEGACY:
+ case OB_SURF:
+ case OB_FONT:
+ case OB_MBALL: {
+ sync.sync_mesh(ob, ob_handle);
+ break;
+ }
+ case OB_VOLUME:
+ break;
+ case OB_CURVES:
+ sync.sync_curves(ob, ob_handle, nullptr);
+ break;
+ case OB_GPENCIL:
+ sync.sync_gpencil(ob, ob_handle);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ob_handle.reset_recalc_flag();
+}
+
+void Instance::end_sync(void)
+{
+}
+
+void Instance::render_sync(void)
+{
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Rendering
+ * \{ */
+
+/**
+ * Conceptually renders one sample per pixel.
+ * Everything based on random sampling should be done here (i.e: DRWViews jitter)
+ **/
+void Instance::render_sample(void)
+{
+ main_view.render();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Interface
+ * \{ */
+
+void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
+{
+}
+
+void Instance::draw_viewport(DefaultFramebufferList *dfbl)
+{
+ render_sample();
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh
new file mode 100644
index 00000000000..0f085b0952a
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * An renderer instance that contains all data to render a full frame.
+ */
+
+#pragma once
+
+#include "BKE_object.h"
+#include "DEG_depsgraph.h"
+#include "DNA_lightprobe_types.h"
+#include "DRW_render.h"
+
+#include "eevee_material.hh"
+#include "eevee_pipeline.hh"
+#include "eevee_shader.hh"
+#include "eevee_sync.hh"
+#include "eevee_view.hh"
+#include "eevee_world.hh"
+
+namespace blender::eevee {
+
+/**
+ * \class Instance
+ * \brief A running instance of the engine.
+ */
+class Instance {
+ public:
+ ShaderModule &shaders;
+ SyncModule sync;
+ MaterialModule materials;
+ PipelineModule pipelines;
+ MainView main_view;
+ World world;
+
+ /** Input data. */
+ Depsgraph *depsgraph;
+ /** Evaluated IDs. */
+ Scene *scene;
+ ViewLayer *view_layer;
+ /** Only available when rendering for final render. */
+ const RenderLayer *render_layer;
+ RenderEngine *render;
+ /** Only available when rendering for viewport. */
+ const DRWView *drw_view;
+ const View3D *v3d;
+ const RegionView3D *rv3d;
+
+ /* Info string displayed at the top of the render / viewport. */
+ char info[64];
+
+ public:
+ Instance()
+ : shaders(*ShaderModule::module_get()),
+ sync(*this),
+ materials(*this),
+ pipelines(*this),
+ main_view(*this),
+ world(*this){};
+ ~Instance(){};
+
+ void init(const int2 &output_res,
+ const rcti *output_rect,
+ RenderEngine *render,
+ Depsgraph *depsgraph,
+ const LightProbe *light_probe_ = nullptr,
+ Object *camera_object = nullptr,
+ const RenderLayer *render_layer = nullptr,
+ const DRWView *drw_view = nullptr,
+ const View3D *v3d = nullptr,
+ const RegionView3D *rv3d = nullptr);
+
+ void begin_sync(void);
+ void object_sync(Object *ob);
+ void end_sync(void);
+
+ void render_sync(void);
+ void render_frame(RenderLayer *render_layer, const char *view_name);
+
+ void draw_viewport(DefaultFramebufferList *dfbl);
+
+ private:
+ void render_sample(void);
+
+ void mesh_sync(Object *ob, ObjectHandle &ob_handle);
+
+ void update_eval_members(void);
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc
new file mode 100644
index 00000000000..f3132185683
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_material.cc
@@ -0,0 +1,320 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#include "DNA_material_types.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_material.h"
+#include "BKE_node.h"
+#include "NOD_shader.h"
+
+#include "eevee_instance.hh"
+
+#include "eevee_material.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Default Material
+ *
+ * \{ */
+
+DefaultSurfaceNodeTree::DefaultSurfaceNodeTree()
+{
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
+ bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_PRINCIPLED);
+ bNode *output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL);
+ bNodeSocket *bsdf_out = nodeFindSocket(bsdf, SOCK_OUT, "BSDF");
+ bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
+ nodeAddLink(ntree, bsdf, bsdf_out, output, output_in);
+ nodeSetActive(ntree, output);
+
+ color_socket_ =
+ (bNodeSocketValueRGBA *)nodeFindSocket(bsdf, SOCK_IN, "Base Color")->default_value;
+ metallic_socket_ =
+ (bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Metallic")->default_value;
+ roughness_socket_ =
+ (bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Roughness")->default_value;
+ specular_socket_ =
+ (bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Specular")->default_value;
+ ntree_ = ntree;
+}
+
+DefaultSurfaceNodeTree::~DefaultSurfaceNodeTree()
+{
+ ntreeFreeEmbeddedTree(ntree_);
+ MEM_SAFE_FREE(ntree_);
+}
+
+/* Configure a default nodetree with the given material. */
+bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma)
+{
+ /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
+ copy_v3_fl3(color_socket_->value, ma->r, ma->g, ma->b);
+ metallic_socket_->value = ma->metallic;
+ roughness_socket_->value = ma->roughness;
+ specular_socket_->value = ma->spec;
+
+ return ntree_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material
+ *
+ * \{ */
+
+MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
+{
+ {
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
+
+ diffuse_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse");
+ diffuse_mat_->nodetree = ntree;
+ diffuse_mat_->use_nodes = true;
+ /* To use the forward pipeline. */
+ diffuse_mat_->blend_method = MA_BM_BLEND;
+
+ bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_DIFFUSE);
+ bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color");
+ copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 0.8f);
+
+ bNode *output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL);
+
+ nodeAddLink(ntree,
+ bsdf,
+ nodeFindSocket(bsdf, SOCK_OUT, "BSDF"),
+ output,
+ nodeFindSocket(output, SOCK_IN, "Surface"));
+
+ nodeSetActive(ntree, output);
+ }
+ {
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
+
+ glossy_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal");
+ glossy_mat_->nodetree = ntree;
+ glossy_mat_->use_nodes = true;
+ /* To use the forward pipeline. */
+ glossy_mat_->blend_method = MA_BM_BLEND;
+
+ bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_GLOSSY);
+ bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color");
+ copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 1.0f);
+ bNodeSocket *roughness = nodeFindSocket(bsdf, SOCK_IN, "Roughness");
+ ((bNodeSocketValueFloat *)roughness->default_value)->value = 0.0f;
+
+ bNode *output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL);
+
+ nodeAddLink(ntree,
+ bsdf,
+ nodeFindSocket(bsdf, SOCK_OUT, "BSDF"),
+ output,
+ nodeFindSocket(output, SOCK_IN, "Surface"));
+
+ nodeSetActive(ntree, output);
+ }
+ {
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
+
+ error_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default error");
+ error_mat_->nodetree = ntree;
+ error_mat_->use_nodes = true;
+
+ /* Use emission and output material to be compatible with both World and Material. */
+ bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
+ bNodeSocket *color = nodeFindSocket(bsdf, SOCK_IN, "Color");
+ copy_v3_fl3(((bNodeSocketValueRGBA *)color->default_value)->value, 1.0f, 0.0f, 1.0f);
+
+ bNode *output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL);
+
+ nodeAddLink(ntree,
+ bsdf,
+ nodeFindSocket(bsdf, SOCK_OUT, "Emission"),
+ output,
+ nodeFindSocket(output, SOCK_IN, "Surface"));
+
+ nodeSetActive(ntree, output);
+ }
+}
+
+MaterialModule::~MaterialModule()
+{
+ for (Material *mat : material_map_.values()) {
+ delete mat;
+ }
+ BKE_id_free(nullptr, glossy_mat_);
+ BKE_id_free(nullptr, diffuse_mat_);
+ BKE_id_free(nullptr, error_mat_);
+}
+
+void MaterialModule::begin_sync(void)
+{
+ queued_shaders_count_ = 0;
+
+ for (Material *mat : material_map_.values()) {
+ mat->init = false;
+ }
+ shader_map_.clear();
+}
+
+MaterialPass MaterialModule::material_pass_get(::Material *blender_mat,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type)
+{
+ bNodeTree *ntree = (blender_mat->use_nodes && blender_mat->nodetree != nullptr) ?
+ blender_mat->nodetree :
+ default_surface_ntree_.nodetree_get(blender_mat);
+
+ MaterialPass matpass;
+ matpass.gpumat = inst_.shaders.material_shader_get(
+ blender_mat, ntree, pipeline_type, geometry_type, true);
+
+ switch (GPU_material_status(matpass.gpumat)) {
+ case GPU_MAT_SUCCESS:
+ break;
+ case GPU_MAT_QUEUED:
+ queued_shaders_count_++;
+ blender_mat = (geometry_type == MAT_GEOM_VOLUME) ? BKE_material_default_volume() :
+ BKE_material_default_surface();
+ matpass.gpumat = inst_.shaders.material_shader_get(
+ blender_mat, blender_mat->nodetree, pipeline_type, geometry_type, false);
+ break;
+ case GPU_MAT_FAILED:
+ default:
+ matpass.gpumat = inst_.shaders.material_shader_get(
+ error_mat_, error_mat_->nodetree, pipeline_type, geometry_type, false);
+ break;
+ }
+ /* Returned material should be ready to be drawn. */
+ BLI_assert(GPU_material_status(matpass.gpumat) == GPU_MAT_SUCCESS);
+
+ if (GPU_material_recalc_flag_get(matpass.gpumat)) {
+ // inst_.sampling.reset();
+ }
+
+ if ((pipeline_type == MAT_PIPE_DEFERRED) &&
+ GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
+ pipeline_type = MAT_PIPE_FORWARD;
+ }
+
+ if ((pipeline_type == MAT_PIPE_FORWARD) &&
+ GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ /* Transparent needs to use one shgroup per object to support reordering. */
+ matpass.shgrp = inst_.pipelines.material_add(blender_mat, matpass.gpumat, pipeline_type);
+ }
+ else {
+ ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type);
+
+ auto add_cb = [&]() -> DRWShadingGroup * {
+ /* First time encountering this shader. Create a shading group. */
+ return inst_.pipelines.material_add(blender_mat, matpass.gpumat, pipeline_type);
+ };
+ DRWShadingGroup *grp = shader_map_.lookup_or_add_cb(shader_key, add_cb);
+
+ if (grp != nullptr) {
+ /* Shading group for this shader already exists. Create a sub one for this material. */
+ /* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the
+ * first "empty" shgroup. This avoids messing the order of subgroups when there is more
+ * nested subgroup (i.e: hair drawing). */
+ /* TODO(fclem) Remove material resource binding from the first group creation. */
+ matpass.shgrp = DRW_shgroup_create_sub(grp);
+ DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat);
+ }
+ }
+
+ return matpass;
+}
+
+Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeometry geometry_type)
+{
+ eMaterialPipeline surface_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? MAT_PIPE_FORWARD :
+ MAT_PIPE_DEFERRED;
+ eMaterialPipeline prepass_pipe = (blender_mat->blend_method == MA_BM_BLEND) ?
+ MAT_PIPE_FORWARD_PREPASS :
+ MAT_PIPE_DEFERRED_PREPASS;
+
+ /* Test */
+ surface_pipe = MAT_PIPE_FORWARD;
+ prepass_pipe = MAT_PIPE_FORWARD_PREPASS;
+
+ MaterialKey material_key(blender_mat, geometry_type, surface_pipe);
+
+ /* TODO allocate in blocks to avoid memory fragmentation. */
+ auto add_cb = [&]() { return new Material(); };
+ Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb);
+
+ /* Forward pipeline needs to use one shgroup per object. */
+ if (mat.init == false || (surface_pipe == MAT_PIPE_FORWARD)) {
+ mat.init = true;
+ /* Order is important for transparent. */
+ mat.prepass = material_pass_get(blender_mat, prepass_pipe, geometry_type);
+ mat.shading = material_pass_get(blender_mat, surface_pipe, geometry_type);
+ if (blender_mat->blend_shadow == MA_BS_NONE) {
+ mat.shadow = MaterialPass();
+ }
+ else {
+ mat.shadow = material_pass_get(blender_mat, MAT_PIPE_SHADOW, geometry_type);
+ }
+
+ mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) &&
+ GPU_material_flag_get(mat.prepass.gpumat,
+ GPU_MATFLAG_TRANSPARENT);
+ }
+ return mat;
+}
+
+/* Return correct material or empty default material if slot is empty. */
+::Material *MaterialModule::material_from_slot(Object *ob, int slot)
+{
+ if (ob->base_flag & BASE_HOLDOUT) {
+ return BKE_material_default_holdout();
+ }
+ ::Material *ma = BKE_object_material_get(ob, slot + 1);
+ if (ma == nullptr) {
+ if (ob->type == OB_VOLUME) {
+ return BKE_material_default_volume();
+ }
+ else {
+ return BKE_material_default_surface();
+ }
+ }
+ return ma;
+}
+
+/* Returned Material references are valid until the next call to this function or
+ * material_get(). */
+MaterialArray &MaterialModule::material_array_get(Object *ob)
+{
+ material_array_.materials.clear();
+ material_array_.gpu_materials.clear();
+
+ const int materials_len = DRW_cache_object_material_count_get(ob);
+
+ for (auto i : IndexRange(materials_len)) {
+ ::Material *blender_mat = material_from_slot(ob, i);
+ Material &mat = material_sync(blender_mat, to_material_geometry(ob));
+ material_array_.materials.append(&mat);
+ material_array_.gpu_materials.append(mat.shading.gpumat);
+ }
+ return material_array_;
+}
+
+/* Returned Material references are valid until the next call to this function or
+ * material_array_get(). */
+Material &MaterialModule::material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type)
+{
+ ::Material *blender_mat = material_from_slot(ob, mat_nr);
+ Material &mat = material_sync(blender_mat, geometry_type);
+ return mat;
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh
new file mode 100644
index 00000000000..0c454128aba
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_material.hh
@@ -0,0 +1,257 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "BLI_map.hh"
+#include "BLI_vector.hh"
+#include "GPU_material.h"
+
+#include "eevee_sync.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name MaterialKey
+ *
+ * \{ */
+
+enum eMaterialPipeline {
+ MAT_PIPE_DEFERRED = 0,
+ MAT_PIPE_FORWARD = 1,
+ MAT_PIPE_DEFERRED_PREPASS = 2,
+ MAT_PIPE_FORWARD_PREPASS = 3,
+ MAT_PIPE_VOLUME = 4,
+ MAT_PIPE_SHADOW = 5,
+};
+
+enum eMaterialGeometry {
+ MAT_GEOM_MESH = 0,
+ MAT_GEOM_CURVES = 1,
+ MAT_GEOM_GPENCIL = 2,
+ MAT_GEOM_VOLUME = 3,
+ MAT_GEOM_WORLD = 4,
+};
+
+static inline void material_type_from_shader_uuid(uint64_t shader_uuid,
+ eMaterialPipeline &pipeline_type,
+ eMaterialGeometry &geometry_type)
+{
+ const uint64_t geometry_mask = ((1u << 3u) - 1u);
+ const uint64_t pipeline_mask = ((1u << 3u) - 1u);
+ geometry_type = static_cast<eMaterialGeometry>(shader_uuid & geometry_mask);
+ pipeline_type = static_cast<eMaterialPipeline>((shader_uuid >> 3u) & pipeline_mask);
+}
+
+static inline uint64_t shader_uuid_from_material_type(eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type)
+{
+ return geometry_type | (pipeline_type << 3);
+}
+
+ENUM_OPERATORS(eClosureBits, CLOSURE_AMBIENT_OCCLUSION)
+
+static inline eClosureBits shader_closure_bits_from_flag(const GPUMaterial *gpumat)
+{
+ eClosureBits closure_bits = eClosureBits(0);
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE)) {
+ closure_bits |= CLOSURE_DIFFUSE;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ closure_bits |= CLOSURE_TRANSPARENCY;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_EMISSION)) {
+ closure_bits |= CLOSURE_EMISSION;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY)) {
+ closure_bits |= CLOSURE_REFLECTION;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE)) {
+ closure_bits |= CLOSURE_SSS;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT)) {
+ closure_bits |= CLOSURE_REFRACTION;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_HOLDOUT)) {
+ closure_bits |= CLOSURE_HOLDOUT;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_AO)) {
+ closure_bits |= CLOSURE_AMBIENT_OCCLUSION;
+ }
+ return closure_bits;
+}
+
+static inline eMaterialGeometry to_material_geometry(const Object *ob)
+{
+ switch (ob->type) {
+ case OB_CURVES:
+ return MAT_GEOM_CURVES;
+ case OB_VOLUME:
+ return MAT_GEOM_VOLUME;
+ case OB_GPENCIL:
+ return MAT_GEOM_GPENCIL;
+ default:
+ return MAT_GEOM_MESH;
+ }
+}
+
+/** Unique key to identify each material in the hashmap. */
+struct MaterialKey {
+ Material *mat;
+ uint64_t options;
+
+ MaterialKey(::Material *mat_, eMaterialGeometry geometry, eMaterialPipeline surface_pipeline)
+ : mat(mat_)
+ {
+ options = shader_uuid_from_material_type(surface_pipeline, geometry);
+ }
+
+ uint64_t hash(void) const
+ {
+ BLI_assert(options < sizeof(*mat));
+ return (uint64_t)mat + options;
+ }
+
+ bool operator<(const MaterialKey &k) const
+ {
+ return (mat < k.mat) || (options < k.options);
+ }
+
+ bool operator==(const MaterialKey &k) const
+ {
+ return (mat == k.mat) && (options == k.options);
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ShaderKey
+ *
+ * \{ */
+
+struct ShaderKey {
+ GPUShader *shader;
+ uint64_t options;
+
+ ShaderKey(GPUMaterial *gpumat, eMaterialGeometry geometry, eMaterialPipeline pipeline)
+ {
+ shader = GPU_material_get_shader(gpumat);
+ options = shader_uuid_from_material_type(pipeline, geometry);
+ options = (options << 16u) | shader_closure_bits_from_flag(gpumat);
+ }
+
+ uint64_t hash(void) const
+ {
+ return (uint64_t)shader + options;
+ }
+
+ bool operator<(const ShaderKey &k) const
+ {
+ return (shader == k.shader) ? (options < k.options) : (shader < k.shader);
+ }
+
+ bool operator==(const ShaderKey &k) const
+ {
+ return (shader == k.shader) && (options == k.options);
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Default Material Nodetree
+ *
+ * In order to support materials without nodetree we reuse and configure a standalone nodetree that
+ * we pass for shader generation. The GPUMaterial is still stored inside the Material even if
+ * it does not use the same nodetree.
+ *
+ * \{ */
+
+class DefaultSurfaceNodeTree {
+ private:
+ bNodeTree *ntree_;
+ bNodeSocketValueRGBA *color_socket_;
+ bNodeSocketValueFloat *metallic_socket_;
+ bNodeSocketValueFloat *roughness_socket_;
+ bNodeSocketValueFloat *specular_socket_;
+
+ public:
+ DefaultSurfaceNodeTree();
+ ~DefaultSurfaceNodeTree();
+
+ bNodeTree *nodetree_get(::Material *ma);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Material
+ *
+ * \{ */
+
+struct MaterialPass {
+ GPUMaterial *gpumat = nullptr;
+ DRWShadingGroup *shgrp = nullptr;
+};
+
+struct Material {
+ bool init = false;
+ bool is_alpha_blend_transparent;
+ MaterialPass shadow, shading, prepass;
+};
+
+struct MaterialArray {
+ Vector<Material *> materials;
+ Vector<GPUMaterial *> gpu_materials;
+};
+
+class MaterialModule {
+ public:
+ ::Material *diffuse_mat_;
+ ::Material *glossy_mat_;
+
+ private:
+ Instance &inst_;
+
+ Map<MaterialKey, Material *> material_map_;
+ Map<ShaderKey, DRWShadingGroup *> shader_map_;
+
+ MaterialArray material_array_;
+
+ DefaultSurfaceNodeTree default_surface_ntree_;
+
+ ::Material *error_mat_;
+
+ int64_t queued_shaders_count_ = 0;
+
+ public:
+ MaterialModule(Instance &inst);
+ ~MaterialModule();
+
+ void begin_sync(void);
+
+ MaterialArray &material_array_get(Object *ob);
+ Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type);
+
+ private:
+ Material &material_sync(::Material *blender_mat, eMaterialGeometry geometry_type);
+
+ ::Material *material_from_slot(Object *ob, int slot);
+ MaterialPass material_pass_get(::Material *blender_mat,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type);
+};
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
new file mode 100644
index 00000000000..6d3a00160a5
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shading passes contain drawcalls specific to shading pipelines.
+ * They are to be shared across views.
+ * This file is only for shading passes. Other passes are declared in their own module.
+ */
+
+#include "eevee_instance.hh"
+
+#include "eevee_pipeline.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name World Pipeline
+ *
+ * Used to draw background.
+ * \{ */
+
+void WorldPipeline::sync(GPUMaterial *gpumat)
+{
+ DRWState state = DRW_STATE_WRITE_COLOR;
+ world_ps_ = DRW_pass_create("World", state);
+
+ /* Push a matrix at the same location as the camera. */
+ float4x4 camera_mat = float4x4::identity();
+ // copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]);
+
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, world_ps_);
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
+ DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat.ptr());
+}
+
+void WorldPipeline::render(void)
+{
+ DRW_draw_pass(world_ps_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Forward Pass
+ *
+ * NPR materials (using Closure to RGBA) or material using ALPHA_BLEND.
+ * \{ */
+
+void ForwardPipeline::sync(void)
+{
+ {
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
+ prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state);
+
+ state |= DRW_STATE_CULL_BACK;
+ prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state);
+
+ DRW_pass_link(prepass_ps_, prepass_culled_ps_);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
+ opaque_ps_ = DRW_pass_create("Forward.Opaque", state);
+
+ state |= DRW_STATE_CULL_BACK;
+ opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state);
+
+ DRW_pass_link(opaque_ps_, opaque_culled_ps_);
+ }
+ {
+ DRWState state = DRW_STATE_DEPTH_LESS_EQUAL;
+ transparent_ps_ = DRW_pass_create("Forward.Transparent", state);
+ }
+}
+
+DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
+ // LightModule &lights = inst_.lights;
+ // LightProbeModule &lightprobes = inst_.lightprobes;
+ // RaytracingModule &raytracing = inst_.raytracing;
+ // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
+ // lights.shgroup_resources(grp);
+ // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
+ // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
+ // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
+ // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
+ // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
+ // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
+ /* TODO(fclem): Make this only needed if material uses it ... somehow. */
+ // if (true) {
+ // DRW_shgroup_uniform_texture_ref(
+ // grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
+ // }
+ // if (raytracing.enabled()) {
+ // DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_data);
+ // DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_data);
+ // DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_data);
+ // DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_screen_radiance_tx_,
+ // no_interp);
+ // }
+ // if (raytracing.enabled()) {
+ // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
+ // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get());
+ // }
+ return grp;
+}
+
+DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
+ prepass_ps_;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
+ return grp;
+}
+
+DRWShadingGroup *ForwardPipeline::material_transparent_add(::Material *blender_mat,
+ GPUMaterial *gpumat)
+{
+ // LightModule &lights = inst_.lights;
+ // LightProbeModule &lightprobes = inst_.lightprobes;
+ // RaytracingModule &raytracing = inst_.raytracing;
+ // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
+ // lights.shgroup_resources(grp);
+ // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
+ // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
+ // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
+ // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
+ // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
+ // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+ // DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
+ /* TODO(fclem): Make this only needed if material uses it ... somehow. */
+ // if (true) {
+ // DRW_shgroup_uniform_texture_ref(
+ // grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
+ // }
+ // if (raytracing.enabled()) {
+ // DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_data);
+ // DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_data);
+ // DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_data);
+ // DRW_shgroup_uniform_texture_ref_ex(
+ // grp, "rt_radiance_tx", &input_screen_radiance_tx_, no_interp);
+ // }
+ // if (raytracing.enabled()) {
+ // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
+ // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get());
+ // }
+
+ DRWState state_disable = DRW_STATE_WRITE_DEPTH;
+ DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
+ if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
+ state_enable |= DRW_STATE_CULL_BACK;
+ }
+ DRW_shgroup_state_disable(grp, state_disable);
+ DRW_shgroup_state_enable(grp, state_enable);
+ return grp;
+}
+
+DRWShadingGroup *ForwardPipeline::prepass_transparent_add(::Material *blender_mat,
+ GPUMaterial *gpumat)
+{
+ if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) {
+ return nullptr;
+ }
+
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
+
+ DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
+ DRWState state_enable = DRW_STATE_WRITE_DEPTH;
+ if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
+ state_enable |= DRW_STATE_CULL_BACK;
+ }
+ DRW_shgroup_state_disable(grp, state_disable);
+ DRW_shgroup_state_enable(grp, state_enable);
+ return grp;
+}
+
+void ForwardPipeline::render(const DRWView *view,
+ GPUTexture *depth_tx,
+ GPUTexture *UNUSED(combined_tx))
+{
+ // HiZBuffer &hiz = inst_.hiz_front;
+
+ DRW_stats_group_start("ForwardOpaque");
+
+ DRW_draw_pass(prepass_ps_);
+ // hiz.set_dirty();
+
+ // if (inst_.raytracing.enabled()) {
+ // rt_buffer.radiance_copy(combined_tx);
+ // hiz.update(depth_tx);
+ // }
+
+ // inst_.shadows.set_view(view, depth_tx);
+
+ DRW_draw_pass(opaque_ps_);
+
+ DRW_stats_group_end();
+
+ DRW_stats_group_start("ForwardTransparent");
+ /* TODO(fclem) This is suboptimal. We could sort during sync. */
+ /* FIXME(fclem) This wont work for panoramic, where we need
+ * to sort by distance to camera, not by z. */
+ // DRW_pass_sort_shgroup_z(transparent_ps_);
+ // DRW_draw_pass(transparent_ps_);
+ DRW_stats_group_end();
+
+ // if (inst_.raytracing.enabled()) {
+ // gbuffer.ray_radiance_tx.release();
+ // }
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh
new file mode 100644
index 00000000000..14eac812e55
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shading passes contain drawcalls specific to shading pipelines.
+ * They are shared across views.
+ * This file is only for shading passes. Other passes are declared in their own module.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+/* TODO(fclem): Move it to GPU/DRAW. */
+#include "../eevee/eevee_lut.h"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name World Pipeline
+ *
+ * Render world values.
+ * \{ */
+
+class WorldPipeline {
+ private:
+ Instance &inst_;
+
+ DRWPass *world_ps_ = nullptr;
+
+ public:
+ WorldPipeline(Instance &inst) : inst_(inst){};
+
+ void sync(GPUMaterial *gpumat);
+ void render(void);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Forward Pass
+ *
+ * Handles alpha blended surfaces and NPR materials (using Closure to RGBA).
+ * \{ */
+
+class ForwardPipeline {
+ private:
+ Instance &inst_;
+
+ DRWPass *prepass_ps_ = nullptr;
+ DRWPass *prepass_culled_ps_ = nullptr;
+ DRWPass *opaque_ps_ = nullptr;
+ DRWPass *opaque_culled_ps_ = nullptr;
+ DRWPass *transparent_ps_ = nullptr;
+
+ // GPUTexture *input_screen_radiance_tx_ = nullptr;
+
+ public:
+ ForwardPipeline(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+
+ DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat)
+ {
+ return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
+ material_transparent_add(blender_mat, gpumat) :
+ material_opaque_add(blender_mat, gpumat);
+ }
+
+ DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
+ {
+ return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
+ prepass_transparent_add(blender_mat, gpumat) :
+ prepass_opaque_add(blender_mat, gpumat);
+ }
+
+ DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
+
+ void render(const DRWView *view, GPUTexture *depth_tx, GPUTexture *combined_tx);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility texture
+ *
+ * 64x64 2D array texture containing LUT tables and blue noises.
+ * \{ */
+
+class UtilityTexture : public Texture {
+ struct Layer {
+ float data[UTIL_TEX_SIZE * UTIL_TEX_SIZE][4];
+ };
+
+ static constexpr int lut_size = UTIL_TEX_SIZE;
+ static constexpr int lut_size_sqr = lut_size * lut_size;
+ static constexpr int layer_count = 4 + UTIL_BTDF_LAYER_COUNT;
+
+ public:
+ UtilityTexture() : Texture("UtilityTx", GPU_RGBA16F, int2(lut_size), layer_count, nullptr)
+ {
+#ifdef RUNTIME_LUT_CREATION
+ float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(lut_size);
+ float(*btdf_ggx_lut)[lut_size_sqr * 2] = (float(*)[lut_size_sqr * 2])
+ EEVEE_lut_update_ggx_btdf(lut_size, UTIL_BTDF_LAYER_COUNT);
+#else
+ const float *bsdf_ggx_lut = bsdf_split_sum_ggx;
+ const float(*btdf_ggx_lut)[lut_size_sqr * 2] = btdf_split_sum_ggx;
+#endif
+
+ Vector<Layer> data(layer_count);
+ {
+ Layer &layer = data[UTIL_BLUE_NOISE_LAYER];
+ memcpy(layer.data, blue_noise, sizeof(layer));
+ }
+ {
+ Layer &layer = data[UTIL_LTC_MAT_LAYER];
+ memcpy(layer.data, ltc_mat_ggx, sizeof(layer));
+ }
+ {
+ Layer &layer = data[UTIL_LTC_MAG_LAYER];
+ for (auto i : IndexRange(lut_size_sqr)) {
+ layer.data[i][0] = bsdf_ggx_lut[i * 2 + 0];
+ layer.data[i][1] = bsdf_ggx_lut[i * 2 + 1];
+ layer.data[i][2] = ltc_mag_ggx[i * 2 + 0];
+ layer.data[i][3] = ltc_mag_ggx[i * 2 + 1];
+ }
+ BLI_assert(UTIL_LTC_MAG_LAYER == UTIL_BSDF_LAYER);
+ }
+ {
+ Layer &layer = data[UTIL_DISK_INTEGRAL_LAYER];
+ for (auto i : IndexRange(lut_size_sqr)) {
+ layer.data[i][UTIL_DISK_INTEGRAL_COMP] = ltc_disk_integral[i];
+ }
+ }
+ {
+ for (auto layer_id : IndexRange(16)) {
+ Layer &layer = data[3 + layer_id];
+ for (auto i : IndexRange(lut_size_sqr)) {
+ layer.data[i][0] = btdf_ggx_lut[layer_id][i * 2 + 0];
+ layer.data[i][1] = btdf_ggx_lut[layer_id][i * 2 + 1];
+ }
+ }
+ }
+ GPU_texture_update_mipmap(*this, 0, GPU_DATA_FLOAT, data.data());
+ }
+
+ ~UtilityTexture(){};
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pipelines
+ *
+ * Contains Shading passes. Shared between views. Objects will subscribe to at least one of them.
+ * \{ */
+
+class PipelineModule {
+ public:
+ WorldPipeline world;
+ // DeferredPipeline deferred;
+ ForwardPipeline forward;
+ // ShadowPipeline shadow;
+ // VelocityPipeline velocity;
+
+ UtilityTexture utility_tx;
+
+ public:
+ PipelineModule(Instance &inst) : world(inst), forward(inst){};
+
+ void sync()
+ {
+ // deferred.sync();
+ forward.sync();
+ // shadow.sync();
+ // velocity.sync();
+ }
+
+ DRWShadingGroup *material_add(::Material *blender_mat,
+ GPUMaterial *gpumat,
+ eMaterialPipeline pipeline_type)
+ {
+ switch (pipeline_type) {
+ case MAT_PIPE_DEFERRED_PREPASS:
+ // return deferred.prepass_add(blender_mat, gpumat);
+ break;
+ case MAT_PIPE_FORWARD_PREPASS:
+ return forward.prepass_add(blender_mat, gpumat);
+ case MAT_PIPE_DEFERRED:
+ // return deferred.material_add(blender_mat, gpumat);
+ break;
+ case MAT_PIPE_FORWARD:
+ return forward.material_add(blender_mat, gpumat);
+ case MAT_PIPE_VOLUME:
+ /* TODO(fclem) volume pass. */
+ return nullptr;
+ case MAT_PIPE_SHADOW:
+ // return shadow.material_add(blender_mat, gpumat);
+ break;
+ }
+ return nullptr;
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
new file mode 100644
index 00000000000..1ebf387f32b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -0,0 +1,375 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shader module that manage shader libraries, deferred compilation,
+ * and static shader usage.
+ */
+
+#include "gpu_shader_create_info.hh"
+
+#include "eevee_shader.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ *
+ * \{ */
+
+ShaderModule *ShaderModule::g_shader_module = nullptr;
+
+ShaderModule *ShaderModule::module_get()
+{
+ if (g_shader_module == nullptr) {
+ /* TODO(fclem) threadsafety. */
+ g_shader_module = new ShaderModule();
+ }
+ return g_shader_module;
+}
+
+void ShaderModule::module_free()
+{
+ if (g_shader_module != nullptr) {
+ /* TODO(fclem) threadsafety. */
+ delete g_shader_module;
+ g_shader_module = nullptr;
+ }
+}
+
+ShaderModule::ShaderModule()
+{
+ for (GPUShader *&shader : shaders_) {
+ shader = nullptr;
+ }
+
+#ifdef DEBUG
+ /* Ensure all shader are described. */
+ for (auto i : IndexRange(MAX_SHADER_TYPE)) {
+ const char *name = static_shader_create_info_name_get(eShaderType(i));
+ if (name == nullptr) {
+ std::cerr << "EEVEE: Missing case for eShaderType(" << i
+ << ") in static_shader_create_info_name_get().";
+ BLI_assert(0);
+ }
+ const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name);
+ BLI_assert_msg(create_info != nullptr, "EEVEE: Missing create info for static shader.");
+ }
+#endif
+}
+
+ShaderModule::~ShaderModule()
+{
+ for (GPUShader *&shader : shaders_) {
+ DRW_SHADER_FREE_SAFE(shader);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Static shaders
+ *
+ * \{ */
+
+const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
+{
+ switch (shader_type) {
+ /* To avoid compiler warning about missing case. */
+ case MAX_SHADER_TYPE:
+ return "";
+ }
+ return "";
+}
+
+GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
+{
+ if (shaders_[shader_type] == nullptr) {
+ const char *shader_name = static_shader_create_info_name_get(shader_type);
+
+ shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name);
+
+ if (shaders_[shader_type] == nullptr) {
+ fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", shader_name);
+ }
+ BLI_assert(shaders_[shader_type] != nullptr);
+ }
+ return shaders_[shader_type];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPU Materials
+ *
+ * \{ */
+
+void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOutput *codegen_)
+{
+ using namespace blender::gpu::shader;
+
+ uint64_t shader_uuid = GPU_material_uuid_get(gpumat);
+
+ eMaterialPipeline pipeline_type;
+ eMaterialGeometry geometry_type;
+ material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type);
+
+ GPUCodegenOutput &codegen = *codegen_;
+ ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
+
+ info.auto_resource_location(true);
+
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ info.define("MAT_TRANSPARENT");
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC)) {
+ switch (geometry_type) {
+ case MAT_GEOM_MESH:
+ /* Support using gpu builtin barycentrics. */
+ info.define("USE_BARYCENTRICS");
+ info.builtins(BuiltinBits::BARYCENTRIC_COORD);
+ break;
+ case MAT_GEOM_CURVES:
+ /* Support using one vec2 attribute. See #hair_get_barycentric(). */
+ info.define("USE_BARYCENTRICS");
+ break;
+ default:
+ /* No support */
+ break;
+ }
+ }
+
+ std::stringstream global_vars;
+ switch (geometry_type) {
+ case MAT_GEOM_MESH:
+ /** Noop. */
+ break;
+ case MAT_GEOM_CURVES:
+ /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */
+ for (auto &input : info.vertex_inputs_) {
+ info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH);
+ }
+ info.vertex_inputs_.clear();
+ break;
+ case MAT_GEOM_WORLD:
+ /**
+ * Only orco layer is supported by world and it is procedurally generated. These are here to
+ * make the attribs_load function calls valids.
+ */
+ ATTR_FALLTHROUGH;
+ case MAT_GEOM_GPENCIL:
+ /**
+ * Only one uv and one color attribute layer are supported by gpencil objects and they are
+ * already declared in another createInfo. These are here to make the attribs_load
+ * function calls valids.
+ */
+ for (auto &input : info.vertex_inputs_) {
+ global_vars << input.type << " " << input.name << ";\n";
+ }
+ info.vertex_inputs_.clear();
+ break;
+ case MAT_GEOM_VOLUME:
+ /** No attributes supported. */
+ info.vertex_inputs_.clear();
+ break;
+ }
+
+ const bool do_fragment_attrib_load = (geometry_type == MAT_GEOM_WORLD);
+
+ if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
+ /* Codegen outputs only one interface. */
+ const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first();
+ /* Globals the attrib_load() can write to when it is in the fragment shader. */
+ global_vars << "struct " << iface.name << " {\n";
+ for (auto &inout : iface.inouts) {
+ global_vars << " " << inout.type << " " << inout.name << ";\n";
+ }
+ global_vars << "};\n";
+ global_vars << iface.name << " " << iface.instance_name << ";\n";
+
+ info.vertex_out_interfaces_.clear();
+ }
+
+ std::stringstream attr_load;
+ attr_load << "void attrib_load()\n";
+ attr_load << "{\n";
+ attr_load << ((codegen.attr_load) ? codegen.attr_load : "");
+ attr_load << "}\n\n";
+
+ std::stringstream vert_gen, frag_gen;
+
+ if (do_fragment_attrib_load) {
+ frag_gen << global_vars.str() << attr_load.str();
+ }
+ else {
+ vert_gen << global_vars.str() << attr_load.str();
+ }
+
+ {
+ /* Only mesh and curves support displacement for now. */
+ if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_CURVES)) {
+ vert_gen << "vec3 nodetree_displacement()\n";
+ vert_gen << "{\n";
+ vert_gen << ((codegen.displacement) ? codegen.displacement : "return vec3(0);\n");
+ vert_gen << "}\n\n";
+ }
+
+ info.vertex_source_generated = vert_gen.str();
+ }
+
+ {
+ frag_gen << ((codegen.material_functions) ? codegen.material_functions : "\n");
+
+ if (codegen.displacement) {
+ /* Bump displacement. Needed to recompute normals after displacement. */
+ info.define("MAT_DISPLACEMENT_BUMP");
+
+ frag_gen << "vec3 nodetree_displacement()\n";
+ frag_gen << "{\n";
+ frag_gen << codegen.displacement;
+ frag_gen << "}\n\n";
+ }
+
+ frag_gen << "Closure nodetree_surface()\n";
+ frag_gen << "{\n";
+ frag_gen << " closure_weights_reset();\n";
+ frag_gen << ((codegen.surface) ? codegen.surface : "return Closure(0);\n");
+ frag_gen << "}\n\n";
+
+ frag_gen << "Closure nodetree_volume()\n";
+ frag_gen << "{\n";
+ frag_gen << " closure_weights_reset();\n";
+ frag_gen << ((codegen.volume) ? codegen.volume : "return Closure(0);\n");
+ frag_gen << "}\n\n";
+
+ frag_gen << "float nodetree_thickness()\n";
+ frag_gen << "{\n";
+ /* TODO(fclem): Better default. */
+ frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.1;\n");
+ frag_gen << "}\n\n";
+
+ info.fragment_source_generated = frag_gen.str();
+ }
+
+ /* Geometry Info. */
+ switch (geometry_type) {
+ case MAT_GEOM_WORLD:
+ info.additional_info("eevee_geom_world");
+ break;
+ case MAT_GEOM_VOLUME:
+ info.additional_info("eevee_geom_volume");
+ break;
+ case MAT_GEOM_GPENCIL:
+ info.additional_info("eevee_geom_gpencil");
+ break;
+ case MAT_GEOM_CURVES:
+ info.additional_info("eevee_geom_hair");
+ break;
+ case MAT_GEOM_MESH:
+ default:
+ info.additional_info("eevee_geom_mesh");
+ break;
+ }
+
+ /* Pipeline Info. */
+ switch (geometry_type) {
+ case MAT_GEOM_WORLD:
+ info.additional_info("eevee_surf_world");
+ break;
+ case MAT_GEOM_VOLUME:
+ break;
+ default:
+ switch (pipeline_type) {
+ case MAT_PIPE_FORWARD_PREPASS:
+ case MAT_PIPE_DEFERRED_PREPASS:
+ case MAT_PIPE_SHADOW:
+ info.additional_info("eevee_surf_depth");
+ break;
+ case MAT_PIPE_DEFERRED:
+ info.additional_info("eevee_surf_deferred");
+ break;
+ case MAT_PIPE_FORWARD:
+ info.additional_info("eevee_surf_forward");
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ break;
+ }
+}
+
+/* WATCH: This can be called from another thread! Needs to not touch the shader module in any
+ * thread unsafe manner. */
+static void codegen_callback(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen)
+{
+ reinterpret_cast<ShaderModule *>(thunk)->material_create_info_ammend(mat, codegen);
+}
+
+GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool deferred_compilation)
+{
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+
+ return DRW_shader_from_material(
+ blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this);
+}
+
+GPUMaterial *ShaderModule::world_shader_get(::World *blender_world, struct bNodeTree *nodetree)
+{
+ eMaterialPipeline pipeline_type = MAT_PIPE_DEFERRED; /* Unused. */
+ eMaterialGeometry geometry_type = MAT_GEOM_WORLD;
+
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+ bool deferred_compilation = false;
+
+ return DRW_shader_from_world(blender_world,
+ nodetree,
+ shader_uuid,
+ is_volume,
+ deferred_compilation,
+ codegen_callback,
+ this);
+}
+
+/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of
+ * materials and call GPU_material_free on it to update the material. */
+GPUMaterial *ShaderModule::material_shader_get(const char *name,
+ ListBase &materials,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool is_lookdev)
+{
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+
+ GPUMaterial *gpumat = GPU_material_from_nodetree(nullptr,
+ nullptr,
+ nodetree,
+ &materials,
+ name,
+ shader_uuid,
+ is_volume,
+ is_lookdev,
+ codegen_callback,
+ this);
+ GPU_material_status_set(gpumat, GPU_MAT_QUEUED);
+ GPU_material_compile(gpumat);
+ return gpumat;
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh
new file mode 100644
index 00000000000..ba7c97b3b6a
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shader module that manage shader libraries, deferred compilation,
+ * and static shader usage.
+ */
+
+#pragma once
+
+#include <array>
+#include <string>
+
+#include "BLI_string_ref.hh"
+#include "DRW_render.h"
+#include "GPU_material.h"
+#include "GPU_shader.h"
+
+#include "eevee_material.hh"
+#include "eevee_sync.hh"
+
+namespace blender::eevee {
+
+/* Keep alphabetical order and clean prefix. */
+enum eShaderType {
+ MAX_SHADER_TYPE = 0,
+};
+
+/**
+ * Shader module. shared between instances.
+ */
+class ShaderModule {
+ private:
+ std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
+
+ /** Shared shader module accross all engine instances. */
+ static ShaderModule *g_shader_module;
+
+ public:
+ ShaderModule();
+ ~ShaderModule();
+
+ GPUShader *static_shader_get(eShaderType shader_type);
+ GPUMaterial *material_shader_get(::Material *blender_mat,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool deferred_compilation);
+ GPUMaterial *world_shader_get(::World *blender_world, struct bNodeTree *nodetree);
+ GPUMaterial *material_shader_get(const char *name,
+ ListBase &materials,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool is_lookdev);
+
+ void material_create_info_ammend(GPUMaterial *mat, GPUCodegenOutput *codegen);
+
+ /** Only to be used by Instance constructor. */
+ static ShaderModule *module_get();
+ static void module_free();
+
+ private:
+ const char *static_shader_create_info_name_get(eShaderType shader_type);
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
new file mode 100644
index 00000000000..2c539339952
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
@@ -0,0 +1,86 @@
+
+/**
+ * Shared structures, enums & defines between C++ and GLSL.
+ * Can also include some math functions but they need to be simple enough to be valid in both
+ * language.
+ */
+
+#ifndef USE_GPU_SHADER_CREATE_INFO
+# pragma once
+
+# include "BLI_memory_utils.hh"
+# include "DRW_gpu_wrapper.hh"
+
+// # include "eevee_defines.hh"
+
+# include "GPU_shader_shared.h"
+
+namespace blender::eevee {
+
+using draw::Framebuffer;
+using draw::Texture;
+using draw::TextureFromPool;
+
+#endif
+
+#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
+
+/* -------------------------------------------------------------------- */
+/** \name Raytracing
+ * \{ */
+
+enum eClosureBits : uint32_t {
+ /** NOTE: Theses are used as stencil bits. So we are limited to 8bits. */
+ CLOSURE_DIFFUSE = (1u << 0u),
+ CLOSURE_SSS = (1u << 1u),
+ CLOSURE_REFLECTION = (1u << 2u),
+ CLOSURE_REFRACTION = (1u << 3u),
+ /* Non-stencil bits. */
+ CLOSURE_TRANSPARENCY = (1u << 8u),
+ CLOSURE_EMISSION = (1u << 9u),
+ CLOSURE_HOLDOUT = (1u << 10u),
+ CLOSURE_VOLUME = (1u << 11u),
+ CLOSURE_AMBIENT_OCCLUSION = (1u << 12u),
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility Texture
+ * \{ */
+
+#define UTIL_TEX_SIZE 64
+#define UTIL_BTDF_LAYER_COUNT 16
+/* Scale and bias to avoid interpolation of the border pixel.
+ * Remap UVs to the border pixels centers. */
+#define UTIL_TEX_UV_SCALE ((UTIL_TEX_SIZE - 1.0f) / UTIL_TEX_SIZE)
+#define UTIL_TEX_UV_BIAS (0.5f / UTIL_TEX_SIZE)
+
+#define UTIL_BLUE_NOISE_LAYER 0
+#define UTIL_LTC_MAT_LAYER 1
+#define UTIL_LTC_MAG_LAYER 2
+#define UTIL_BSDF_LAYER 2
+#define UTIL_BTDF_LAYER 3
+#define UTIL_DISK_INTEGRAL_LAYER 3
+#define UTIL_DISK_INTEGRAL_COMP 2
+
+#ifndef __cplusplus
+/* Fetch texel. Wrapping if above range. */
+float4 utility_tx_fetch(sampler2DArray util_tx, float2 texel, float layer)
+{
+ return texelFetch(util_tx, int3(int2(texel) % UTIL_TEX_SIZE, layer), 0);
+}
+
+/* Sample at uv position. Filtered & Wrapping enabled. */
+float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
+{
+ return textureLod(util_tx, float3(uv, layer), 0.0);
+}
+#endif
+
+/** \} */
+
+#ifdef __cplusplus
+
+} // namespace blender::eevee
+#endif
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc
new file mode 100644
index 00000000000..c0dba146f7e
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc
@@ -0,0 +1,316 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Converts the different renderable object types to drawcalls.
+ */
+
+#include "eevee_engine.h"
+
+#include "BKE_gpencil.h"
+#include "BKE_object.h"
+#include "DEG_depsgraph_query.h"
+#include "DNA_curves_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_particle_types.h"
+
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Draw Data
+ *
+ * \{ */
+
+static void draw_data_init_cb(struct DrawData *dd)
+{
+ /* Object has just been created or was never evaluated by the engine. */
+ dd->recalc = ID_RECALC_ALL;
+}
+
+ObjectHandle &SyncModule::sync_object(Object *ob)
+{
+ DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_next_type;
+ struct DrawData *dd = DRW_drawdata_ensure(
+ (ID *)ob, owner, sizeof(eevee::ObjectHandle), draw_data_init_cb, nullptr);
+ ObjectHandle &eevee_dd = *reinterpret_cast<ObjectHandle *>(dd);
+
+ if (eevee_dd.object_key.ob == nullptr) {
+ eevee_dd.object_key = ObjectKey(ob);
+ }
+
+ const int recalc_flags = ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_SHADING |
+ ID_RECALC_GEOMETRY;
+ if ((eevee_dd.recalc & recalc_flags) != 0) {
+ // inst_.sampling.reset();
+ UNUSED_VARS(inst_);
+ }
+
+ return eevee_dd;
+}
+
+WorldHandle &SyncModule::sync_world(::World *world)
+{
+ DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_next_type;
+ struct DrawData *dd = DRW_drawdata_ensure(
+ (ID *)world, owner, sizeof(eevee::WorldHandle), draw_data_init_cb, nullptr);
+ WorldHandle &eevee_dd = *reinterpret_cast<WorldHandle *>(dd);
+
+ const int recalc_flags = ID_RECALC_ALL;
+ if ((eevee_dd.recalc & recalc_flags) != 0) {
+ // inst_.sampling.reset();
+ }
+ return eevee_dd;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Common
+ * \{ */
+
+static inline void shgroup_geometry_call(DRWShadingGroup *grp,
+ Object *ob,
+ GPUBatch *geom,
+ int v_first = -1,
+ int v_count = -1,
+ bool use_instancing = false)
+{
+ if (grp == nullptr) {
+ return;
+ }
+
+ if (v_first == -1) {
+ DRW_shgroup_call(grp, geom, ob);
+ }
+ else if (use_instancing) {
+ DRW_shgroup_call_instance_range(grp, ob, geom, v_first, v_count);
+ }
+ else {
+ DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh
+ * \{ */
+
+void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle)
+{
+ MaterialArray &material_array = inst_.materials.material_array_get(ob);
+
+ GPUBatch **mat_geom = DRW_cache_object_surface_material_get(
+ ob, material_array.gpu_materials.data(), material_array.gpu_materials.size());
+
+ if (mat_geom == nullptr) {
+ return;
+ }
+
+ bool is_shadow_caster = false;
+ bool is_alpha_blend = false;
+ for (auto i : material_array.gpu_materials.index_range()) {
+ GPUBatch *geom = mat_geom[i];
+ if (geom == nullptr) {
+ continue;
+ }
+ Material *material = material_array.materials[i];
+ shgroup_geometry_call(material->shading.shgrp, ob, geom);
+ shgroup_geometry_call(material->prepass.shgrp, ob, geom);
+ shgroup_geometry_call(material->shadow.shgrp, ob, geom);
+
+ is_shadow_caster = is_shadow_caster || material->shadow.shgrp != nullptr;
+ is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent;
+ }
+ // shading_passes.velocity.mesh_add(ob, ob_handle);
+
+ // shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPencil
+ * \{ */
+
+#define DO_BATCHING true
+
+struct gpIterData {
+ Instance &inst;
+ Object *ob;
+ MaterialArray &material_array;
+ int cfra;
+
+ /* Drawcall batching. */
+ GPUBatch *geom = nullptr;
+ Material *material = nullptr;
+ int vfirst = 0;
+ int vcount = 0;
+ bool instancing = false;
+
+ gpIterData(Instance &inst_, Object *ob_)
+ : inst(inst_), ob(ob_), material_array(inst_.materials.material_array_get(ob_))
+ {
+ cfra = DEG_get_ctime(inst.depsgraph);
+ };
+};
+
+static void gpencil_drawcall_flush(gpIterData &iter)
+{
+ if (iter.geom != nullptr) {
+ shgroup_geometry_call(iter.material->shading.shgrp,
+ iter.ob,
+ iter.geom,
+ iter.vfirst,
+ iter.vcount,
+ iter.instancing);
+ shgroup_geometry_call(iter.material->prepass.shgrp,
+ iter.ob,
+ iter.geom,
+ iter.vfirst,
+ iter.vcount,
+ iter.instancing);
+ shgroup_geometry_call(iter.material->shadow.shgrp,
+ iter.ob,
+ iter.geom,
+ iter.vfirst,
+ iter.vcount,
+ iter.instancing);
+ }
+ iter.geom = nullptr;
+ iter.vfirst = -1;
+ iter.vcount = 0;
+}
+
+/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
+static void gpencil_drawcall_add(gpIterData &iter,
+ GPUBatch *geom,
+ Material *material,
+ int v_first,
+ int v_count,
+ bool instancing)
+{
+ int last = iter.vfirst + iter.vcount;
+ /* Interrupt draw-call grouping if the sequence is not consecutive. */
+ if (!DO_BATCHING || (geom != iter.geom) || (material != iter.material) || (v_first - last > 3)) {
+ gpencil_drawcall_flush(iter);
+ }
+ iter.geom = geom;
+ iter.material = material;
+ iter.instancing = instancing;
+ if (iter.vfirst == -1) {
+ iter.vfirst = v_first;
+ }
+ iter.vcount = v_first + v_count - iter.vfirst;
+}
+
+static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
+{
+ gpIterData &iter = *(gpIterData *)thunk;
+
+ Material *material = iter.material_array.materials[gps->mat_nr];
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter.ob, gps->mat_nr + 1);
+
+ bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
+ bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
+ (!DRW_state_is_image_render() && ((gps->flag & GP_STROKE_NOFILL) != 0));
+ bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0);
+
+ if (hide_material) {
+ return;
+ }
+
+ if (show_fill) {
+ GPUBatch *geom = DRW_cache_gpencil_fills_get(iter.ob, iter.cfra);
+ int vfirst = gps->runtime.fill_start * 3;
+ int vcount = gps->tot_triangles * 3;
+ gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false);
+ }
+
+ if (show_stroke) {
+ GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter.ob, iter.cfra);
+ /* Start one vert before to have gl_InstanceID > 0 (see shader). */
+ int vfirst = gps->runtime.stroke_start - 1;
+ /* Include "potential" cyclic vertex and start adj vertex (see shader). */
+ int vcount = gps->totpoints + 1 + 1;
+ gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true);
+ }
+}
+
+void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle)
+{
+ gpIterData iter(inst_, ob);
+
+ BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter);
+
+ gpencil_drawcall_flush(iter);
+
+ /* TODO(fclem) Gpencil velocity. */
+ // shading_passes.velocity.gpencil_add(ob, ob_handle);
+
+ // bool is_caster = true; /* TODO material.shadow.shgrp. */
+ // bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
+ // shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hair
+ * \{ */
+
+static void shgroup_curves_call(MaterialPass &matpass,
+ Object *ob,
+ ParticleSystem *part_sys = nullptr,
+ ModifierData *modifier_data = nullptr)
+{
+ if (matpass.shgrp == nullptr) {
+ return;
+ }
+ DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat);
+}
+
+void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data)
+{
+ int mat_nr = CURVES_MATERIAL_NR;
+
+ ParticleSystem *part_sys = nullptr;
+ if (modifier_data != nullptr) {
+ part_sys = reinterpret_cast<ParticleSystemModifierData *>(modifier_data)->psys;
+ if (!DRW_object_is_visible_psys_in_active_context(ob, part_sys)) {
+ return;
+ }
+ ParticleSettings *part_settings = part_sys->part;
+ const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
+ part_settings->draw_as;
+ if (draw_as != PART_DRAW_PATH) {
+ return;
+ }
+ mat_nr = part_settings->omat;
+ }
+
+ Material &material = inst_.materials.material_get(ob, mat_nr - 1, MAT_GEOM_CURVES);
+
+ shgroup_curves_call(material.shading, ob, part_sys, modifier_data);
+ shgroup_curves_call(material.prepass, ob, part_sys, modifier_data);
+ shgroup_curves_call(material.shadow, ob, part_sys, modifier_data);
+ /* TODO(fclem) Hair velocity. */
+ // shading_passes.velocity.gpencil_add(ob, ob_handle);
+
+ // bool is_caster = material.shadow.shgrp != nullptr;
+ // bool is_alpha_blend = material.is_alpha_blend_transparent;
+ // shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh
new file mode 100644
index 00000000000..74b08d0f10a
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Structures to identify unique data blocks. The keys are unique so we are able to
+ * match ids across frame updates.
+ */
+
+#pragma once
+
+#include "BKE_duplilist.h"
+#include "BLI_ghash.h"
+#include "BLI_map.hh"
+#include "DNA_object_types.h"
+#include "DRW_render.h"
+#include "GPU_material.h"
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name ObjectKey
+ *
+ * Unique key to identify each object in the hashmap.
+ * \{ */
+
+struct ObjectKey {
+ /** Hash value of the key. */
+ uint64_t hash_value;
+ /** Original Object or source object for duplis. */
+ Object *ob;
+ /** Original Parent object for duplis. */
+ Object *parent;
+ /** Dupli objects recursive unique identifier */
+ int id[MAX_DUPLI_RECUR];
+ /** If object uses particle system hair. */
+ bool use_particle_hair;
+#ifdef DEBUG
+ char name[64];
+#endif
+ ObjectKey() : ob(nullptr), parent(nullptr){};
+
+ ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], bool use_particle_hair_)
+ : ob(ob_), parent(parent_), use_particle_hair(use_particle_hair_)
+ {
+ if (id_) {
+ memcpy(id, id_, sizeof(id));
+ }
+ else {
+ memset(id, 0, sizeof(id));
+ }
+ /* Compute hash on creation so we avoid the cost of it for every sync. */
+ hash_value = BLI_ghashutil_ptrhash(ob);
+ hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_ptrhash(parent));
+ for (int i = 0; i < MAX_DUPLI_RECUR; i++) {
+ if (id[i] != 0) {
+ hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_inthash(id[i]));
+ }
+ else {
+ break;
+ }
+ }
+#ifdef DEBUG
+ STRNCPY(name, ob->id.name);
+#endif
+ }
+
+ ObjectKey(Object *ob, DupliObject *dupli, Object *parent)
+ : ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, false){};
+
+ ObjectKey(Object *ob)
+ : ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){};
+
+ uint64_t hash(void) const
+ {
+ return hash_value;
+ }
+
+ bool operator<(const ObjectKey &k) const
+ {
+ if (ob != k.ob) {
+ return (ob < k.ob);
+ }
+ if (parent != k.parent) {
+ return (parent < k.parent);
+ }
+ if (use_particle_hair != k.use_particle_hair) {
+ return (use_particle_hair < k.use_particle_hair);
+ }
+ return memcmp(id, k.id, sizeof(id)) < 0;
+ }
+
+ bool operator==(const ObjectKey &k) const
+ {
+ if (ob != k.ob) {
+ return false;
+ }
+ if (parent != k.parent) {
+ return false;
+ }
+ if (use_particle_hair != k.use_particle_hair) {
+ return false;
+ }
+ return memcmp(id, k.id, sizeof(id)) == 0;
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sync Module
+ *
+ * \{ */
+
+struct ObjectHandle : public DrawData {
+ ObjectKey object_key;
+
+ void reset_recalc_flag(void)
+ {
+ if (recalc != 0) {
+ recalc = 0;
+ }
+ }
+};
+
+struct WorldHandle : public DrawData {
+ void reset_recalc_flag(void)
+ {
+ if (recalc != 0) {
+ recalc = 0;
+ }
+ }
+};
+
+class SyncModule {
+ private:
+ Instance &inst_;
+
+ public:
+ SyncModule(Instance &inst) : inst_(inst){};
+ ~SyncModule(){};
+
+ ObjectHandle &sync_object(Object *ob);
+ WorldHandle &sync_world(::World *world);
+
+ void sync_mesh(Object *ob, ObjectHandle &ob_handle);
+ void sync_gpencil(Object *ob, ObjectHandle &ob_handle);
+ void sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc
new file mode 100644
index 00000000000..aa3f229123b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_view.cc
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * A view is either:
+ * - The entire main view.
+ * - A fragment of the main view (for panoramic projections).
+ * - A shadow map view.
+ * - A lightprobe view (either planar, cubemap, irradiance grid).
+ *
+ * A pass is a container for scene data. It is view agnostic but has specific logic depending on
+ * its type. Passes are shared between views.
+ */
+
+#include "BKE_global.h"
+#include "DRW_render.h"
+
+#include "eevee_instance.hh"
+
+#include "eevee_view.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name ShadingView
+ * \{ */
+
+void ShadingView::init()
+{
+ // dof_.init();
+ // mb_.init();
+}
+
+void ShadingView::sync(int2 render_extent_)
+{
+ if (false /* inst_.camera.is_panoramic() */) {
+ int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y;
+ /* Divide pixel count between the 6 views. Rendering to a square target. */
+ extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6)));
+ /* TODO(fclem) Clip unused views heres. */
+ is_enabled_ = true;
+ }
+ else {
+ extent_ = render_extent_;
+ /* Only enable -Z view. */
+ is_enabled_ = (StringRefNull(name_) == "negZ_view");
+ }
+
+ if (!is_enabled_) {
+ return;
+ }
+
+ /* Create views. */
+ // const CameraData &data = inst_.camera.data_get();
+
+ float4x4 viewmat, winmat;
+ const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr();
+#if 0
+ if (false /* inst_.camera.is_panoramic() */) {
+ /* TODO(fclem) Overscans. */
+ /* For now a mandatory 5% overscan for DoF. */
+ float side = data.clip_near * 1.05f;
+ float near = data.clip_near;
+ float far = data.clip_far;
+ perspective_m4(winmat.ptr(), -side, side, -side, side, near, far);
+ viewmat = face_matrix_ * data.viewmat;
+ }
+ else {
+ viewmat_p = data.viewmat.ptr();
+ winmat_p = data.winmat.ptr();
+ }
+#else
+ /* TEMP */
+ const DRWView *default_view = DRW_view_default_get();
+ DRW_view_winmat_get(default_view, winmat.ptr(), false);
+ DRW_view_viewmat_get(default_view, viewmat.ptr(), false);
+#endif
+
+ main_view_ = DRW_view_create(viewmat_p, winmat_p, nullptr, nullptr, nullptr);
+ sub_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
+ render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
+
+ // dof_.sync(winmat_p, extent_);
+ // mb_.sync(extent_);
+ // velocity_.sync(extent_);
+ // rt_buffer_opaque_.sync(extent_);
+ // rt_buffer_refract_.sync(extent_);
+ // inst_.hiz_back.view_sync(extent_);
+ // inst_.hiz_front.view_sync(extent_);
+ // inst_.gbuffer.view_sync(extent_);
+
+ combined_tx_.sync();
+ postfx_tx_.sync();
+}
+
+void ShadingView::render(void)
+{
+ if (!is_enabled_) {
+ return;
+ }
+
+ /* Query temp textures and create framebuffers. */
+ /* HACK: View name should be unique and static.
+ * With this, we can reuse the same texture across views. */
+ DrawEngineType *owner = (DrawEngineType *)name_;
+
+ depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8, extent_);
+ combined_tx_.acquire(extent_, GPU_RGBA16F, owner);
+ view_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_));
+
+ update_view();
+
+ DRW_stats_group_start(name_);
+ // DRW_view_set_active(render_view_);
+
+ /* Alpha stores transmittance. So start at 1. */
+ float4 clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
+ // GPU_framebuffer_bind(view_fb_);
+ // GPU_framebuffer_clear_color_depth(view_fb_, clear_color, 1.0f);
+ GPUFrameBuffer *dfb = GPU_framebuffer_active_get();
+ GPU_framebuffer_clear_color_depth(dfb, clear_color, 1.0f);
+
+ inst_.pipelines.world.render();
+
+ // inst_.pipelines.deferred.render(
+ // render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_);
+
+ // inst_.lightprobes.draw_cache_display();
+
+ // inst_.lookdev.render_overlay(view_fb_);
+
+ inst_.pipelines.forward.render(render_view_, depth_tx_, combined_tx_);
+
+ // inst_.lights.debug_draw(view_fb_);
+ // inst_.shadows.debug_draw(view_fb_);
+
+ // velocity_.render(depth_tx_);
+
+ // if (inst_.render_passes.vector) {
+ // inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_);
+ // }
+
+ // GPUTexture *final_radiance_tx = render_post(combined_tx_);
+
+ // if (inst_.render_passes.combined) {
+ // inst_.render_passes.combined->accumulate(final_radiance_tx, sub_view_);
+ // }
+
+ // if (inst_.render_passes.depth) {
+ // inst_.render_passes.depth->accumulate(depth_tx_, sub_view_);
+ // }
+
+ DRW_stats_group_end();
+
+ combined_tx_.release();
+ postfx_tx_.release();
+}
+
+GPUTexture *ShadingView::render_post(GPUTexture *input_tx)
+{
+#if 0
+ if (!dof_.postfx_enabled() && !mb_.enabled()) {
+ return input_tx;
+ }
+ /* HACK: View name should be unique and static.
+ * With this, we can reuse the same texture across views. */
+ postfx_tx_.acquire(extent_, GPU_RGBA16F, (void *)name_);
+
+ GPUTexture *velocity_tx = velocity_.view_vectors_get();
+ GPUTexture *output_tx = postfx_tx_;
+
+ /* Swapping is done internally. Actual output is set to the next input. */
+ dof_.render(depth_tx_, &input_tx, &output_tx);
+ mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx);
+#endif
+ return input_tx;
+}
+
+void ShadingView::update_view(void)
+{
+ float4x4 viewmat, winmat;
+ DRW_view_viewmat_get(main_view_, viewmat.ptr(), false);
+ DRW_view_winmat_get(main_view_, winmat.ptr(), false);
+
+ /* Anti-Aliasing / Super-Sampling jitter. */
+ // float jitter_u = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_U) - 0.5f) / extent_[0];
+ // float jitter_v = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_V) - 0.5f) / extent_[1];
+
+ // window_translate_m4(winmat.ptr(), winmat.ptr(), jitter_u, jitter_v);
+ DRW_view_update_sub(sub_view_, viewmat.ptr(), winmat.ptr());
+
+ /* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop
+ * out of the blurring radius. To fix this, use custom enlarged culling matrix. */
+ // dof_.jitter_apply(winmat, viewmat);
+ DRW_view_update_sub(render_view_, viewmat.ptr(), winmat.ptr());
+
+ // inst_.lightprobes.set_view(render_view_, extent_);
+ // inst_.lights.set_view(render_view_, extent_, !inst_.use_scene_lights());
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh
new file mode 100644
index 00000000000..16c2184784c
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_view.hh
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * A view is either:
+ * - The entire main view.
+ * - A portion of the main view (for panoramic projections).
+ * - A lightprobe view (either planar, cubemap, irradiance grid).
+ *
+ * A pass is a container for scene data. It is view agnostic but has specific logic depending on
+ * its type. Passes are shared between views.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "eevee_camera.hh"
+#include "eevee_pipeline.hh"
+#include "eevee_shader.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name ShadingView
+ *
+ * Render the scene and fill all render passes data.
+ * \{ */
+
+class ShadingView {
+ private:
+ Instance &inst_;
+ /** Static string pointer. Used as debug name and as UUID for texture pool. */
+ const char *name_;
+ /** Matrix to apply to the viewmat. */
+ const float (*face_matrix_)[4];
+
+ /** Post-fx modules. */
+ // DepthOfField dof_;
+ // MotionBlur mb_;
+ // Velocity velocity_;
+
+ /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
+ // RaytraceBuffer rt_buffer_opaque_;
+ // RaytraceBuffer rt_buffer_refract_;
+
+ Framebuffer view_fb_;
+ Texture depth_tx_;
+ TextureFromPool combined_tx_;
+ TextureFromPool postfx_tx_;
+
+ /** Main views is created from the camera (or is from the viewport). It is not jittered. */
+ DRWView *main_view_ = nullptr;
+ /** Sub views is jittered versions or the main views. This allows jitter updates without trashing
+ * the visibility culling cache. */
+ DRWView *sub_view_ = nullptr;
+ /** Same as sub_view_ but has Depth Of Field jitter applied. */
+ DRWView *render_view_ = nullptr;
+
+ /** Render size of the view. Can change between scene sample eval. */
+ int2 extent_ = {-1, -1};
+
+ bool is_enabled_ = false;
+
+ public:
+ ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4])
+ : inst_(inst), name_(name), face_matrix_(face_matrix){};
+
+ ~ShadingView(){};
+
+ void init(void);
+
+ void sync(int2 render_extent_);
+
+ void render(void);
+
+ GPUTexture *render_post(GPUTexture *input_tx);
+
+ private:
+ void update_view(void);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Main View
+ *
+ * Container for all views needed to render the final image.
+ * We might need up to 6 views for panoramic cameras.
+ * All views are always available but only enabled for if need.
+ * \{ */
+
+class MainView {
+ private:
+ /* WORKAROUND: Defining this as an array does not seems to work on GCC < 9.4.
+ * It tries to use the copy constructor and fails because ShadingView is non-copyable and
+ * non-movable. */
+ ShadingView shading_views_0;
+ ShadingView shading_views_1;
+ ShadingView shading_views_2;
+ ShadingView shading_views_3;
+ ShadingView shading_views_4;
+ ShadingView shading_views_5;
+#define shading_views_ (&shading_views_0)
+ /** Internal render size. */
+ int render_extent_[2];
+
+ public:
+ MainView(Instance &inst)
+ : shading_views_0(inst, "posX_view", cubeface_mat[0]),
+ shading_views_1(inst, "negX_view", cubeface_mat[1]),
+ shading_views_2(inst, "posY_view", cubeface_mat[2]),
+ shading_views_3(inst, "negY_view", cubeface_mat[3]),
+ shading_views_4(inst, "posZ_view", cubeface_mat[4]),
+ shading_views_5(inst, "negZ_view", cubeface_mat[5])
+ {
+ }
+
+ void init(const int2 full_extent_)
+ {
+ /* TODO(fclem) parameter hidden in experimental. We need to figure out mipmap bias to preserve
+ * texture crispiness. */
+ float resolution_scale = 1.0f;
+ for (int i = 0; i < 2; i++) {
+ render_extent_[i] = max_ii(1, roundf(full_extent_[i] * resolution_scale));
+ }
+
+ for (auto i : IndexRange(6)) {
+ shading_views_[i].init();
+ }
+ }
+
+ void sync(void)
+ {
+ for (auto i : IndexRange(6)) {
+ shading_views_[i].sync(render_extent_);
+ }
+ }
+
+ void render(void)
+ {
+ for (auto i : IndexRange(6)) {
+ shading_views_[i].render();
+ }
+ }
+
+#undef shading_views_
+};
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc
new file mode 100644
index 00000000000..939f6087137
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_world.cc
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#include "BKE_lib_id.h"
+#include "BKE_node.h"
+#include "BKE_world.h"
+#include "DEG_depsgraph_query.h"
+#include "NOD_shader.h"
+
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Default Material
+ *
+ * \{ */
+
+DefaultWorldNodeTree::DefaultWorldNodeTree()
+{
+ bNodeTree *ntree = ntreeAddTree(nullptr, "World Nodetree", ntreeType_Shader->idname);
+ bNode *background = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND);
+ bNode *output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD);
+ bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
+ bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
+ nodeAddLink(ntree, background, background_out, output, output_in);
+ nodeSetActive(ntree, output);
+
+ color_socket_ =
+ (bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value;
+ ntree_ = ntree;
+}
+
+DefaultWorldNodeTree::~DefaultWorldNodeTree()
+{
+ ntreeFreeEmbeddedTree(ntree_);
+ MEM_SAFE_FREE(ntree_);
+}
+
+/* Configure a default nodetree with the given world. */
+bNodeTree *DefaultWorldNodeTree::nodetree_get(::World *wo)
+{
+ /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
+ copy_v3_fl3(color_socket_->value, wo->horr, wo->horg, wo->horb);
+ return ntree_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name World
+ *
+ * \{ */
+
+void World::sync()
+{
+ // if (inst_.lookdev.sync_world()) {
+ // return;
+ // }
+
+ ::World *bl_world = inst_.scene->world;
+
+ if (bl_world == nullptr) {
+ // bl_world = BKE_world_default();
+ return;
+ }
+ else {
+ WorldHandle &wo_handle = inst_.sync.sync_world(bl_world);
+
+ if (wo_handle.recalc != 0) {
+ // inst_.lightprobes.set_world_dirty();
+ }
+ wo_handle.reset_recalc_flag();
+ }
+
+ /* TODO(fclem) This should be detected to scene level. */
+ ::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id);
+ if (assign_if_different(prev_original_world, orig_world)) {
+ // inst_.sampling.reset();
+ }
+
+ bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ?
+ bl_world->nodetree :
+ default_tree.nodetree_get(bl_world);
+
+ GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree);
+ inst_.pipelines.world.sync(gpumat);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh
new file mode 100644
index 00000000000..a7b77ef2e62
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_world.hh
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * World rendering with material handling. Also take care of lookdev
+ * HDRI and default material.
+ */
+
+#pragma once
+
+#include "DNA_world_types.h"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Default World Nodetree
+ *
+ * In order to support worlds without nodetree we reuse and configure a standalone nodetree that
+ * we pass for shader generation. The GPUMaterial is still stored inside the World even if
+ * it does not use a nodetree.
+ * \{ */
+
+class DefaultWorldNodeTree {
+ private:
+ bNodeTree *ntree_;
+ bNodeSocketValueRGBA *color_socket_;
+
+ public:
+ DefaultWorldNodeTree();
+ ~DefaultWorldNodeTree();
+
+ bNodeTree *nodetree_get(::World *world);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name World
+ *
+ * \{ */
+
+class World {
+ private:
+ Instance &inst_;
+
+ DefaultWorldNodeTree default_tree;
+
+ /* Used to detect if world change. */
+ ::World *prev_original_world = nullptr;
+
+ public:
+ World(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
new file mode 100644
index 00000000000..d315fe92400
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
@@ -0,0 +1,305 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+
+#if defined(MAT_GEOM_MESH)
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh
+ *
+ * Mesh objects attributes are loaded using vertex input attributes.
+ * \{ */
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(vec4 orco)
+{
+ /* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
+ * attrib (which is [0,0,0,1]). */
+ if (orco.w == 1.0) {
+ /* If the object does not have any deformation, the orco layer calculation is done on the fly
+ * using the orco_madd factors. */
+ return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz;
+ }
+ return orco.xyz * 0.5 + 0.5;
+}
+# endif
+vec4 attr_load_tangent(vec4 tangent)
+{
+ tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz));
+ return tangent;
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return attr;
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return attr;
+}
+float attr_load_float(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return attr;
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_GPENCIL)
+
+/* -------------------------------------------------------------------- */
+/** \name Grease Pencil
+ *
+ * Grease Pencil objects have one uv and one color attribute layer.
+ * \{ */
+
+/* Globals to feed the load functions. */
+vec2 g_uvs;
+vec4 g_color;
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(vec4 orco)
+{
+ vec3 lP = point_world_to_object(interp.P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+vec3 attr_load_uv(vec3 dummy)
+{
+ return vec3(g_uvs, 0.0);
+}
+vec4 attr_load_color(vec4 dummy)
+{
+ return g_color;
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0.0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0.0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0.0);
+}
+float attr_load_float(float attr)
+{
+ return 0.0;
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_CURVES)
+
+/* -------------------------------------------------------------------- */
+/** \name Curve
+ *
+ * Curve objects loads attributes from buffers through sampler buffers.
+ * \{ */
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(samplerBuffer cd_buf)
+{
+ vec3 P = hair_get_strand_pos();
+ vec3 lP = transform_point(ModelMatrixInverse, P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+vec4 attr_load_tangent(samplerBuffer cd_buf)
+{
+ /* Not supported for the moment. */
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+vec3 attr_load_uv(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgb;
+}
+vec4 attr_load_color(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgba;
+}
+vec4 attr_load_vec4(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgba;
+}
+vec3 attr_load_vec3(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rgb;
+}
+vec2 attr_load_vec2(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).rg;
+}
+float attr_load_float(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.curves_strand_id).r;
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_VOLUME)
+
+/* -------------------------------------------------------------------- */
+/** \name Volume
+ *
+ * Volume objects loads attributes from "grids" in the form of 3D textures.
+ * Per grid transform order is following loading order.
+ * \{ */
+
+# ifndef OBINFO_LIB
+# error "draw_object_infos is mandatory for volume objects"
+# endif
+
+vec3 g_orco;
+int g_attr_id = 0;
+
+vec3 grid_coordinates()
+{
+ vec3 co = g_orco;
+ /* Optional per-grid transform. */
+ if (drw_volume.grids_xform[g_attr_id][3][3] != 0.0) {
+ co = (drw_volume.grids_xform[g_attr_id] * vec4(objectPosition, 1.0)).xyz;
+ }
+ g_attr_id += 1;
+ return co;
+}
+
+vec3 attr_load_orco(sampler3D tex)
+{
+ g_attr_id += 1;
+ return g_orco;
+}
+vec4 attr_load_tangent(sampler3D tex)
+{
+ attr_id += 1;
+ return vec4(0);
+}
+vec4 attr_load_vec4(sampler3D tex)
+{
+ return texture(tex, grid_coordinates());
+}
+vec3 attr_load_vec3(sampler3D tex)
+{
+ return texture(tex, grid_coordinates()).rgb;
+}
+vec2 attr_load_vec2(sampler3D tex)
+{
+ return texture(tex, grid_coordinates()).rg;
+}
+float attr_load_float(sampler3D tex)
+{
+ return texture(tex, grid_coordinates()).r;
+}
+vec4 attr_load_color(sampler3D tex)
+{
+ return texture(tex, grid_coordinates());
+}
+vec3 attr_load_uv(sampler3D attr)
+{
+ attr_id += 1;
+ return vec3(0);
+}
+
+/** \} */
+
+#elif defined(MAT_GEOM_WORLD)
+
+/* -------------------------------------------------------------------- */
+/** \name World
+ *
+ * World has no attributes other than orco.
+ * \{ */
+
+vec3 attr_load_orco(vec4 orco)
+{
+ return -g_data.N;
+}
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0);
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0);
+}
+float attr_load_float(float attr)
+{
+ return 0.0;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return vec3(0);
+}
+
+/** \} */
+
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Volume Attribute post
+ *
+ * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on
+ * the engine side. But as of now, the engines are reponsible for loading the attributes.
+ *
+ * \{ */
+
+#if defined(MAT_GEOM_VOLUME)
+
+float attr_load_temperature_post(float attr)
+{
+ /* Bring the into standard range without having to modify the grid values */
+ attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0;
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ /* Density is premultiplied for interpolation, divide it out here. */
+ attr.rgb *= safe_rcp(attr.a);
+ attr.rgb *= drw_volume.color_mul.rgb;
+ attr.a = 1.0;
+ return attr;
+}
+
+#else /* Noop for any other surface. */
+
+float attr_load_temperature_post(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ return attr;
+}
+
+#endif
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
new file mode 100644
index 00000000000..4c123031c83
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
@@ -0,0 +1,37 @@
+
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl) /* TODO rename to curve. */
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ init_interface();
+
+ vec3 T;
+
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ hair_get_pos_tan_binor_time(is_persp,
+ ModelMatrixInverse,
+ ViewMatrixInverse[3].xyz,
+ ViewMatrixInverse[2].xyz,
+ interp.P,
+ T,
+ interp.curves_binormal,
+ interp.curves_time,
+ interp.curves_thickness,
+ interp.curves_time_width);
+
+ interp.N = cross(T, interp.curves_binormal);
+ interp.curves_strand_id = hair_get_strand_id();
+ interp.barycentric_coords = hair_get_barycentric();
+
+ init_globals();
+ attrib_load();
+
+ interp.P += nodetree_displacement();
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
new file mode 100644
index 00000000000..8e7201b9bce
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
@@ -0,0 +1,46 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+
+void main(void)
+{
+ init_interface();
+
+ /* TODO(fclem): Expose through a node? */
+ vec4 sspos;
+ vec2 aspect;
+ float strength;
+ float hardness;
+ vec2 thickness;
+
+ gl_Position = gpencil_vertex(ma,
+ ma1,
+ ma2,
+ ma3,
+ pos,
+ pos1,
+ pos2,
+ pos3,
+ uv1,
+ uv2,
+ col1,
+ col2,
+ fcol1,
+ vec4(drw_view.viewport_size, drw_view.viewport_size_inverse),
+ interp.P,
+ interp.N,
+ g_color,
+ strength,
+ g_uvs,
+ sspos,
+ aspect,
+ thickness,
+ hardness);
+
+ init_globals();
+ attrib_load();
+
+ interp.P += nodetree_displacement();
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
new file mode 100644
index 00000000000..24d5a2c60b0
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
@@ -0,0 +1,20 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ init_interface();
+
+ interp.P = point_object_to_world(pos);
+ interp.N = normal_object_to_world(nor);
+
+ init_globals();
+ attrib_load();
+
+ interp.P += nodetree_displacement();
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl
new file mode 100644
index 00000000000..6ce98f26c7e
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_world_vert.glsl
@@ -0,0 +1,21 @@
+
+/**
+ * Custom fullscreen triangle with placeholders varyings.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ /* Fullscreen triangle. */
+ int v = gl_VertexID % 3;
+ float x = float((v & 1) << 2) - 1.0;
+ float y = float((v & 2) << 1) - 1.0;
+ gl_Position = vec4(x, y, 1.0, 1.0);
+
+ /* Pass view position to keep accuracy. */
+ interp.P = project_point(ProjectionMatrixInverse, gl_Position.xyz);
+ interp.N = vec3(1);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
new file mode 100644
index 00000000000..0110a2bd930
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
@@ -0,0 +1,358 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+
+vec3 g_emission;
+vec3 g_transmittance;
+float g_holdout;
+
+/* The Closure type is never used. Use float as dummy type. */
+#define Closure float
+
+/* Sampled closure parameters. */
+ClosureDiffuse g_diffuse_data;
+ClosureReflection g_reflection_data;
+ClosureRefraction g_refraction_data;
+/* Weight accumulation per sampled closure type. */
+float g_diffuse_weight;
+float g_reflection_weight;
+float g_refraction_weight;
+/* Random number per sampled closure type. */
+float g_diffuse_rand;
+float g_reflection_rand;
+float g_refraction_rand;
+
+/**
+ * Returns true if the closure is to be selected based on the input weight.
+ */
+bool closure_select(float weight, inout float total_weight, inout float r)
+{
+ total_weight += weight;
+ float x = weight / total_weight;
+ bool chosen = (r < x);
+ /* Assuming that if r is in the interval [0,x] or [x,1], it's still uniformly distributed within
+ * that interval, so you remaping to [0,1] again to explore this space of probability. */
+ r = (chosen) ? (r / x) : ((r - x) / (1.0 - x));
+ return chosen;
+}
+
+#define SELECT_CLOSURE(destination, weight_total, random, candidate) \
+ if (closure_select(candidate.weight, weight_total, random)) { \
+ destination = candidate; \
+ }
+
+void closure_weights_reset()
+{
+ g_diffuse_weight = 0.0;
+ g_reflection_weight = 0.0;
+ g_refraction_weight = 0.0;
+
+ g_diffuse_data.weight = 0.0;
+ g_diffuse_data.color = vec3(0.0);
+ g_diffuse_data.N = vec3(0.0);
+ g_diffuse_data.sss_radius = vec3(0.0);
+ g_diffuse_data.sss_id = uint(0);
+
+ g_reflection_data.weight = 0.0;
+ g_reflection_data.color = vec3(0.0);
+ g_reflection_data.N = vec3(0.0);
+ g_reflection_data.roughness = 0.0;
+
+ g_refraction_data.weight = 0.0;
+ g_refraction_data.color = vec3(0.0);
+ g_refraction_data.N = vec3(0.0);
+ g_refraction_data.roughness = 0.0;
+ g_refraction_data.ior = 0.0;
+
+ /* TODO */
+ g_diffuse_rand = 0.5;
+ g_reflection_rand = 0.5;
+ g_refraction_rand = 0.5;
+
+ g_emission = vec3(0.0);
+ g_transmittance = vec3(0.0);
+ g_holdout = 0.0;
+}
+
+/* Single BSDFs. */
+Closure closure_eval(ClosureDiffuse diffuse)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureTranslucent translucent)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureReflection reflection)
+{
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureRefraction refraction)
+{
+ SELECT_CLOSURE(g_refraction_data, g_refraction_weight, g_refraction_rand, refraction);
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureEmission emission)
+{
+ g_emission += emission.emission * emission.weight;
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureTransparency transparency)
+{
+ g_transmittance += transparency.transmittance * transparency.weight;
+ g_holdout += transparency.holdout * transparency.weight;
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureVolumeScatter volume_scatter)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+Closure closure_eval(ClosureHair hair)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+/* Glass BSDF. */
+Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
+{
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_refraction_data, g_refraction_weight, g_refraction_rand, refraction);
+ return Closure(0);
+}
+
+/* Dielectric BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ return Closure(0);
+}
+
+/* ClearCoat BSDF. */
+Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat)
+{
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, clearcoat);
+ return Closure(0);
+}
+
+/* Volume BSDF. */
+Closure closure_eval(ClosureVolumeScatter volume_scatter,
+ ClosureVolumeAbsorption volume_absorption,
+ ClosureEmission emission)
+{
+ /* TODO */
+ return Closure(0);
+}
+
+/* Specular BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, clearcoat);
+ return Closure(0);
+}
+
+/* Principled BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat,
+ ClosureRefraction refraction)
+{
+ SELECT_CLOSURE(g_diffuse_data, g_diffuse_weight, g_diffuse_rand, diffuse);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, reflection);
+ SELECT_CLOSURE(g_reflection_data, g_reflection_weight, g_reflection_rand, clearcoat);
+ SELECT_CLOSURE(g_refraction_data, g_refraction_weight, g_refraction_rand, refraction);
+ return Closure(0);
+}
+
+/* Noop since we are sampling closures. */
+Closure closure_add(Closure cl1, Closure cl2)
+{
+ return Closure(0);
+}
+Closure closure_mix(Closure cl1, Closure cl2, float fac)
+{
+ return Closure(0);
+}
+
+float ambient_occlusion_eval(vec3 normal,
+ float distance,
+ const float inverted,
+ const float sample_count)
+{
+ /* TODO */
+ return 0.0;
+}
+
+#ifndef GPU_METAL
+void attrib_load();
+Closure nodetree_surface();
+Closure nodetree_volume();
+vec3 nodetree_displacement();
+float nodetree_thickness();
+#endif
+
+/* Stubs. */
+vec2 btdf_lut(float a, float b, float c)
+{
+ return vec2(1, 0);
+}
+vec2 brdf_lut(float a, float b)
+{
+ return vec2(1, 0);
+}
+vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c)
+{
+ return a;
+}
+vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c)
+{
+ return a;
+}
+float F_eta(float a, float b)
+{
+ return a;
+}
+vec4 closure_to_rgba(Closure cl)
+{
+ return vec4(0.0);
+}
+void output_aov(vec4 color, float value, uint hash)
+{
+}
+
+#ifdef EEVEE_MATERIAL_STUBS
+# define attrib_load()
+# define nodetree_displacement() vec3(0.0)
+# define nodetree_surface() Closure(0)
+# define nodetree_volume() Closure(0)
+# define nodetree_thickness() 0.1
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Fragment Displacement
+ *
+ * Displacement happening in the fragment shader.
+ * Can be used in conjunction with a per vertex displacement.
+ *
+ * \{ */
+
+#ifdef MAT_DISPLACEMENT_BUMP
+/* Return new shading normal. */
+vec3 displacement_bump()
+{
+# ifdef GPU_FRAGMENT_SHADER
+ vec2 dHd;
+ dF_branch(dot(nodetree_displacement(), g_data.N + dF_impl(g_data.N)), dHd);
+
+ vec3 dPdx = dFdx(g_data.P);
+ vec3 dPdy = dFdy(g_data.P);
+
+ /* Get surface tangents from normal. */
+ vec3 Rx = cross(dPdy, g_data.N);
+ vec3 Ry = cross(g_data.N, dPdx);
+
+ /* Compute surface gradient and determinant. */
+ float det = dot(dPdx, Rx);
+
+ vec3 surfgrad = dHd.x * Rx + dHd.y * Ry;
+
+ float facing = FrontFacing ? 1.0 : -1.0;
+ return normalize(abs(det) * g_data.N - facing * sign(det) * surfgrad);
+# else
+ return g_data.N;
+# endif
+}
+#endif
+
+void fragment_displacement()
+{
+#ifdef MAT_DISPLACEMENT_BUMP
+ g_data.N = displacement_bump();
+#endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Coordinate implementations
+ *
+ * Callbacks for the texture coordinate node.
+ *
+ * \{ */
+
+vec3 coordinate_camera(vec3 P)
+{
+ vec3 vP;
+ if (false /* probe */) {
+ /* Unsupported. It would make the probe camera-dependent. */
+ vP = P;
+ }
+ else {
+#ifdef MAT_WORLD
+ vP = transform_direction(ViewMatrix, P);
+#else
+ vP = transform_point(ViewMatrix, P);
+#endif
+ }
+ vP.z = -vP.z;
+ return vP;
+}
+
+vec3 coordinate_screen(vec3 P)
+{
+ vec3 window = vec3(0.0);
+ if (false /* probe */) {
+ /* Unsupported. It would make the probe camera-dependent. */
+ window.xy = vec2(0.5);
+ }
+ else {
+ /* TODO(fclem): Actual camera tranform. */
+ window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5;
+ window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
+ }
+ return window;
+}
+
+vec3 coordinate_reflect(vec3 P, vec3 N)
+{
+#ifdef MAT_WORLD
+ return N;
+#else
+ return -reflect(cameraVec(P), N);
+#endif
+}
+
+vec3 coordinate_incoming(vec3 P)
+{
+#ifdef MAT_WORLD
+ return -P;
+#else
+ return cameraVec(P);
+#endif
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
new file mode 100644
index 00000000000..fbc5be853a0
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
@@ -0,0 +1,18 @@
+
+/**
+ * Deferred lighting evaluation: Lighting is evaluated in a separate pass.
+ *
+ * Outputs shading parameter per pixel using a randomized set of BSDFs.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+
+void main(void)
+{
+ init_globals();
+
+ nodetree_surface();
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
new file mode 100644
index 00000000000..a7656d70c1b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
@@ -0,0 +1,71 @@
+
+/**
+ * Depth shader that can stochastically discard transparent pixel.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */
+float hash(vec2 a)
+{
+ return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x))));
+}
+
+float hash3d(vec3 a)
+{
+ return hash(vec2(hash(a.xy), a.z));
+}
+
+float hashed_alpha_threshold(float hash_scale, float hash_offset, vec3 P)
+{
+ /* Find the discretized derivatives of our coordinates. */
+ float max_deriv = max(length(dFdx(P)), length(dFdy(P)));
+ float pix_scale = 1.0 / (hash_scale * max_deriv);
+ /* Find two nearest log-discretized noise scales. */
+ float pix_scale_log = log2(pix_scale);
+ vec2 pix_scales;
+ pix_scales.x = exp2(floor(pix_scale_log));
+ pix_scales.y = exp2(ceil(pix_scale_log));
+ /* Compute alpha thresholds at our two noise scales. */
+ vec2 alpha;
+ alpha.x = hash3d(floor(pix_scales.x * P));
+ alpha.y = hash3d(floor(pix_scales.y * P));
+ /* Factor to interpolate lerp with. */
+ float fac = fract(log2(pix_scale));
+ /* Interpolate alpha threshold from noise at two scales. */
+ float x = mix(alpha.x, alpha.y, fac);
+ /* Pass into CDF to compute uniformly distrib threshold. */
+ float a = min(fac, 1.0 - fac);
+ float one_a = 1.0 - a;
+ float denom = 1.0 / (2 * a * one_a);
+ float one_x = (1 - x);
+ vec3 cases = vec3((x * x) * denom, (x - 0.5 * a) / one_a, 1.0 - (one_x * one_x * denom));
+ /* Find our final, uniformly distributed alpha threshold. */
+ float threshold = (x < one_a) ? ((x < a) ? cases.x : cases.y) : cases.z;
+ /* Jitter the threshold for TAA accumulation. */
+ threshold = fract(threshold + hash_offset);
+ /* Avoids threshold == 0. */
+ threshold = clamp(threshold, 1.0e-6, 1.0);
+ return threshold;
+}
+
+void main(void)
+{
+#ifdef MAT_TRANSPARENT
+ init_globals();
+
+ nodetree_surface();
+
+ // float noise_offset = sampling_rng_1D_get(sampling_buf, SAMPLING_TRANSPARENCY);
+ float noise_offset = 0.5;
+ float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P);
+
+ float transparency = avg(g_transmittance);
+ if (transparency > random_threshold) {
+ discard;
+ }
+#endif
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
new file mode 100644
index 00000000000..d83802c73f3
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
@@ -0,0 +1,36 @@
+
+/**
+ * Forward lighting evaluation: Lighting is evaluated during the geometry rasterization.
+ *
+ * This is used by alpha blended materials and materials using Shader to RGB nodes.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+
+void main(void)
+{
+ init_globals();
+
+ fragment_displacement();
+
+ nodetree_surface();
+
+ g_holdout = saturate(g_holdout);
+
+ out_radiance.rgb = g_emission;
+ out_radiance.rgb += g_diffuse_data.color * saturate(g_diffuse_data.N.z);
+ out_radiance.rgb += g_reflection_data.color * saturate(g_reflection_data.N.z);
+ out_radiance.a = 0.0;
+
+ out_radiance.rgb *= 1.0 - g_holdout;
+
+ out_transmittance.rgb = g_transmittance;
+ out_transmittance.a = saturate(avg(g_transmittance));
+
+ /* Test */
+ out_transmittance.a = 1.0 - out_transmittance.a;
+ out_radiance.a = 1.0 - out_radiance.a;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
new file mode 100644
index 00000000000..5811b935c0d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
@@ -0,0 +1,106 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+
+#if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH)
+vec3 barycentric_distances_get()
+{
+ /* NOTE: No need to undo perspective divide since it has not been applied. */
+ vec3 pos0 = (ProjectionMatrixInverse * gpu_position_at_vertex(0)).xyz;
+ vec3 pos1 = (ProjectionMatrixInverse * gpu_position_at_vertex(1)).xyz;
+ vec3 pos2 = (ProjectionMatrixInverse * gpu_position_at_vertex(2)).xyz;
+ vec3 edge21 = pos2 - pos1;
+ vec3 edge10 = pos1 - pos0;
+ vec3 edge02 = pos0 - pos2;
+ vec3 d21 = safe_normalize(edge21);
+ vec3 d10 = safe_normalize(edge10);
+ vec3 d02 = safe_normalize(edge02);
+ vec3 dists;
+ float d = dot(d21, edge02);
+ dists.x = sqrt(dot(edge02, edge02) - d * d);
+ d = dot(d02, edge10);
+ dists.y = sqrt(dot(edge10, edge10) - d * d);
+ d = dot(d10, edge21);
+ dists.z = sqrt(dot(edge21, edge21) - d * d);
+ return dists.xyz;
+}
+#endif
+
+void init_globals_mesh(void)
+{
+#if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_MESH)
+ g_data.barycentric_coords = gpu_BaryCoord.xy;
+ g_data.barycentric_dists = barycentric_distances_get();
+#endif
+}
+
+void init_globals_curves(void)
+{
+#if 0 /* TODO */
+ /* Shade as a cylinder. */
+ float cos_theta = interp.curves_time_width / interp.curves_thickness;
+ float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
+ g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta);
+
+ g_data.is_strand = true;
+ g_data.hair_time = interp.curves_time;
+ g_data.hair_thickness = interp.curves_thickness;
+ g_data.hair_strand_id = interp.curves_strand_id;
+# if defined(USE_BARYCENTRICS) && defined(GPU_FRAGMENT_SHADER) && defined(MAT_GEOM_CURVES)
+ g_data.barycentric_coords = hair_resolve_barycentric(interp.barycentric_coords);
+# endif
+#endif
+}
+
+void init_globals_gpencil(void)
+{
+ /* Undo backface flip as the gpencil normal is already pointing towards the camera. */
+ g_data.N = interp.N;
+}
+
+void init_globals(void)
+{
+ /* Default values. */
+ g_data.P = interp.P;
+ g_data.N = safe_normalize(interp.N);
+ g_data.Ng = g_data.N;
+ g_data.is_strand = false;
+ g_data.hair_time = 0.0;
+ g_data.hair_thickness = 0.0;
+ g_data.hair_strand_id = 0;
+ g_data.ray_type = RAY_TYPE_CAMERA; /* TODO */
+ g_data.ray_depth = 0.0;
+ g_data.ray_length = distance(g_data.P, cameraPos);
+ g_data.barycentric_coords = vec2(0.0);
+ g_data.barycentric_dists = vec3(0.0);
+
+#ifdef GPU_FRAGMENT_SHADER
+ g_data.N = (FrontFacing) ? g_data.N : -g_data.N;
+ g_data.Ng = safe_normalize(cross(dFdx(g_data.P), dFdy(g_data.P)));
+#endif
+
+#if defined(MAT_GEOM_MESH)
+ init_globals_mesh();
+#elif defined(MAT_GEOM_CURVES)
+ init_globals_curves();
+#elif defined(MAT_GEOM_GPENCIL)
+ init_globals_gpencil();
+#endif
+}
+
+/* Avoid some compiler issue with non set interface parameters. */
+void init_interface()
+{
+#ifdef GPU_VERTEX_SHADER
+ interp.P = vec3(0.0);
+ interp.N = vec3(0.0);
+ interp.barycentric_coords = vec2(0.0);
+ interp.curves_binormal = vec3(0.0);
+ interp.curves_time = 0.0;
+ interp.curves_time_width = 0.0;
+ interp.curves_thickness = 0.0;
+ interp.curves_strand_id = 0;
+ drw_ResourceID_iface.resource_index = resource_id;
+#endif
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
new file mode 100644
index 00000000000..9901596623d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
@@ -0,0 +1,29 @@
+
+/**
+ * Background used to shade the world.
+ *
+ * Outputs shading parameter per pixel using a set of randomized BSDFs.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+
+void main(void)
+{
+ init_globals();
+ /* View position is passed to keep accuracy. */
+ g_data.N = normal_view_to_world(viewCameraVec(interp.P));
+ g_data.Ng = g_data.N;
+ g_data.P = -g_data.N + cameraPos;
+ attrib_load();
+
+ nodetree_surface();
+
+ g_holdout = saturate(g_holdout);
+
+ out_background.rgb = safe_color(g_emission) * (1.0 - g_holdout);
+ out_background.a = saturate(avg(g_transmittance)) * g_holdout;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
new file mode 100644
index 00000000000..13269d833e8
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
@@ -0,0 +1,176 @@
+
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Common
+ * \{ */
+
+/* TODO(@fclem): This is a bit out of place at the moment. */
+GPU_SHADER_CREATE_INFO(eevee_shared)
+ .typedef_source("eevee_defines.hh")
+ .typedef_source("eevee_shader_shared.hh");
+
+GPU_SHADER_CREATE_INFO(eevee_sampling_data)
+ .additional_info("eevee_shared")
+ .uniform_buf(14, "SamplingData", "sampling_buf");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Surface Mesh Type
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_geom_mesh)
+ .define("MAT_GEOM_MESH")
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_in(1, Type::VEC3, "nor")
+ .vertex_source("eevee_geom_mesh_vert.glsl")
+ .additional_info("draw_mesh", "draw_resource_id_varying", "draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(eevee_geom_gpencil)
+ .define("MAT_GEOM_GPENCIL")
+ .vertex_source("eevee_geom_gpencil_vert.glsl")
+ .additional_info("draw_gpencil", "draw_resource_id_varying", "draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(eevee_geom_curves)
+ .define("MAT_GEOM_CURVES")
+ .vertex_source("eevee_geom_curves_vert.glsl")
+ .additional_info("draw_hair", "draw_resource_id_varying", "draw_resource_handle");
+
+GPU_SHADER_CREATE_INFO(eevee_geom_world)
+ .define("MAT_GEOM_WORLD")
+ .builtins(BuiltinBits::VERTEX_ID)
+ .vertex_source("eevee_geom_world_vert.glsl")
+ .additional_info("draw_modelmat", "draw_resource_id_varying", "draw_resource_handle");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Surface
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp")
+ .smooth(Type::VEC3, "P")
+ .smooth(Type::VEC3, "N")
+ .smooth(Type::VEC2, "barycentric_coords")
+ .smooth(Type::VEC3, "curves_binormal")
+ .smooth(Type::FLOAT, "curves_time")
+ .smooth(Type::FLOAT, "curves_time_width")
+ .smooth(Type::FLOAT, "curves_thickness")
+ .flat(Type::INT, "curves_strand_id");
+
+#define image_out(slot, qualifier, format, name) \
+ image(slot, format, qualifier, ImageType::FLOAT_2D, name, Frequency::PASS)
+
+GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
+ .vertex_out(eevee_surf_iface)
+ /* Note: This removes the possibility of using gl_FragDepth. */
+ // .early_fragment_test(true)
+ /* Direct output. */
+ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
+ /* Gbuffer. */
+ // .image_out(0, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_color")
+ // .image_out(1, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_data")
+ // .image_out(2, Qualifier::WRITE, GPU_RGBA16F, "gbuff_transmit_normal")
+ // .image_out(3, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_reflection_color")
+ // .image_out(4, Qualifier::WRITE, GPU_RGBA16F, "gbuff_reflection_normal")
+ // .image_out(5, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_emission")
+ /* Renderpasses. */
+ // .image_out(6, Qualifier::READ_WRITE, GPU_RGBA16F, "rpass_volume_light")
+ /* TODO: AOVs maybe? */
+ .fragment_source("eevee_surf_deferred_frag.glsl")
+ // .additional_info("eevee_sampling_data", "eevee_utility_texture")
+ ;
+
+#undef image_out
+
+GPU_SHADER_CREATE_INFO(eevee_surf_forward)
+ .auto_resource_location(true)
+ .vertex_out(eevee_surf_iface)
+ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
+ .fragment_source("eevee_surf_forward_frag.glsl")
+ // .additional_info("eevee_sampling_data",
+ // "eevee_lightprobe_data",
+ /* Optionnally added depending on the material. */
+ // "eevee_raytrace_data",
+ // "eevee_transmittance_data",
+ // "eevee_utility_texture",
+ // "eevee_light_data",
+ // "eevee_shadow_data"
+ // )
+ ;
+
+GPU_SHADER_CREATE_INFO(eevee_surf_depth)
+ .vertex_out(eevee_surf_iface)
+ .fragment_source("eevee_surf_depth_frag.glsl")
+ // .additional_info("eevee_sampling_data", "eevee_utility_texture")
+ ;
+
+GPU_SHADER_CREATE_INFO(eevee_surf_world)
+ .vertex_out(eevee_surf_iface)
+ .fragment_out(0, Type::VEC4, "out_background")
+ .fragment_source("eevee_surf_world_frag.glsl")
+ // .additional_info("eevee_utility_texture")
+ ;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Volume
+ * \{ */
+
+#if 0 /* TODO */
+GPU_SHADER_INTERFACE_INFO(eevee_volume_iface, "interp")
+ .smooth(Type::VEC3, "P_start")
+ .smooth(Type::VEC3, "P_end");
+
+GPU_SHADER_CREATE_INFO(eevee_volume_deferred)
+ .sampler(0, ImageType::DEPTH_2D, "depth_max_tx")
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_out(eevee_volume_iface)
+ .fragment_out(0, Type::UVEC4, "out_volume_data")
+ .fragment_out(1, Type::VEC4, "out_transparency_data")
+ .additional_info("eevee_shared")
+ .vertex_source("eevee_volume_vert.glsl")
+ .fragment_source("eevee_volume_deferred_frag.glsl")
+ .additional_info("draw_fullscreen");
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Test shaders
+ *
+ * Variations that are only there to test shaders at compile time.
+ * \{ */
+
+#ifdef DEBUG
+
+/* Stub functions defined by the material evaluation. */
+GPU_SHADER_CREATE_INFO(eevee_material_stub).define("EEVEE_MATERIAL_STUBS");
+
+# define EEVEE_MAT_FINAL_VARIATION(name, ...) \
+ GPU_SHADER_CREATE_INFO(name) \
+ .additional_info(__VA_ARGS__) \
+ .auto_resource_location(true) \
+ .do_static_compilation(true);
+
+# define EEVEE_MAT_GEOM_VARIATIONS(prefix, ...) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_world, "eevee_geom_world", __VA_ARGS__) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_gpencil, "eevee_geom_gpencil", __VA_ARGS__) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_hair, "eevee_geom_curves", __VA_ARGS__) \
+ EEVEE_MAT_FINAL_VARIATION(prefix##_mesh, "eevee_geom_mesh", __VA_ARGS__)
+
+# define EEVEE_MAT_PIPE_VARIATIONS(name, ...) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_world, "eevee_surf_world", __VA_ARGS__) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_depth, "eevee_surf_depth", __VA_ARGS__) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_deferred, "eevee_surf_deferred", __VA_ARGS__) \
+ EEVEE_MAT_GEOM_VARIATIONS(name##_forward, "eevee_surf_forward", __VA_ARGS__)
+
+EEVEE_MAT_PIPE_VARIATIONS(eevee_surface, "eevee_material_stub")
+
+#endif
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_manager_profiling.h b/source/blender/draw/intern/draw_manager_profiling.h
index 5453fa5dc27..a64f139d8db 100644
--- a/source/blender/draw/intern/draw_manager_profiling.h
+++ b/source/blender/draw/intern/draw_manager_profiling.h
@@ -7,6 +7,10 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct rcti;
void DRW_stats_free(void);
@@ -27,3 +31,7 @@ void DRW_stats_query_start(const char *name);
void DRW_stats_query_end(void);
void DRW_stats_draw(const rcti *rect);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 93030d6ab6e..f9688e6c78c 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -411,6 +411,7 @@ list(APPEND SRC ${glsl_source_list_file})
list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SRC_SHADER_CREATE_INFOS
+ ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh
../draw/engines/overlay/shaders/infos/armature_info.hh