diff options
Diffstat (limited to 'source')
38 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) { diff --git a/source/tools b/source/tools -Subproject 7011d02c292ac1c91a5c9cc1a075ea2727982ce +Subproject d7d7e9d41f7499aa4639f96c843156ff834385b |