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:
authorJeroen Bakker <jbakker>2020-12-04 10:13:54 +0300
committerJeroen Bakker <jeroen@blender.org>2020-12-04 10:14:07 +0300
commit2bae11d5c08a9095f2c8ec5e465e73ada9840ed1 (patch)
tree1b256d7acff23d763758daa33282b9238ba72f5b /source/blender
parent2bd0263fbf2175c672d46c9df9eff7fd3ceecbce (diff)
EEVEE: Arbitrary Output Variables
This patch adds support for AOVs in EEVEE. AOV Outputs can be defined in the render pass tab and used in shader materials. Both Object and World based shaders are supported. The AOV can be previewed in the viewport using the renderpass selector in the shading popover. AOV names that conflict with other AOVs are automatically corrected. AOV conflicts with render passes get a warning icon. The reason behind this is that changing render engines/passes can change the conflict, but you might not notice it. Changing this automatically would also make the materials incorrect, so best to leave this to the user. **Implementation** The patch adds a copies the AOV structures of Cycles into Blender. The goal is that the Cycles will use Blenders AOV defintions. In the Blender kernel (`layer.c`) the logic of these structures are implemented. The GLSL shader of any GPUMaterial can hold multiple outputs (the main output and the AOV outputs) based on the renderPassUBO the right output is selected. This selection uses an hash that encodes the AOV structure. The full AOV needed to be encoded when actually drawing the material pass as the AOV type changes the behavior of the AOV. This isn't known yet when the GLSL is compiled. **Future Developments** * The AOV definitions in the render layer panel isn't shared with Cycles. Cycles should be migrated to use the same viewlayer aovs. During a previous attempt this failed as the AOV validation in cycles and in Blender have implementation differences what made it crash when an aov name was invalid. This could be fixed by extending the external render engine API. * Add support to Cycles to render AOVs in the 3d viewport. * Use a drop down list for selecting AOVs in the AOV Output node. * Give user feedback when multiple AOV output nodes with the same AOV name exists in the same shader. * Fix viewing single channel images in the image editor [T83314] * Reduce viewport render time by only render needed draw passes. [T83316] Reviewed By: Brecht van Lommel, Clément Foucault Differential Revision: https://developer.blender.org/D7010
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_appdir.h1
-rw-r--r--source/blender/blenkernel/BKE_layer.h10
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/intern/appdir.c8
-rw-r--r--source/blender/blenkernel/intern/layer.c162
-rw-r--r--source/blender/blenkernel/intern/layer_test.cc177
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.cc2
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c3
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c119
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h19
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c60
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c53
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl13
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl37
-rw-r--r--source/blender/editors/render/render_intern.h2
-rw-r--r--source/blender/editors/render/render_ops.c2
-rw-r--r--source/blender/editors/render/render_shading.c88
-rw-r--r--source/blender/gpu/CMakeLists.txt1
-rw-r--r--source/blender/gpu/GPU_material.h1
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c37
-rw-r--r--source/blender/gpu/intern/gpu_material.c8
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c7
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c4
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h10
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl13
-rw-r--r--source/blender/makesdna/DNA_layer_types.h29
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h1
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_internal.h4
-rw-r--r--source/blender/makesrna/intern/rna_layer.c13
-rw-r--r--source/blender/makesrna/intern/rna_scene.c95
-rw-r--r--source/blender/makesrna/intern/rna_space.c96
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c15
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_aov.c19
-rw-r--r--source/blender/windowmanager/WM_api.h3
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c1
-rw-r--r--source/blender/windowmanager/intern/wm_window.c11
37 files changed, 1061 insertions, 65 deletions
diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h
index 3e52d7f3301..6da6079ea2a 100644
--- a/source/blender/blenkernel/BKE_appdir.h
+++ b/source/blender/blenkernel/BKE_appdir.h
@@ -26,6 +26,7 @@ extern "C" {
struct ListBase;
void BKE_appdir_init(void);
+void BKE_appdir_exit(void);
/* note on naming: typical _get() suffix is omitted here,
* since its the main purpose of the API. */
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index 7091a060243..e5fab35891c 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -42,6 +42,7 @@ struct Depsgraph;
struct LayerCollection;
struct Main;
struct Object;
+struct RenderEngine;
struct Scene;
struct View3D;
struct ViewLayer;
@@ -444,6 +445,15 @@ bool BKE_view_layer_filter_edit_mesh_has_edges(struct Object *ob, void *user_dat
BKE_view_layer_array_from_objects_in_mode( \
view_layer, v3d, r_len, {.object_mode = mode, .no_dup_data = true})
+struct ViewLayerAOV *BKE_view_layer_add_aov(struct ViewLayer *view_layer);
+void BKE_view_layer_remove_aov(struct ViewLayer *view_layer, struct ViewLayerAOV *aov);
+void BKE_view_layer_set_active_aov(struct ViewLayer *view_layer, struct ViewLayerAOV *aov);
+void BKE_view_layer_verify_aov(struct RenderEngine *engine,
+ struct Scene *scene,
+ struct ViewLayer *view_layer);
+bool BKE_view_layer_has_valid_aov(struct ViewLayer *view_layer);
+ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, struct ViewLayerAOV *view_layer_aov);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 1a90d6fadd3..a328c600eac 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -726,6 +726,7 @@ if(WITH_GTESTS)
intern/fcurve_test.cc
intern/lattice_deform_test.cc
intern/tracking_test.cc
+ intern/layer_test.cc
)
set(TEST_INC
../editors/include
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 2038079744d..b1462167edd 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -114,6 +114,14 @@ void BKE_appdir_init(void)
#endif
}
+void BKE_appdir_exit(void)
+{
+#ifndef NDEBUG
+ BLI_assert(is_appdir_init == true);
+ is_appdir_init = false;
+#endif
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 73b330bc0de..6b346d7a337 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -57,6 +57,8 @@
#include "DRW_engine.h"
+#include "RE_engine.h"
+
#include "MEM_guardedalloc.h"
#include "BLO_read_write.h"
@@ -279,6 +281,8 @@ void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user)
}
}
BLI_freelistN(&view_layer->drawdata);
+ BLI_freelistN(&view_layer->aovs);
+ view_layer->active_aov = NULL;
MEM_SAFE_FREE(view_layer->stats);
@@ -412,6 +416,28 @@ void BKE_view_layer_base_select_and_set_active(struct ViewLayer *view_layer, Bas
}
/**************************** Copy View Layer and Layer Collections ***********************/
+static void layer_aov_copy_data(ViewLayer *view_layer_dst,
+ const ViewLayer *view_layer_src,
+ ListBase *aovs_dst,
+ const ListBase *aovs_src)
+{
+ if (aovs_src != NULL) {
+ BLI_duplicatelist(aovs_dst, aovs_src);
+ }
+
+ ViewLayerAOV *aov_dst = aovs_dst->first;
+ const ViewLayerAOV *aov_src = aovs_src->first;
+
+ while (aov_dst != NULL) {
+ BLI_assert(aov_src);
+ if (aov_src == view_layer_src->active_aov) {
+ view_layer_dst->active_aov = aov_dst;
+ }
+
+ aov_dst = aov_dst->next;
+ aov_src = aov_src->next;
+ }
+}
static void layer_collections_copy_data(ViewLayer *view_layer_dst,
const ViewLayer *view_layer_src,
@@ -482,6 +508,10 @@ void BKE_view_layer_copy_data(Scene *scene_dst,
LayerCollection *lc_scene_dst = view_layer_dst->layer_collections.first;
lc_scene_dst->collection = scene_dst->master_collection;
+ BLI_listbase_clear(&view_layer_dst->aovs);
+ layer_aov_copy_data(
+ view_layer_dst, view_layer_src, &view_layer_dst->aovs, &view_layer_src->aovs);
+
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)view_layer_dst->mat_override);
}
@@ -1864,6 +1894,9 @@ void BKE_view_layer_blend_write(BlendWriter *writer, ViewLayer *view_layer)
LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) {
BLO_write_struct(writer, FreestyleLineSet, fls);
}
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ BLO_write_struct(writer, ViewLayerAOV, aov);
+ }
write_layer_collections(writer, &view_layer->layer_collections);
}
@@ -1899,6 +1932,9 @@ void BKE_view_layer_blend_read_data(BlendDataReader *reader, ViewLayer *view_lay
BLO_read_list(reader, &(view_layer->freestyle_config.modules));
BLO_read_list(reader, &(view_layer->freestyle_config.linesets));
+ BLO_read_list(reader, &view_layer->aovs);
+ BLO_read_data_address(reader, &view_layer->active_aov);
+
BLI_listbase_clear(&view_layer->drawdata);
view_layer->object_bases_array = NULL;
view_layer->object_bases_hash = NULL;
@@ -1952,3 +1988,129 @@ void BKE_view_layer_blend_read_lib(BlendLibReader *reader, Library *lib, ViewLay
IDP_BlendReadLib(reader, view_layer->id_properties);
}
+
+/* -------------------------------------------------------------------- */
+/** \name Shader AOV
+ * \{ */
+
+static void viewlayer_aov_make_name_unique(ViewLayer *view_layer)
+{
+ ViewLayerAOV *aov = view_layer->active_aov;
+ if (aov == NULL) {
+ return;
+ }
+ BLI_uniquename(
+ &view_layer->aovs, aov, DATA_("AOV"), '.', offsetof(ViewLayerAOV, name), sizeof(aov->name));
+}
+
+static void viewlayer_aov_active_set(ViewLayer *view_layer, ViewLayerAOV *aov)
+{
+ if (aov != NULL) {
+ BLI_assert(BLI_findindex(&view_layer->aovs, aov) != -1);
+ view_layer->active_aov = aov;
+ }
+ else {
+ view_layer->active_aov = NULL;
+ }
+}
+
+struct ViewLayerAOV *BKE_view_layer_add_aov(struct ViewLayer *view_layer)
+{
+ ViewLayerAOV *aov;
+ aov = MEM_callocN(sizeof(ViewLayerAOV), __func__);
+ aov->type = AOV_TYPE_COLOR;
+ BLI_strncpy(aov->name, DATA_("AOV"), sizeof(aov->name));
+ BLI_addtail(&view_layer->aovs, aov);
+ viewlayer_aov_active_set(view_layer, aov);
+ viewlayer_aov_make_name_unique(view_layer);
+ return aov;
+}
+
+void BKE_view_layer_remove_aov(ViewLayer *view_layer, ViewLayerAOV *aov)
+{
+ BLI_assert(BLI_findindex(&view_layer->aovs, aov) != -1);
+ BLI_assert(aov != NULL);
+ if (view_layer->active_aov == aov) {
+ if (aov->next) {
+ viewlayer_aov_active_set(view_layer, aov->next);
+ }
+ else {
+ viewlayer_aov_active_set(view_layer, aov->prev);
+ }
+ }
+ BLI_freelinkN(&view_layer->aovs, aov);
+}
+
+void BKE_view_layer_set_active_aov(ViewLayer *view_layer, ViewLayerAOV *aov)
+{
+ viewlayer_aov_active_set(view_layer, aov);
+}
+
+static void bke_view_layer_verify_aov_cb(void *userdata,
+ Scene *UNUSED(scene),
+ ViewLayer *UNUSED(view_layer),
+ const char *name,
+ int UNUSED(channels),
+ const char *UNUSED(chanid),
+ int UNUSED(type))
+{
+ GHash *name_count = userdata;
+ void **value_p;
+ void *key = BLI_strdup(name);
+
+ if (!BLI_ghash_ensure_p(name_count, key, &value_p)) {
+ *value_p = POINTER_FROM_INT(1);
+ }
+ else {
+ int value = POINTER_AS_INT(*value_p);
+ value++;
+ *value_p = POINTER_FROM_INT(value);
+ MEM_freeN(key);
+ }
+}
+
+/* Update the naming and conflicts of the AOVs.
+ *
+ * Name must be unique between all AOVs.
+ * Conflicts with render passes will show a conflict icon. Reason is that switching a render
+ * engine or activating a render pass could lead to other conflicts that wouldn't be that clear
+ * for the user. */
+void BKE_view_layer_verify_aov(struct RenderEngine *engine,
+ struct Scene *scene,
+ struct ViewLayer *view_layer)
+{
+ viewlayer_aov_make_name_unique(view_layer);
+
+ GHash *name_count = BLI_ghash_str_new(__func__);
+ RE_engine_update_render_passes(
+ engine, scene, view_layer, bke_view_layer_verify_aov_cb, name_count);
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ void **value_p = BLI_ghash_lookup(name_count, aov->name);
+ int count = POINTER_AS_INT(value_p);
+ SET_FLAG_FROM_TEST(aov->flag, count > 1, AOV_CONFLICT);
+ }
+ BLI_ghash_free(name_count, MEM_freeN, NULL);
+}
+
+/* Check if the given view layer has at least one valid AOV. */
+bool BKE_view_layer_has_valid_aov(ViewLayer *view_layer)
+{
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, struct ViewLayerAOV *aov)
+{
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ if (BLI_findindex(&view_layer->aovs, aov) != -1) {
+ return view_layer;
+ }
+ }
+ return NULL;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/layer_test.cc b/source/blender/blenkernel/intern/layer_test.cc
new file mode 100644
index 00000000000..84a96ed0895
--- /dev/null
+++ b/source/blender/blenkernel/intern/layer_test.cc
@@ -0,0 +1,177 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 by Blender Foundation.
+ */
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_appdir.h"
+#include "BKE_idtype.h"
+#include "BKE_layer.h"
+
+#include "BLI_string.h"
+
+#include "RE_engine.h"
+
+#include "IMB_imbuf.h"
+
+#include "CLG_log.h"
+
+#include "RNA_access.h"
+
+namespace blender::bke::tests {
+
+TEST(view_layer, aov_unique_names)
+{
+ /* Set Up */
+ CLG_init();
+ BKE_appdir_init();
+ IMB_init();
+ RE_engines_init();
+
+ Scene scene = {{NULL}};
+ IDType_ID_SCE.init_data(&scene.id);
+ ViewLayer *view_layer = static_cast<ViewLayer *>(scene.view_layers.first);
+
+ RenderEngineType *engine_type = RE_engines_find(scene.r.engine);
+ RenderEngine *engine = RE_engine_create(engine_type);
+
+ EXPECT_FALSE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_EQ(view_layer->active_aov, nullptr);
+
+ /* Add an AOV */
+ ViewLayerAOV *aov1 = BKE_view_layer_add_aov(view_layer);
+ BKE_view_layer_verify_aov(engine, &scene, view_layer);
+ EXPECT_EQ(view_layer->active_aov, aov1);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0);
+
+ /* Add a second AOV */
+ ViewLayerAOV *aov2 = BKE_view_layer_add_aov(view_layer);
+ BKE_view_layer_verify_aov(engine, &scene, view_layer);
+ EXPECT_EQ(view_layer->active_aov, aov2);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0);
+ EXPECT_FALSE((aov2->flag & AOV_CONFLICT) != 0);
+ EXPECT_TRUE(STREQ(aov1->name, "AOV"));
+ EXPECT_TRUE(STREQ(aov2->name, "AOV.001"));
+
+ /* Revert previous resolution */
+ BLI_strncpy(aov2->name, "AOV", MAX_NAME);
+ BKE_view_layer_verify_aov(engine, &scene, view_layer);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0);
+ EXPECT_FALSE((aov2->flag & AOV_CONFLICT) != 0);
+ EXPECT_TRUE(STREQ(aov1->name, "AOV"));
+ EXPECT_TRUE(STREQ(aov2->name, "AOV.001"));
+
+ /* Resolve by removing AOV resolution */
+ BKE_view_layer_remove_aov(view_layer, aov2);
+ aov2 = NULL;
+ BKE_view_layer_verify_aov(engine, &scene, view_layer);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0);
+
+ /* Tear down */
+ RE_engine_free(engine);
+ RE_engines_exit();
+ IDType_ID_SCE.free_data(&scene.id);
+ IMB_exit();
+ BKE_appdir_exit();
+ CLG_exit();
+}
+
+static void test_render_pass_conflict(Scene *scene,
+ RenderEngine *engine,
+ ViewLayer *view_layer,
+ ViewLayerAOV *aov,
+ const char *render_pass_name,
+ const char *rna_prop_name)
+{
+ PointerRNA ptr;
+ RNA_pointer_create(&scene->id, &RNA_ViewLayer, view_layer, &ptr);
+ RNA_boolean_set(&ptr, rna_prop_name, false);
+
+ /* Rename to Conflicting name */
+ BLI_strncpy(aov->name, render_pass_name, MAX_NAME);
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov->flag & AOV_CONFLICT) != 0);
+ EXPECT_TRUE(STREQ(aov->name, render_pass_name));
+
+ /* Activate render pass */
+ RNA_boolean_set(&ptr, rna_prop_name, true);
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ EXPECT_FALSE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_TRUE((aov->flag & AOV_CONFLICT) != 0);
+ EXPECT_TRUE(STREQ(aov->name, render_pass_name));
+
+ /* Deactivate render pass */
+ RNA_boolean_set(&ptr, rna_prop_name, false);
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov->flag & AOV_CONFLICT) != 0);
+ EXPECT_TRUE(STREQ(aov->name, render_pass_name));
+}
+
+TEST(view_layer, aov_conflict)
+{
+ /* Set Up */
+ CLG_init();
+ BKE_appdir_init();
+ IMB_init();
+ RE_engines_init();
+
+ Scene scene = {{NULL}};
+ IDType_ID_SCE.init_data(&scene.id);
+ ViewLayer *view_layer = static_cast<ViewLayer *>(scene.view_layers.first);
+
+ RenderEngineType *engine_type = RE_engines_find(scene.r.engine);
+ RenderEngine *engine = RE_engine_create(engine_type);
+
+ EXPECT_FALSE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_EQ(view_layer->active_aov, nullptr);
+
+ /* Add an AOV */
+ ViewLayerAOV *aov = BKE_view_layer_add_aov(view_layer);
+ BKE_view_layer_verify_aov(engine, &scene, view_layer);
+ EXPECT_EQ(view_layer->active_aov, aov);
+ EXPECT_TRUE(BKE_view_layer_has_valid_aov(view_layer));
+ EXPECT_FALSE((aov->flag & AOV_CONFLICT) != 0);
+
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "Depth", "use_pass_z");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "Normal", "use_pass_normal");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "Mist", "use_pass_mist");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "Shadow", "use_pass_shadow");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "AO", "use_pass_ambient_occlusion");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "Emit", "use_pass_emit");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "Env", "use_pass_environment");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "DiffDir", "use_pass_diffuse_direct");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "DiffCol", "use_pass_diffuse_color");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "GlossDir", "use_pass_glossy_direct");
+ test_render_pass_conflict(&scene, engine, view_layer, aov, "GlossCol", "use_pass_glossy_color");
+
+ /* Tear down */
+ RE_engine_free(engine);
+ RE_engines_exit();
+ IDType_ID_SCE.free_data(&scene.id);
+ IMB_exit();
+ BKE_appdir_exit();
+ CLG_exit();
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
index a3aabf6ac10..8d8dc3aebf7 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
@@ -103,7 +103,7 @@ void BlendfileLoadingBaseTest::TearDownTestCase()
BKE_blender_atexit();
BKE_tempdir_session_purge();
-
+ BKE_appdir_exit();
CLG_exit();
testing::Test::TearDownTestCase();
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index 5c4ee015c86..47068d0b843 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -240,6 +240,9 @@ void EEVEE_view_layer_data_free(void *storage)
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment);
+ for (int aov_index = 0; aov_index < MAX_AOVS; aov_index++) {
+ DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
+ }
if (sldata->material_cache) {
BLI_memblock_destroy(sldata->material_cache, NULL);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 58f182ecf8d..c7a8f7729eb 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -244,31 +244,31 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
/* Create RenderPass UBO */
if (sldata->renderpass_ubo.combined == NULL) {
EEVEE_RenderPassData data;
- data = (EEVEE_RenderPassData){true, true, true, true, true, false, false};
+ data = (EEVEE_RenderPassData){true, true, true, true, true, false, false, false, 0};
sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.combined");
- data = (EEVEE_RenderPassData){true, false, false, false, false, true, false};
+ data = (EEVEE_RenderPassData){true, false, false, false, false, true, false, false, 0};
sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.diff_color");
- data = (EEVEE_RenderPassData){true, true, false, false, false, false, false};
+ data = (EEVEE_RenderPassData){true, true, false, false, false, false, false, false, 0};
sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.diff_light");
- data = (EEVEE_RenderPassData){false, false, true, false, false, false, false};
+ data = (EEVEE_RenderPassData){false, false, true, false, false, false, false, false, 0};
sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.spec_color");
- data = (EEVEE_RenderPassData){false, false, true, true, false, false, false};
+ data = (EEVEE_RenderPassData){false, false, true, true, false, false, false, false, 0};
sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.spec_light");
- data = (EEVEE_RenderPassData){false, false, false, false, true, false, false};
+ data = (EEVEE_RenderPassData){false, false, false, false, true, false, false, false, 0};
sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.emit");
- data = (EEVEE_RenderPassData){true, true, true, true, true, false, true};
+ data = (EEVEE_RenderPassData){true, true, true, true, true, false, true, false, 0};
sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.environment");
}
@@ -276,6 +276,51 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
/* Used combined pass by default. */
g_data->renderpass_ubo = sldata->renderpass_ubo.combined;
+ {
+ g_data->num_aovs_used = 0;
+ if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
+ EEVEE_RenderPassData data = {true, true, true, true, true, false, false, true, 0};
+ if (stl->g_data->aov_hash == EEVEE_AOV_HASH_ALL) {
+ ViewLayer *view_layer = draw_ctx->view_layer;
+ int aov_index = 0;
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ if (aov_index == MAX_AOVS) {
+ break;
+ }
+ data.renderPassAOVActive = EEVEE_renderpasses_aov_hash(aov);
+ if (sldata->renderpass_ubo.aovs[aov_index]) {
+ GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[aov_index], &data);
+ }
+ else {
+ sldata->renderpass_ubo.aovs[aov_index] = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.aovs");
+ }
+ aov_index++;
+ }
+ g_data->num_aovs_used = aov_index;
+ }
+ else {
+ /* Rendering a single AOV in the 3d viewport */
+ data.renderPassAOVActive = stl->g_data->aov_hash;
+ if (sldata->renderpass_ubo.aovs[0]) {
+ GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[0], &data);
+ }
+ else {
+ sldata->renderpass_ubo.aovs[0] = GPU_uniformbuf_create_ex(
+ sizeof(data), &data, "renderpass_ubo.aovs");
+ }
+ g_data->num_aovs_used = 1;
+ }
+ }
+ /* Free AOV UBO's that are not in use. */
+ for (int aov_index = g_data->num_aovs_used; aov_index < MAX_AOVS; aov_index++) {
+ DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
+ }
+ }
+
/* HACK: EEVEE_material_get can create a new context. This can only be
* done when there is no active framebuffer. We do this here otherwise
* `EEVEE_renderpasses_output_init` will fail. It cannot be done in
@@ -949,6 +994,11 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
material_renderpass_init(fbl, &txl->spec_color_accum, texture_format, do_clear);
}
+ if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
+ for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
+ material_renderpass_init(fbl, &txl->aov_surface_accum[aov_index], texture_format, do_clear);
+ }
+ }
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
material_renderpass_init(fbl, &txl->spec_light_accum, texture_format, do_clear);
@@ -960,6 +1010,7 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
DRWPass *renderpass,
+ DRWPass *renderpass2,
EEVEE_PrivateData *pd,
GPUTexture *output_tx,
struct GPUUniformBuf *renderpass_option_ubo)
@@ -969,6 +1020,9 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
pd->renderpass_ubo = renderpass_option_ubo;
DRW_draw_pass(renderpass);
+ if (renderpass2) {
+ DRW_draw_pass(renderpass2);
+ }
GPU_framebuffer_texture_detach(fbl->material_accum_fb, output_tx);
}
@@ -983,38 +1037,69 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
if (fbl->material_accum_fb != NULL) {
DRWPass *material_accum_ps = psl->material_accum_ps;
+ DRWPass *background_accum_ps = psl->background_accum_ps;
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
material_renderpass_accumulate(
- fbl, psl->background_accum_ps, pd, txl->env_accum, sldata->renderpass_ubo.environment);
+ fbl, background_accum_ps, NULL, pd, txl->env_accum, sldata->renderpass_ubo.environment);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
material_renderpass_accumulate(
- fbl, material_accum_ps, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
+ fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_accumulate(
- fbl, material_accum_ps, pd, txl->diff_color_accum, sldata->renderpass_ubo.diff_color);
+ material_renderpass_accumulate(fbl,
+ material_accum_ps,
+ NULL,
+ pd,
+ txl->diff_color_accum,
+ sldata->renderpass_ubo.diff_color);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_accumulate(
- fbl, material_accum_ps, pd, txl->diff_light_accum, sldata->renderpass_ubo.diff_light);
+ material_renderpass_accumulate(fbl,
+ material_accum_ps,
+ NULL,
+ pd,
+ txl->diff_light_accum,
+ sldata->renderpass_ubo.diff_light);
if (effects->enabled_effects & EFFECT_SSS) {
EEVEE_subsurface_output_accumulate(sldata, vedata);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- material_renderpass_accumulate(
- fbl, material_accum_ps, pd, txl->spec_color_accum, sldata->renderpass_ubo.spec_color);
+ material_renderpass_accumulate(fbl,
+ material_accum_ps,
+ NULL,
+ pd,
+ txl->spec_color_accum,
+ sldata->renderpass_ubo.spec_color);
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_accumulate(
- fbl, material_accum_ps, pd, txl->spec_light_accum, sldata->renderpass_ubo.spec_light);
+ material_renderpass_accumulate(fbl,
+ material_accum_ps,
+ NULL,
+ pd,
+ txl->spec_light_accum,
+ sldata->renderpass_ubo.spec_light);
if (effects->enabled_effects & EFFECT_SSR) {
EEVEE_reflection_output_accumulate(sldata, vedata);
}
}
+ if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
+ for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
+ material_renderpass_accumulate(fbl,
+ material_accum_ps,
+ background_accum_ps,
+ pd,
+ txl->aov_surface_accum[aov_index],
+ sldata->renderpass_ubo.aovs[aov_index]);
+ }
+ }
+ /* Free unused aov textures. */
+ for (int aov_index = pd->num_aovs_used; aov_index < MAX_AOVS; aov_index++) {
+ DRW_TEXTURE_FREE_SAFE(txl->aov_surface_accum[aov_index]);
+ }
/* Restore default. */
pd->renderpass_ubo = sldata->renderpass_ubo.combined;
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index f5cef8f3c25..1385721a569 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -53,6 +53,7 @@ extern struct DrawEngineType draw_engine_eevee_type;
#define MAX_SHADOW_CASCADE 8
#define MAX_SHADOW_CUBE (MAX_SHADOW - MAX_CASCADE_NUM * MAX_SHADOW_CASCADE)
#define MAX_BLOOM_STEP 16
+#define MAX_AOVS 64
// #define DEBUG_SHADOW_DISTRIBUTION
@@ -163,8 +164,9 @@ BLI_INLINE bool eevee_hdri_preview_overlay_enabled(const View3D *v3d)
#define EEVEE_RENDERPASSES_MATERIAL \
(EEVEE_RENDER_PASS_EMIT | EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_DIFFUSE_LIGHT | \
EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_SPECULAR_LIGHT | \
- EEVEE_RENDER_PASS_ENVIRONMENT)
-
+ EEVEE_RENDER_PASS_ENVIRONMENT | EEVEE_RENDER_PASS_AOV)
+#define EEVEE_AOV_HASH_ALL -1
+#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1
/* Material shader variations */
enum {
VAR_MAT_MESH = (1 << 0),
@@ -376,6 +378,7 @@ typedef struct EEVEE_TextureList {
struct GPUTexture *diff_light_accum;
struct GPUTexture *spec_color_accum;
struct GPUTexture *spec_light_accum;
+ struct GPUTexture *aov_surface_accum[MAX_AOVS];
struct GPUTexture *emit_accum;
struct GPUTexture *bloom_accum;
struct GPUTexture *ssr_accum;
@@ -430,7 +433,9 @@ typedef struct EEVEE_RenderPassData {
int renderPassEmit;
int renderPassSSSColor;
int renderPassEnvironment;
- int _pad[1];
+ int renderPassAOV;
+ int renderPassAOVActive;
+ int _pad[3];
} EEVEE_RenderPassData;
/* ************ LIGHT UBO ************* */
@@ -860,6 +865,7 @@ typedef struct EEVEE_ViewLayerData {
struct GPUUniformBuf *spec_color;
struct GPUUniformBuf *spec_light;
struct GPUUniformBuf *emit;
+ struct GPUUniformBuf *aovs[MAX_AOVS];
} renderpass_ubo;
/* Common Uniform Buffer */
@@ -959,6 +965,9 @@ typedef struct EEVEE_PrivateData {
/* Renderpasses */
/* Bitmask containing the active render_passes */
eViewLayerEEVEEPassType render_passes;
+ int aov_hash;
+ int num_aovs_used;
+
/* Uniform references that are referenced inside the `renderpass_pass`. They are updated
* to reuse the drawing pass and the shading group. */
int renderpass_type;
@@ -1284,10 +1293,12 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
bool post_effect);
void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
- eViewLayerEEVEEPassType renderpass_type);
+ eViewLayerEEVEEPassType renderpass_type,
+ int aov_index);
void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata);
bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata);
+int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov);
/* eevee_temporal_sampling.c */
void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 504e4e1d336..32e6eac2402 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -301,7 +301,7 @@ static void eevee_render_result_normal(RenderLayer *rl,
}
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL);
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_NORMAL, 3, vedata->fbl->renderpass_fb, vedata);
}
@@ -321,7 +321,7 @@ static void eevee_render_result_z(RenderLayer *rl,
}
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_Z) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z);
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_Z, 1, vedata->fbl->renderpass_fb, vedata);
}
@@ -334,7 +334,7 @@ static void eevee_render_result_mist(RenderLayer *rl,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST);
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_MIST, 1, vedata->fbl->renderpass_fb, vedata);
}
@@ -347,7 +347,7 @@ static void eevee_render_result_shadow(RenderLayer *rl,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW);
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_SHADOW, 3, vedata->fbl->renderpass_fb, vedata);
}
@@ -360,7 +360,7 @@ static void eevee_render_result_occlusion(RenderLayer *rl,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO);
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_AO, 3, vedata->fbl->renderpass_fb, vedata);
}
@@ -378,7 +378,7 @@ static void eevee_render_result_bloom(RenderLayer *rl,
}
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM);
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_BLOOM, 3, vedata->fbl->renderpass_fb, vedata);
}
@@ -386,7 +386,7 @@ static void eevee_render_result_bloom(RenderLayer *rl,
#define EEVEE_RENDER_RESULT_MATERIAL_PASS(pass_name, eevee_pass_type) \
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_##eevee_pass_type) != 0) { \
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type); \
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type, 0); \
eevee_render_color_result( \
rl, viewname, rect, RE_PASSNAME_##pass_name, 3, vedata->fbl->renderpass_fb, vedata); \
}
@@ -462,6 +462,35 @@ static void eevee_render_result_volume_transmittance(RenderLayer *rl,
EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_TRANSMITTANCE, VOLUME_TRANSMITTANCE)
}
+static void eevee_render_result_aovs(RenderLayer *rl,
+ const char *viewname,
+ const rcti *rect,
+ EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata)
+{
+ if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ ViewLayer *view_layer = draw_ctx->view_layer;
+ int aov_index = 0;
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AOV, aov_index);
+ switch (aov->type) {
+ case AOV_TYPE_COLOR:
+ eevee_render_color_result(
+ rl, viewname, rect, aov->name, 4, vedata->fbl->renderpass_fb, vedata);
+ break;
+ case AOV_TYPE_VALUE:
+ eevee_render_color_result(
+ rl, viewname, rect, aov->name, 1, vedata->fbl->renderpass_fb, vedata);
+ }
+ aov_index++;
+ }
+ }
+}
+
#undef EEVEE_RENDER_RESULT_MATERIAL_PASS
static void eevee_render_draw_background(EEVEE_Data *vedata)
@@ -641,6 +670,7 @@ void EEVEE_render_read_result(EEVEE_Data *vedata,
eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata);
+ eevee_render_result_aovs(rl, viewname, rect, vedata, sldata);
}
void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
@@ -675,6 +705,22 @@ void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *v
CHECK_PASS_EEVEE(VOLUME_TRANSMITTANCE, SOCK_RGBA, 3, "RGB");
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ switch (aov->type) {
+ case AOV_TYPE_COLOR:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
+ break;
+ case AOV_TYPE_VALUE:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
+ break;
+ default:
+ break;
+ }
+ }
+
#undef CHECK_PASS_LEGACY
#undef CHECK_PASS_EEVEE
}
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index be73225b348..3f75f10b204 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -27,6 +27,7 @@
#include "BKE_global.h" /* for G.debug_value */
+#include "BLI_hash.h"
#include "BLI_string_utils.h"
#include "DEG_depsgraph_query.h"
@@ -36,12 +37,13 @@
typedef enum eRenderPassPostProcessType {
PASS_POST_UNDEFINED = 0,
PASS_POST_ACCUMULATED_COLOR = 1,
- PASS_POST_ACCUMULATED_LIGHT = 2,
- PASS_POST_ACCUMULATED_VALUE = 3,
- PASS_POST_DEPTH = 4,
- PASS_POST_AO = 5,
- PASS_POST_NORMAL = 6,
- PASS_POST_TWO_LIGHT_BUFFERS = 7,
+ PASS_POST_ACCUMULATED_COLOR_ALPHA = 2,
+ PASS_POST_ACCUMULATED_LIGHT = 3,
+ PASS_POST_ACCUMULATED_VALUE = 4,
+ PASS_POST_DEPTH = 5,
+ PASS_POST_AO = 6,
+ PASS_POST_NORMAL = 7,
+ PASS_POST_TWO_LIGHT_BUFFERS = 8,
} eRenderPassPostProcessType;
/* bitmask containing all renderpasses that need post-processing */
@@ -70,6 +72,15 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0;
}
+/* Calculate the hash for an AOV. The least significant bit is used to store the AOV
+ * type the rest of the bits are used for the name hash. */
+int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
+{
+ int hash = BLI_hash_string(aov->name);
+ SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK);
+ return hash;
+}
+
void EEVEE_renderpasses_init(EEVEE_Data *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
@@ -81,10 +92,24 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata)
if (v3d) {
const Scene *scene = draw_ctx->scene;
eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass;
+ g_data->aov_hash = 0;
+
if (render_pass == EEVEE_RENDER_PASS_BLOOM &&
((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
render_pass = EEVEE_RENDER_PASS_COMBINED;
}
+ if (render_pass == EEVEE_RENDER_PASS_AOV) {
+ ViewLayerAOV *aov = BLI_findstring(
+ &view_layer->aovs, v3d->shading.aov_name, offsetof(ViewLayerAOV, name));
+ if (aov != NULL) {
+ g_data->aov_hash = EEVEE_renderpasses_aov_hash(aov);
+ }
+ else {
+ /* AOV not found in view layer. */
+ render_pass = EEVEE_RENDER_PASS_COMBINED;
+ }
+ }
+
g_data->render_passes = render_pass;
}
else {
@@ -110,10 +135,14 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata)
ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
#undef ENABLE_FROM_LEGACY
+ if (DRW_state_is_image_render() && !BLI_listbase_is_empty(&view_layer->aovs)) {
+ enabled_render_passes |= EEVEE_RENDER_PASS_AOV;
+ g_data->aov_hash = EEVEE_AOV_HASH_ALL;
+ }
+
g_data->render_passes = (enabled_render_passes & EEVEE_RENDERPASSES_ALL) |
EEVEE_RENDER_PASS_COMBINED;
}
-
EEVEE_material_renderpasses_init(vedata);
}
@@ -216,7 +245,8 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve
* After invoking this function the active frame-buffer is set to `vedata->fbl->renderpass_fb`. */
void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
- eViewLayerEEVEEPassType renderpass_type)
+ eViewLayerEEVEEPassType renderpass_type,
+ int aov_index)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
@@ -311,6 +341,11 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata),
}
break;
}
+ case EEVEE_RENDER_PASS_AOV: {
+ g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR_ALPHA;
+ g_data->renderpass_input = txl->aov_surface_accum[aov_index];
+ break;
+ }
case EEVEE_RENDER_PASS_BLOOM: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
g_data->renderpass_input = txl->bloom_accum;
@@ -392,7 +427,7 @@ void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
}
if (is_valid) {
- EEVEE_renderpasses_postprocess(sldata, vedata, render_pass);
+ EEVEE_renderpasses_postprocess(sldata, vedata, render_pass, 0);
GPU_framebuffer_bind(dfbl->default_fb);
DRW_transform_none(txl->renderpass);
}
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
index 36cf3cecf40..3e0a5e76d00 100644
--- a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
@@ -1,3 +1,4 @@
+#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1
/* ---------------------------------------------------------------------- */
/** \name Resources
@@ -12,6 +13,8 @@ layout(std140) uniform renderpass_block
bool renderPassEmit;
bool renderPassSSSColor;
bool renderPassEnvironment;
+ bool renderPassAOV;
+ int renderPassAOVActive;
};
/** \} */
@@ -40,4 +43,14 @@ vec3 render_pass_emission_mask(vec3 emission_light)
return renderPassEmit ? emission_light : vec3(0.0);
}
+bool render_pass_aov_is_color()
+{
+ return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0;
+}
+
+int render_pass_aov_hash()
+{
+ return renderPassAOVActive & ~EEVEE_AOV_HASH_COLOR_TYPE_MASK;
+}
+
/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
index 89a411bc7cb..eb6ca4b9de8 100644
--- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
@@ -4,12 +4,13 @@
#define PASS_POST_UNDEFINED 0
#define PASS_POST_ACCUMULATED_COLOR 1
-#define PASS_POST_ACCUMULATED_LIGHT 2
-#define PASS_POST_ACCUMULATED_VALUE 3
-#define PASS_POST_DEPTH 4
-#define PASS_POST_AO 5
-#define PASS_POST_NORMAL 6
-#define PASS_POST_TWO_LIGHT_BUFFERS 7
+#define PASS_POST_ACCUMULATED_COLOR_ALPHA 2
+#define PASS_POST_ACCUMULATED_LIGHT 3
+#define PASS_POST_ACCUMULATED_VALUE 4
+#define PASS_POST_DEPTH 5
+#define PASS_POST_AO 6
+#define PASS_POST_NORMAL 7
+#define PASS_POST_TWO_LIGHT_BUFFERS 8
uniform int postProcessType;
uniform int currentSample;
@@ -55,7 +56,7 @@ vec3 safe_divide_even_color(vec3 a, vec3 b)
void main()
{
- vec3 color;
+ vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
ivec2 texel = ivec2(gl_FragCoord.xy);
if (postProcessType == PASS_POST_DEPTH) {
@@ -66,11 +67,11 @@ void main()
else {
depth = -get_view_z_from_depth(depth);
}
- color = vec3(depth);
+ color.rgb = vec3(depth);
}
else if (postProcessType == PASS_POST_AO) {
float ao_accum = texelFetch(inputBuffer, texel, 0).r;
- color = vec3(min(1.0, ao_accum / currentSample));
+ color.rgb = vec3(min(1.0, ao_accum / currentSample));
}
else if (postProcessType == PASS_POST_NORMAL) {
float depth = texelFetch(depthBuffer, texel, 0).r;
@@ -80,35 +81,39 @@ void main()
if (depth != 1.0 && any(notEqual(encoded_normal, vec2(0.0)))) {
vec3 decoded_normal = normal_decode(texelFetch(inputBuffer, texel, 0).rg, vec3(0.0));
vec3 world_normal = mat3(ViewMatrixInverse) * decoded_normal;
- color = world_normal;
+ color.rgb = world_normal;
}
else {
- color = vec3(0.0);
+ color.rgb = vec3(0.0);
}
}
else if (postProcessType == PASS_POST_ACCUMULATED_VALUE) {
float accumulated_value = texelFetch(inputBuffer, texel, 0).r;
- color = vec3(accumulated_value / currentSample);
+ color.rgb = vec3(accumulated_value / currentSample);
}
else if (postProcessType == PASS_POST_ACCUMULATED_COLOR) {
vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb;
+ color.rgb = (accumulated_color / currentSample);
+ }
+ else if (postProcessType == PASS_POST_ACCUMULATED_COLOR_ALPHA) {
+ vec4 accumulated_color = texelFetch(inputBuffer, texel, 0);
color = (accumulated_color / currentSample);
}
else if (postProcessType == PASS_POST_ACCUMULATED_LIGHT) {
vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb;
vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb;
- color = safe_divide_even_color(accumulated_light, accumulated_color);
+ color.rgb = safe_divide_even_color(accumulated_light, accumulated_color);
}
else if (postProcessType == PASS_POST_TWO_LIGHT_BUFFERS) {
vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb +
texelFetch(inputSecondLightBuffer, texel, 0).rgb;
vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb;
- color = safe_divide_even_color(accumulated_light, accumulated_color);
+ color.rgb = safe_divide_even_color(accumulated_light, accumulated_color);
}
else {
/* Output error color: Unknown how to post process this pass. */
- color = vec3(1.0, 0.0, 1.0);
+ color.rgb = vec3(1.0, 0.0, 1.0);
}
- fragColor = vec4(color, 1.0);
+ fragColor = color;
}
diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h
index d3a06f0fc2c..e1d03e6f3be 100644
--- a/source/blender/editors/render/render_intern.h
+++ b/source/blender/editors/render/render_intern.h
@@ -46,6 +46,8 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot);
void SCENE_OT_view_layer_add(struct wmOperatorType *ot);
void SCENE_OT_view_layer_remove(struct wmOperatorType *ot);
+void SCENE_OT_view_layer_add_aov(struct wmOperatorType *ot);
+void SCENE_OT_view_layer_remove_aov(struct wmOperatorType *ot);
void SCENE_OT_light_cache_bake(struct wmOperatorType *ot);
void SCENE_OT_light_cache_free(struct wmOperatorType *ot);
diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c
index 706249a3f8b..e0aa02b354d 100644
--- a/source/blender/editors/render/render_ops.c
+++ b/source/blender/editors/render/render_ops.c
@@ -53,6 +53,8 @@ void ED_operatortypes_render(void)
WM_operatortype_append(SCENE_OT_view_layer_add);
WM_operatortype_append(SCENE_OT_view_layer_remove);
+ WM_operatortype_append(SCENE_OT_view_layer_add_aov);
+ WM_operatortype_append(SCENE_OT_view_layer_remove_aov);
WM_operatortype_append(SCENE_OT_render_view_add);
WM_operatortype_append(SCENE_OT_render_view_remove);
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 60e5c2081fd..b69337b1621 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -90,6 +90,7 @@
#include "UI_interface.h"
+#include "RE_engine.h"
#include "RE_pipeline.h"
#include "engines/eevee/eevee_lightcache.h"
@@ -1014,6 +1015,93 @@ void SCENE_OT_view_layer_remove(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name View Layer Add AOV Operator
+ * \{ */
+
+static int view_layer_add_aov_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ BKE_view_layer_add_aov(view_layer);
+
+ RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
+ if (engine_type->update_render_passes) {
+ RenderEngine *engine = RE_engine_create(engine_type);
+ if (engine) {
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ }
+ RE_engine_free(engine);
+ engine = NULL;
+ }
+
+ DEG_id_tag_update(&scene->id, 0);
+ DEG_relations_tag_update(CTX_data_main(C));
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCENE_OT_view_layer_add_aov(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add AOV";
+ ot->idname = "SCENE_OT_view_layer_add_aov";
+ ot->description = "Add a Shader AOV";
+
+ /* api callbacks */
+ ot->exec = view_layer_add_aov_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Layer Remove AOV Operator
+ * \{ */
+
+static int view_layer_remove_aov_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ BKE_view_layer_remove_aov(view_layer, view_layer->active_aov);
+
+ RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
+ if (engine_type->update_render_passes) {
+ RenderEngine *engine = RE_engine_create(engine_type);
+ if (engine) {
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ }
+ RE_engine_free(engine);
+ engine = NULL;
+ }
+
+ DEG_id_tag_update(&scene->id, 0);
+ DEG_relations_tag_update(CTX_data_main(C));
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCENE_OT_view_layer_remove_aov(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove AOV";
+ ot->idname = "SCENE_OT_view_layer_remove_aov";
+ ot->description = "Remove Active AOV";
+
+ /* api callbacks */
+ ot->exec = view_layer_remove_aov_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Light Cache Bake Operator
* \{ */
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 7e2fe753b7b..69a79e2f2ce 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -313,6 +313,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_noise.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_normal.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_normal_map.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_object_info.glsl SRC)
+data_to_c_simple(shaders/material/gpu_shader_material_output_aov.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_output_material.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_output_world.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_particle_info.glsl SRC)
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index 67cd1a61aed..312da491a36 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -173,6 +173,7 @@ GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat,
const int index);
void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash);
void GPU_material_sss_profile_create(GPUMaterial *material,
float radii[3],
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index 3ebe2edc89e..84da95f6fee 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -411,7 +411,7 @@ static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph)
BLI_dynstr_append(ds, "\n");
}
-static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *finaloutput)
+static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph)
{
LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
BLI_dynstr_appendf(ds, " %s(", node->name);
@@ -509,8 +509,11 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f
BLI_dynstr_append(ds, ");\n");
}
+}
- BLI_dynstr_appendf(ds, "\n return tmp%d;\n", finaloutput->id);
+static void codegen_final_output(DynStr *ds, GPUOutput *finaloutput)
+{
+ BLI_dynstr_appendf(ds, "return tmp%d;\n", finaloutput->id);
}
static char *code_generate_fragment(GPUMaterial *material,
@@ -593,7 +596,35 @@ static char *code_generate_fragment(GPUMaterial *material,
}
codegen_declare_tmps(ds, graph);
- codegen_call_functions(ds, graph, graph->outlink->output);
+ codegen_call_functions(ds, graph);
+
+ BLI_dynstr_append(ds, " #ifndef VOLUMETRICS\n");
+ BLI_dynstr_append(ds, " if (renderPassAOV) {\n");
+ BLI_dynstr_append(ds, " switch (render_pass_aov_hash()) {\n");
+ GSet *aovhashes_added = BLI_gset_int_new(__func__);
+ LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) {
+ void *aov_key = POINTER_FROM_INT(aovlink->hash);
+ if (BLI_gset_haskey(aovhashes_added, aov_key)) {
+ continue;
+ }
+ BLI_dynstr_appendf(ds, " case %d: {\n ", aovlink->hash);
+ codegen_final_output(ds, aovlink->outlink->output);
+ BLI_dynstr_append(ds, " }\n");
+ BLI_gset_add(aovhashes_added, aov_key);
+ }
+ BLI_gset_free(aovhashes_added, NULL);
+ BLI_dynstr_append(ds, " default: {\n");
+ BLI_dynstr_append(ds, " Closure no_aov = CLOSURE_DEFAULT;\n");
+ BLI_dynstr_append(ds, " no_aov.holdout = 1.0;\n");
+ BLI_dynstr_append(ds, " return no_aov;\n");
+ BLI_dynstr_append(ds, " }\n");
+ BLI_dynstr_append(ds, " }\n");
+ BLI_dynstr_append(ds, " } else {\n");
+ BLI_dynstr_append(ds, " #else /* VOLUMETRICS */\n");
+ BLI_dynstr_append(ds, " {\n");
+ BLI_dynstr_append(ds, " #endif /* VOLUMETRICS */\n ");
+ codegen_final_output(ds, graph->outlink->output);
+ BLI_dynstr_append(ds, " }\n");
BLI_dynstr_append(ds, "}\n");
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index a0fe77598f2..3f22424c7c9 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -597,6 +597,14 @@ void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link)
}
}
+void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash)
+{
+ GPUNodeGraphOutputLink *aov_link = MEM_callocN(sizeof(GPUNodeGraphOutputLink), __func__);
+ aov_link->outlink = link;
+ aov_link->hash = hash;
+ BLI_addtail(&material->graph.outlink_aovs, aov_link);
+}
+
GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material)
{
return &material->graph;
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index e0165e1fa83..496988c4ba9 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -84,6 +84,7 @@ extern char datatoc_gpu_shader_material_noise_glsl[];
extern char datatoc_gpu_shader_material_normal_glsl[];
extern char datatoc_gpu_shader_material_normal_map_glsl[];
extern char datatoc_gpu_shader_material_object_info_glsl[];
+extern char datatoc_gpu_shader_material_output_aov_glsl[];
extern char datatoc_gpu_shader_material_output_material_glsl[];
extern char datatoc_gpu_shader_material_output_world_glsl[];
extern char datatoc_gpu_shader_material_particle_info_glsl[];
@@ -354,6 +355,11 @@ static GPUMaterialLibrary gpu_shader_material_object_info_library = {
.dependencies = {NULL},
};
+static GPUMaterialLibrary gpu_shader_material_output_aov_library = {
+ .code = datatoc_gpu_shader_material_output_aov_glsl,
+ .dependencies = {NULL},
+};
+
static GPUMaterialLibrary gpu_shader_material_output_material_library = {
.code = datatoc_gpu_shader_material_output_material_glsl,
.dependencies = {NULL},
@@ -619,6 +625,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
&gpu_shader_material_normal_library,
&gpu_shader_material_normal_map_library,
&gpu_shader_material_object_info_library,
+ &gpu_shader_material_output_aov_library,
&gpu_shader_material_output_material_library,
&gpu_shader_material_output_world_library,
&gpu_shader_material_particle_info_library,
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index 2a2a51e32b3..08da49c3475 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -805,6 +805,7 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph)
/* Free both node graph and requested attributes and textures. */
void gpu_node_graph_free(GPUNodeGraph *graph)
{
+ BLI_freelistN(&graph->outlink_aovs);
gpu_node_graph_free_nodes(graph);
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) {
@@ -847,6 +848,9 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph)
}
gpu_nodes_tag(graph->outlink);
+ LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) {
+ gpu_nodes_tag(aovlink->outlink);
+ }
for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) {
next = node->next;
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index a0e6298cd92..0ef95d94c0d 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -141,12 +141,20 @@ typedef struct GPUInput {
};
} GPUInput;
+typedef struct GPUNodeGraphOutputLink {
+ struct GPUNodeGraphOutputLink *next, *prev;
+ int hash;
+ GPUNodeLink *outlink;
+} GPUNodeGraphOutputLink;
+
typedef struct GPUNodeGraph {
/* Nodes */
ListBase nodes;
- /* Output. */
+ /* Main Output. */
GPUNodeLink *outlink;
+ /* List of GPUNodeGraphOutputLink */
+ ListBase outlink_aovs;
/* Requested attributes and textures. */
ListBase attributes;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl
new file mode 100644
index 00000000000..648994739bf
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl
@@ -0,0 +1,13 @@
+
+void node_output_aov(vec4 color, float value, out Closure result)
+{
+ result = CLOSURE_DEFAULT;
+#ifndef VOLUMETRICS
+ if (render_pass_aov_is_color()) {
+ result.radiance = color.rgb;
+ }
+ else {
+ result.radiance = vec3(value);
+ }
+#endif
+}
diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index 85065ba35d4..f2e3e0a3c9c 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -47,8 +47,20 @@ typedef enum eViewLayerEEVEEPassType {
EEVEE_RENDER_PASS_SHADOW = (1 << 12),
EEVEE_RENDER_PASS_AO = (1 << 13),
EEVEE_RENDER_PASS_BLOOM = (1 << 14),
+ EEVEE_RENDER_PASS_AOV = (1 << 15),
} eViewLayerEEVEEPassType;
-#define EEVEE_RENDER_PASS_MAX_BIT 15
+#define EEVEE_RENDER_PASS_MAX_BIT 16
+
+/* ViewLayerAOV.type */
+typedef enum eViewLayerAOVType {
+ AOV_TYPE_VALUE = 0,
+ AOV_TYPE_COLOR = 1,
+} eViewLayerAOVType;
+
+/* ViewLayerAOV.type */
+typedef enum eViewLayerAOVFlag {
+ AOV_CONFLICT = (1 << 0),
+} eViewLayerAOVFlag;
typedef struct Base {
struct Base *next, *prev;
@@ -104,6 +116,17 @@ typedef struct ViewLayerEEVEE {
int _pad[1];
} ViewLayerEEVEE;
+/* AOV Renderpass definition. */
+typedef struct ViewLayerAOV {
+ struct ViewLayerAOV *next, *prev;
+
+ /* Name of the AOV */
+ char name[64];
+ int flag;
+ /* Type of AOV (color/value)
+ * matches `eViewLayerAOVType` */
+ int type;
+} ViewLayerAOV;
typedef struct ViewLayer {
struct ViewLayer *next, *prev;
/** MAX_NAME. */
@@ -136,6 +159,10 @@ typedef struct ViewLayer {
struct FreestyleConfig freestyle_config;
struct ViewLayerEEVEE eevee;
+ /* List containing the `ViewLayerAOV`s */
+ ListBase aovs;
+ ViewLayerAOV *active_aov;
+
/* Runtime data */
/** ViewLayerEngineData. */
ListBase drawdata;
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 9a233878840..b8e2256c3c6 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -192,6 +192,7 @@ typedef struct View3DShading {
/* Render pass displayed in the viewport. Is an `eScenePassType` where one bit is set */
int render_pass;
+ char aov_name[64];
struct IDProperty *prop;
void *_pad2;
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index f51202348b5..a581edcb04b 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -60,6 +60,7 @@ extern StructRNA RNA_AnimData;
extern StructRNA RNA_AnimViz;
extern StructRNA RNA_AnimVizMotionPaths;
extern StructRNA RNA_AnyType;
+extern StructRNA RNA_AOV;
extern StructRNA RNA_Area;
extern StructRNA RNA_AreaLight;
extern StructRNA RNA_Armature;
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 31e920a6799..1c6f83efd65 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -345,6 +345,10 @@ void rna_ViewLayer_material_override_update(struct Main *bmain,
void rna_ViewLayer_pass_update(struct Main *bmain,
struct Scene *activescene,
struct PointerRNA *ptr);
+void rna_ViewLayer_active_aov_index_range(
+ PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax);
+int rna_ViewLayer_active_aov_index_get(PointerRNA *ptr);
+void rna_ViewLayer_active_aov_index_set(PointerRNA *ptr, int value);
/* named internal so as not to conflict with obj.update() rna func */
void rna_Object_internal_update_data(struct Main *bmain,
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index c7a53757296..0dcbd8a070b 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -152,7 +152,18 @@ static void rna_ViewLayer_update_render_passes(ID *id)
if (scene->nodetree) {
ntreeCompositUpdateRLayers(scene->nodetree);
}
-}
+
+ RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
+ if (engine_type->update_render_passes) {
+ RenderEngine *engine = RE_engine_create(engine_type);
+ if (engine) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ }
+ }
+ RE_engine_free(engine);
+ engine = NULL;
+ }}
static PointerRNA rna_ViewLayer_objects_get(CollectionPropertyIterator *iter)
{
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 235042122e6..0f13ba29d3b 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -529,6 +529,12 @@ const EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_view_layer_aov_type_items[] = {
+ {AOV_TYPE_COLOR, "COLOR", 0, "Color", ""},
+ {AOV_TYPE_VALUE, "VALUE", 0, "Value", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_gpencil_interpolation_mode_items[] = {
/* interpolation */
@@ -1779,6 +1785,27 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr)
ntreeCompositUpdateRLayers(scene->nodetree);
}
+ ViewLayer *view_layer = NULL;
+ if (ptr->type == &RNA_ViewLayer) {
+ view_layer = (ViewLayer *)ptr->data;
+ }
+ else if (ptr->type == &RNA_AOV) {
+ ViewLayerAOV *aov = (ViewLayerAOV *)ptr->data;
+ view_layer = BKE_view_layer_find_with_aov(scene, aov);
+ }
+
+ if (view_layer) {
+ RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
+ if (engine_type->update_render_passes) {
+ RenderEngine *engine = RE_engine_create(engine_type);
+ if (engine) {
+ BKE_view_layer_verify_aov(engine, scene, view_layer);
+ }
+ RE_engine_free(engine);
+ engine = NULL;
+ }
+ }
+
rna_Scene_glsl_update(bmain, activescene, ptr);
}
@@ -2397,6 +2424,28 @@ static void rna_ViewLayer_remove(
}
}
+void rna_ViewLayer_active_aov_index_range(
+ PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ ViewLayer *view_layer = (ViewLayer *)ptr->data;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&view_layer->aovs) - 1);
+}
+
+int rna_ViewLayer_active_aov_index_get(PointerRNA *ptr)
+{
+ ViewLayer *view_layer = (ViewLayer *)ptr->data;
+ return BLI_findindex(&view_layer->aovs, view_layer->active_aov);
+}
+
+void rna_ViewLayer_active_aov_index_set(PointerRNA *ptr, int value)
+{
+ ViewLayer *view_layer = (ViewLayer *)ptr->data;
+ ViewLayerAOV *aov = BLI_findlink(&view_layer->aovs, value);
+ view_layer->active_aov = aov;
+}
+
/* Fake value, used internally (not saved to DNA). */
# define V3D_ORIENT_DEFAULT -1
@@ -3963,6 +4012,33 @@ static void rna_def_view_layer_eevee(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
}
+static void rna_def_view_layer_aov(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+ srna = RNA_def_struct(brna, "AOV", NULL);
+ RNA_def_struct_sdna(srna, "ViewLayerAOV");
+ RNA_def_struct_ui_text(srna, "Shader AOV", "");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the AOV");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", AOV_CONFLICT);
+ RNA_def_property_ui_text(prop, "Valid", "Is the name of the AOV conflicting");
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, rna_enum_view_layer_aov_type_items);
+ RNA_def_property_enum_default(prop, AOV_TYPE_COLOR);
+ RNA_def_property_ui_text(prop, "Type", "Data type of the AOV");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
+}
+
void rna_def_view_layer_common(StructRNA *srna, const bool scene)
{
PropertyRNA *prop;
@@ -4013,6 +4089,24 @@ void rna_def_view_layer_common(StructRNA *srna, const bool scene)
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "ViewLayerEEVEE");
RNA_def_property_ui_text(prop, "EEVEE Settings", "View layer settings for EEVEE");
+
+ prop = RNA_def_property(srna, "aovs", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "aovs", NULL);
+ RNA_def_property_struct_type(prop, "AOV");
+ RNA_def_property_ui_text(prop, "Shader AOV", "");
+
+ prop = RNA_def_property(srna, "active_aov", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "AOV");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Shader AOV", "Active AOV");
+
+ prop = RNA_def_property(srna, "active_aov_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_ViewLayer_active_aov_index_get",
+ "rna_ViewLayer_active_aov_index_set",
+ "rna_ViewLayer_active_aov_index_range");
+ RNA_def_property_ui_text(prop, "Active AOV Index", "Index of active aov");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
/* layer options */
@@ -7848,6 +7942,7 @@ void RNA_def_scene(BlenderRNA *brna)
rna_def_display_safe_areas(brna);
rna_def_scene_display(brna);
rna_def_scene_eevee(brna);
+ rna_def_view_layer_aov(brna);
rna_def_view_layer_eevee(brna);
rna_def_scene_gpencil(brna);
RNA_define_animate_sdna(true);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 8ff4336ba83..eabb71c79d3 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -426,6 +426,9 @@ static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] =
{EEVEE_RENDER_PASS_NORMAL, "NORMAL", 0, "Normal", ""},
{EEVEE_RENDER_PASS_MIST, "MIST", 0, "Mist", ""},
+ {0, "", ICON_NONE, "Shader AOV", ""},
+ {EEVEE_RENDER_PASS_AOV, "AOV", 0, "AOV", ""},
+
{0, NULL, 0, NULL, NULL},
};
@@ -1065,6 +1068,19 @@ static Scene *rna_3DViewShading_scene(PointerRNA *ptr)
}
}
+static ViewLayer *rna_3DViewShading_view_layer(PointerRNA *ptr)
+{
+ /* Get scene, depends if using 3D view or OpenGL render settings. */
+ ID *id = ptr->owner_id;
+ if (GS(id->name) == ID_SCE) {
+ return NULL;
+ }
+ else {
+ bScreen *screen = (bScreen *)ptr->owner_id;
+ return WM_windows_view_layer_get_from_screen(G_MAIN->wm.first, screen);
+ }
+}
+
static int rna_3DViewShading_type_get(PointerRNA *ptr)
{
/* Available shading types depend on render engine. */
@@ -1292,15 +1308,33 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C,
bool *r_free)
{
Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
const bool bloom_enabled = scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED;
+ const bool aov_available = BKE_view_layer_has_valid_aov(view_layer);
int totitem = 0;
EnumPropertyItem *result = NULL;
+ EnumPropertyItem aov_template;
for (int i = 0; rna_enum_view3dshading_render_pass_type_items[i].identifier != NULL; i++) {
const EnumPropertyItem *item = &rna_enum_view3dshading_render_pass_type_items[i];
- if (!((!bloom_enabled &&
- (item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))))) {
+ if (item->value == EEVEE_RENDER_PASS_AOV) {
+ aov_template.value = item->value;
+ aov_template.icon = 0;
+ aov_template.description = item->description;
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ aov_template.name = aov->name;
+ aov_template.identifier = aov->name;
+ RNA_enum_item_add(&result, &totitem, &aov_template);
+ aov_template.value++;
+ }
+ }
+ else if (!((!bloom_enabled &&
+ (item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))) ||
+ (!aov_available && STREQ(item->name, "Shader AOV")))) {
RNA_enum_item_add(&result, &totitem, item);
}
}
@@ -1314,14 +1348,58 @@ static int rna_3DViewShading_render_pass_get(PointerRNA *ptr)
View3DShading *shading = (View3DShading *)ptr->data;
eViewLayerEEVEEPassType result = shading->render_pass;
Scene *scene = rna_3DViewShading_scene(ptr);
+ ViewLayer *view_layer = rna_3DViewShading_view_layer(ptr);
if (result == EEVEE_RENDER_PASS_BLOOM && ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
- result = EEVEE_RENDER_PASS_COMBINED;
+ return EEVEE_RENDER_PASS_COMBINED;
+ }
+ else if (result == EEVEE_RENDER_PASS_AOV) {
+ if (!view_layer) {
+ return EEVEE_RENDER_PASS_COMBINED;
+ }
+ const int aov_index = BLI_findstringindex(
+ &view_layer->aovs, shading->aov_name, offsetof(ViewLayerAOV, name));
+ if (aov_index == -1) {
+ return EEVEE_RENDER_PASS_COMBINED;
+ }
+ return result + aov_index;
}
return result;
}
+static void rna_3DViewShading_render_pass_set(PointerRNA *ptr, int value)
+{
+ View3DShading *shading = (View3DShading *)ptr->data;
+ Scene *scene = rna_3DViewShading_scene(ptr);
+ ViewLayer *view_layer = rna_3DViewShading_view_layer(ptr);
+ shading->aov_name[0] = 0;
+
+ if ((value & EEVEE_RENDER_PASS_AOV) != 0) {
+ if (!view_layer) {
+ shading->render_pass = EEVEE_RENDER_PASS_COMBINED;
+ return;
+ }
+ const int aov_index = value & ~EEVEE_RENDER_PASS_AOV;
+ ViewLayerAOV *aov = BLI_findlink(&view_layer->aovs, aov_index);
+ if (!aov) {
+ /* AOV not found, cannot select AOV. */
+ shading->render_pass = EEVEE_RENDER_PASS_COMBINED;
+ return;
+ }
+
+ shading->render_pass = EEVEE_RENDER_PASS_AOV;
+ BLI_strncpy(shading->aov_name, aov->name, sizeof(aov->name));
+ }
+ else if (value == EEVEE_RENDER_PASS_BLOOM &&
+ ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
+ shading->render_pass = EEVEE_RENDER_PASS_COMBINED;
+ }
+ else {
+ shading->render_pass = value;
+ }
+}
+
static void rna_SpaceView3D_use_local_collections_update(bContext *C, PointerRNA *ptr)
{
Main *bmain = CTX_data_main(C);
@@ -3488,9 +3566,17 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "render_pass");
RNA_def_property_enum_items(prop, rna_enum_view3dshading_render_pass_type_items);
RNA_def_property_ui_text(prop, "Render Pass", "Render Pass to show in the viewport");
- RNA_def_property_enum_funcs(
- prop, "rna_3DViewShading_render_pass_get", NULL, "rna_3DViewShading_render_pass_itemf");
+ RNA_def_property_enum_funcs(prop,
+ "rna_3DViewShading_render_pass_get",
+ "rna_3DViewShading_render_pass_set",
+ "rna_3DViewShading_render_pass_itemf");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL);
+
+ prop = RNA_def_property(srna, "aov_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "aov_name");
+ RNA_def_property_ui_text(prop, "Shader AOV Name", "Name of the active Shader AOV");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
static void rna_def_space_view3d_overlay(BlenderRNA *brna)
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index efd0e48f41a..a385cb7039f 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -903,6 +903,16 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
/* Duplicate bump height branches for manual derivatives.
*/
nodeChainIterBackwards(localtree, output, ntree_shader_bump_branches, localtree, 0);
+ LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
+ if (node->type == SH_NODE_OUTPUT_AOV) {
+ nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0);
+ nTreeTags tags = {
+ .ssr_id = 1.0,
+ .sss_id = 1.0,
+ };
+ ntree_shader_tag_nodes(localtree, node, &tags);
+ }
+ }
/* TODO(fclem): consider moving this to the gpu shader tree evaluation. */
nTreeTags tags = {
@@ -913,6 +923,11 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
exec = ntreeShaderBeginExecTree(localtree);
ntreeExecGPUNodes(exec, mat, output);
+ LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
+ if (node->type == SH_NODE_OUTPUT_AOV) {
+ ntreeExecGPUNodes(exec, mat, node);
+ }
+ }
ntreeShaderEndExecTree(exec);
/* EEVEE: Find which material domain was used (volume, surface ...). */
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
index 8e73a547bf7..403b3e6d9d6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c
+++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
@@ -19,6 +19,8 @@
#include "../node_shader_util.h"
+#include "BLI_hash.h"
+
/* **************** OUTPUT ******************** */
static bNodeSocketTemplate sh_node_output_aov_in[] = {
@@ -33,6 +35,22 @@ static void node_shader_init_output_aov(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = aov;
}
+static int node_shader_gpu_output_aov(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ GPUNodeLink *outlink;
+ NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage;
+ /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */
+ unsigned int hash = BLI_hash_string(aov->name) & ~1;
+ GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink);
+ GPU_material_add_output_link_aov(mat, outlink, hash);
+
+ return true;
+}
+
/* node type definition */
void register_node_type_sh_output_aov(void)
{
@@ -43,6 +61,7 @@ void register_node_type_sh_output_aov(void)
node_type_init(&ntype, node_shader_init_output_aov);
node_type_storage(
&ntype, "NodeShaderOutputAOV", node_free_standard_storage, node_copy_standard_storage);
+ node_type_gpu(&ntype, node_shader_gpu_output_aov);
/* Do not allow muting output node. */
node_type_internal_links(&ntype, NULL);
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 47c5487a458..430130d4727 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -130,6 +130,9 @@ void WM_windows_scene_data_sync(const ListBase *win_lb, struct Scene *scene) ATT
struct Scene *WM_windows_scene_get_from_screen(const struct wmWindowManager *wm,
const struct bScreen *screen)
ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+struct ViewLayer *WM_windows_view_layer_get_from_screen(const struct wmWindowManager *wm,
+ const struct bScreen *screen)
+ ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
struct WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm,
const struct bScreen *screen)
ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 926e61f4a0e..74f352c0b62 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -656,6 +656,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
* pieces of Blender using sound may exit cleanly, see also T50676. */
BKE_sound_exit();
+ BKE_appdir_exit();
CLG_exit();
BKE_blender_atexit();
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 589b8e2f156..14798653a31 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -2261,6 +2261,17 @@ Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen
return NULL;
}
+ViewLayer *WM_windows_view_layer_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
+{
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ if (WM_window_get_active_screen(win) == screen) {
+ return WM_window_get_active_view_layer(win);
+ }
+ }
+
+ return NULL;
+}
+
WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
{
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {