diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 2f88bb6beb9..681e2e82b37 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -18,6 +18,8 @@ from bl_ui.properties_view_layer import ( ViewLayerLightgroupsPanelHelper, ) +from bl_ui.properties_object import has_geometry_visibility + class CyclesPresetPanel(PresetPanel, Panel): COMPAT_ENGINES = {'CYCLES'} @@ -1255,46 +1257,14 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): col.prop(cob, "use_deform_motion", text="Deformation") -def has_geometry_visibility(ob): - return ob and ( - (ob.type in { - 'MESH', - 'CURVE', - 'SURFACE', - 'FONT', - 'META', - 'LIGHT', - 'VOLUME', - 'POINTCLOUD', - 'CURVES', - }) or (ob.instance_type == 'COLLECTION' and ob.instance_collection)) - - -class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel): - bl_label = "Shading" - bl_context = "object" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - if not CyclesButtonsPanel.poll(context): - return False - - ob = context.object - return ob and has_geometry_visibility(ob) - - def draw(self, context): - pass - - class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel): bl_label = "Shadow Terminator" - bl_parent_id = "CYCLES_OBJECT_PT_shading" + bl_parent_id = "OBJECT_PT_shading" bl_context = "object" @classmethod def poll(cls, context): - return context.object.type != 'LIGHT' + return CyclesButtonsPanel.poll(context) and context.object.type != 'LIGHT' def draw(self, context): layout = self.layout @@ -1310,12 +1280,12 @@ class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel): class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel): bl_label = "Fast GI Approximation" - bl_parent_id = "CYCLES_OBJECT_PT_shading" + bl_parent_id = "OBJECT_PT_shading" bl_context = "object" @classmethod def poll(cls, context): - return context.object.type != 'LIGHT' + return CyclesButtonsPanel.poll(context) and context.object.type != 'LIGHT' def draw(self, context): layout = self.layout @@ -1334,7 +1304,7 @@ class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel): class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel): bl_label = "Caustics" - bl_parent_id = "CYCLES_OBJECT_PT_shading" + bl_parent_id = "OBJECT_PT_shading" bl_context = "object" @classmethod @@ -1356,7 +1326,7 @@ class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel): class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel): bl_label = "Light Group" - bl_parent_id = "CYCLES_OBJECT_PT_shading" + bl_parent_id = "OBJECT_PT_shading" bl_context = "object" bl_options = {'DEFAULT_CLOSED'} @@ -1379,96 +1349,6 @@ class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel): sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = ob.lightgroup -class CYCLES_OBJECT_MT_light_linking_context_menu(Menu): - bl_label = "Light Linking Specials" - - def draw(self, _context): - layout = self.layout - - layout.operator("object.light_linking_receivers_select") - - -class CYCLES_OBJECT_MT_shadow_linking_context_menu(Menu): - bl_label = "Shadow Linking Specials" - - def draw(self, _context): - layout = self.layout - - layout.operator("object.light_linking_blockers_select") - - -class CYCLES_OBJECT_PT_light_linking(CyclesButtonsPanel, Panel): - bl_label = "Light Linking" - bl_parent_id = "CYCLES_OBJECT_PT_shading" - bl_context = "object" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - object = context.object - light_linking = object.light_linking - - col = layout.column() - - col.template_ID( - light_linking, - "receiver_collection", - new="object.light_linking_receiver_collection_new") - - if not light_linking.receiver_collection: - return - - row = layout.row() - col = row.column() - col.template_light_linking_collection(row, light_linking, "receiver_collection") - - col = row.column() - sub = col.column(align=True) - prop = sub.operator("object.light_linking_receivers_link", icon='ADD', text="") - prop.link_state = 'INCLUDE' - sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="") - sub = col.column() - sub.menu("CYCLES_OBJECT_MT_light_linking_context_menu", icon='DOWNARROW_HLT', text="") - - -class CYCLES_OBJECT_PT_shadow_linking(CyclesButtonsPanel, Panel): - bl_label = "Shadow Linking" - bl_parent_id = "CYCLES_OBJECT_PT_shading" - bl_context = "object" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - object = context.object - light_linking = object.light_linking - - col = layout.column() - - col.template_ID( - light_linking, - "blocker_collection", - new="object.light_linking_blocker_collection_new") - - if not light_linking.blocker_collection: - return - - row = layout.row() - col = row.column() - col.template_light_linking_collection(row, light_linking, "blocker_collection") - - col = row.column() - sub = col.column(align=True) - prop = sub.operator("object.light_linking_blockers_link", icon='ADD', text="") - prop.link_state = 'INCLUDE' - sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="") - sub = col.column() - sub.menu("CYCLES_OBJECT_MT_shadow_linking_context_menu", icon='DOWNARROW_HLT', text="") - - class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel): bl_label = "Visibility" bl_context = "object" @@ -2517,14 +2397,6 @@ def draw_pause(self, context): layout.prop(cscene, "preview_pause", icon='PLAY' if cscene.preview_pause else 'PAUSE', text="") -def draw_make_links(self, context): - if context.engine == "CYCLES": - layout = self.layout - layout.separator() - layout.operator_menu_enum("object.light_linking_receivers_link", "link_state") - layout.operator_menu_enum("object.light_linking_blockers_link", "link_state") - - def get_panels(): exclude_panels = { 'DATA_PT_camera_dof', @@ -2608,15 +2480,10 @@ classes = ( CYCLES_CAMERA_PT_dof_aperture, CYCLES_PT_context_material, CYCLES_OBJECT_PT_motion_blur, - CYCLES_OBJECT_PT_shading, CYCLES_OBJECT_PT_shading_shadow_terminator, CYCLES_OBJECT_PT_shading_gi_approximation, CYCLES_OBJECT_PT_shading_caustics, CYCLES_OBJECT_PT_lightgroup, - CYCLES_OBJECT_MT_light_linking_context_menu, - CYCLES_OBJECT_PT_light_linking, - CYCLES_OBJECT_MT_shadow_linking_context_menu, - CYCLES_OBJECT_PT_shadow_linking, CYCLES_OBJECT_PT_visibility, CYCLES_OBJECT_PT_visibility_ray_visibility, CYCLES_OBJECT_PT_visibility_culling, @@ -2663,7 +2530,6 @@ def register(): bpy.types.RENDER_PT_context.append(draw_device) bpy.types.VIEW3D_HT_header.append(draw_pause) - bpy.types.VIEW3D_MT_make_links.append(draw_make_links) for panel in get_panels(): panel.COMPAT_ENGINES.add('CYCLES') @@ -2677,7 +2543,6 @@ def unregister(): bpy.types.RENDER_PT_context.remove(draw_device) bpy.types.VIEW3D_HT_header.remove(draw_pause) - bpy.types.VIEW3D_MT_make_links.remove(draw_make_links) for panel in get_panels(): if 'CYCLES' in panel.COMPAT_ENGINES: diff --git a/scripts/startup/bl_ui/properties_object.py b/scripts/startup/bl_ui/properties_object.py index e35fae2c8c1..2822da58427 100644 --- a/scripts/startup/bl_ui/properties_object.py +++ b/scripts/startup/bl_ui/properties_object.py @@ -421,6 +421,128 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel): col.prop(ob, "is_holdout") +def has_geometry_visibility(ob): + return ob and ( + (ob.type in { + 'MESH', + 'CURVE', + 'SURFACE', + 'FONT', + 'META', + 'LIGHT', + 'VOLUME', + 'POINTCLOUD', + 'CURVES', + }) or (ob.instance_type == 'COLLECTION' and ob.instance_collection)) + + +class OBJECT_PT_shading(ObjectButtonsPanel, Panel): + bl_label = "Shading" + bl_context = "object" + bl_options = {'DEFAULT_CLOSED'} + + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'} + + @classmethod + def poll(cls, context): + if context.engine not in cls.COMPAT_ENGINES: + return False + + return has_geometry_visibility(context.object) + + def draw(self, context): + pass + + +class OBJECT_MT_light_linking_context_menu(Menu): + bl_label = "Light Linking Specials" + + def draw(self, _context): + layout = self.layout + + layout.operator("object.light_linking_receivers_select") + + +class OBJECT_PT_light_linking(ObjectButtonsPanel, Panel): + bl_label = "Light Linking" + bl_parent_id = "OBJECT_PT_shading" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + object = context.object + light_linking = object.light_linking + + col = layout.column() + + col.template_ID( + light_linking, + "receiver_collection", + new="object.light_linking_receiver_collection_new") + + if not light_linking.receiver_collection: + return + + row = layout.row() + col = row.column() + col.template_light_linking_collection(row, light_linking, "receiver_collection") + + col = row.column() + sub = col.column(align=True) + prop = sub.operator("object.light_linking_receivers_link", icon='ADD', text="") + prop.link_state = 'INCLUDE' + sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="") + sub = col.column() + sub.menu("OBJECT_MT_light_linking_context_menu", icon='DOWNARROW_HLT', text="") + + +class OBJECT_MT_shadow_linking_context_menu(Menu): + bl_label = "Shadow Linking Specials" + + def draw(self, _context): + layout = self.layout + + layout.operator("object.light_linking_blockers_select") + + +class OBJECT_PT_shadow_linking(ObjectButtonsPanel, Panel): + bl_label = "Shadow Linking" + bl_parent_id = "OBJECT_PT_shading" + bl_context = "object" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + object = context.object + light_linking = object.light_linking + + col = layout.column() + + col.template_ID( + light_linking, + "blocker_collection", + new="object.light_linking_blocker_collection_new") + + if not light_linking.blocker_collection: + return + + row = layout.row() + col = row.column() + col.template_light_linking_collection(row, light_linking, "blocker_collection") + + col = row.column() + sub = col.column(align=True) + prop = sub.operator("object.light_linking_blockers_link", icon='ADD', text="") + prop.link_state = 'INCLUDE' + sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="") + sub = col.column() + sub.menu("OBJECT_MT_shadow_linking_context_menu", icon='DOWNARROW_HLT', text="") + + class OBJECT_PT_animation(ObjectButtonsPanel, PropertiesAnimationMixin, PropertyPanel, Panel): _animated_id_context_property = "object" @@ -443,6 +565,11 @@ classes = ( OBJECT_PT_motion_paths, OBJECT_PT_motion_paths_display, OBJECT_PT_display, + OBJECT_PT_shading, + OBJECT_MT_light_linking_context_menu, + OBJECT_PT_light_linking, + OBJECT_MT_shadow_linking_context_menu, + OBJECT_PT_shadow_linking, OBJECT_PT_visibility, OBJECT_PT_lineart, OBJECT_PT_animation, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 95c8691ca7b..c5a98243147 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -3651,6 +3651,10 @@ class VIEW3D_MT_make_links(Menu): layout.operator("object.data_transfer") layout.operator("object.datalayout_transfer") + layout.separator() + layout.operator_menu_enum("object.light_linking_receivers_link", "link_state") + layout.operator_menu_enum("object.light_linking_blockers_link", "link_state") + class VIEW3D_MT_paint_vertex(Menu): bl_label = "Paint" diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 83c9ba80583..2d53042d67f 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -496,6 +496,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl engines/eevee_next/shaders/eevee_shadow_tilemap_rendermap_comp.glsl + engines/eevee_next/shaders/eevee_shadow_visibility_comp.glsl engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl engines/eevee_next/shaders/eevee_subsurface_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh b/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh index f6c05025e37..ee306c6d8a8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh +++ b/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh @@ -141,7 +141,7 @@ struct GBuffer { eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_ATTACHMENT; - header_tx.ensure_2d(GPU_R16UI, extent, usage); + header_tx.ensure_2d(GPU_R32UI, extent, usage); closure_tx.ensure_2d_array(GPU_RGB10_A2, extent, data_count, usage); normal_tx.ensure_2d_array(GPU_RG16, extent, normal_count, usage); /* Ensure layer view for frame-buffer attachment. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 77dc0372050..5fb503c9cc5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -19,6 +19,12 @@ namespace blender::eevee { +/* Convert by putting the least significant bits in the first component. */ +static uint2 uint64_to_uint2(uint64_t data) +{ + return {uint(data), uint(data >> uint64_t(32))}; +} + /* -------------------------------------------------------------------- */ /** \name LightData * \{ */ @@ -50,6 +56,7 @@ void Light::sync(ShadowModule &shadows, float4x4 object_to_world, char visibility_flag, const ::Light *la, + const LightLinking *light_linking /* = nullptr */, float threshold) { using namespace blender::math; @@ -99,6 +106,16 @@ void Light::sync(ShadowModule &shadows, shadow_discard_safe(shadows); } + if (light_linking) { + this->light_set_membership = uint64_to_uint2(light_linking->runtime.light_set_membership); + this->shadow_set_membership = uint64_to_uint2(light_linking->runtime.shadow_set_membership); + } + else { + /* Set all bits if light linking is not used. */ + this->light_set_membership = uint64_to_uint2(~uint64_t(0)); + this->shadow_set_membership = uint64_to_uint2(~uint64_t(0)); + } + this->initialized = true; } @@ -365,7 +382,7 @@ void LightModule::begin_sync() Light &light = light_map_.lookup_or_add_default(world_sunlight_key); light.used = true; - light.sync(inst_.shadows, float4x4::identity(), 0, &la, light_threshold_); + light.sync(inst_.shadows, float4x4::identity(), 0, &la, nullptr, light_threshold_); sun_lights_len_ += 1; } @@ -388,7 +405,12 @@ void LightModule::sync_light(const Object *ob, ObjectHandle &handle) light.used = true; if (handle.recalc != 0 || !light.initialized) { light.initialized = true; - light.sync(inst_.shadows, ob->object_to_world(), ob->visibility_flag, la, light_threshold_); + light.sync(inst_.shadows, + ob->object_to_world(), + ob->visibility_flag, + la, + ob->light_linking, + light_threshold_); } sun_lights_len_ += int(is_sun_light(light.type)); local_lights_len_ += int(!is_sun_light(light.type)); diff --git a/source/blender/draw/engines/eevee_next/eevee_light.hh b/source/blender/draw/engines/eevee_next/eevee_light.hh index 25557f54a43..8fdee1cc9d5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.hh +++ b/source/blender/draw/engines/eevee_next/eevee_light.hh @@ -80,6 +80,7 @@ struct Light : public LightData, NonCopyable { float4x4 object_to_world, char visibility_flag, const ::Light *la, + const LightLinking *light_linking, float threshold); void shadow_ensure(ShadowModule &shadows); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index a350ed0f7e2..6a48dbf4ca0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -340,6 +340,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_shadow_page_tile_store"; case SHADOW_TILEMAP_TAG_USAGE_VOLUME: return "eevee_shadow_tag_usage_volume"; + case SHADOW_VIEW_VISIBILITY: + return "eevee_shadow_view_visibility"; case SUBSURFACE_CONVOLVE: return "eevee_subsurface_convolve"; case SUBSURFACE_SETUP: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 065898547d0..68c4869f0c6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -139,6 +139,7 @@ enum eShaderType { SHADOW_TILEMAP_TAG_USAGE_SURFELS, SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT, SHADOW_TILEMAP_TAG_USAGE_VOLUME, + SHADOW_VIEW_VISIBILITY, SUBSURFACE_CONVOLVE, SUBSURFACE_SETUP, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 7ec33874e62..5127816f085 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1008,6 +1008,11 @@ struct LightData { /* True if the light uses jittered soft shadows. */ bool32_t shadow_jitter; float _pad2; + uint2 light_set_membership; + /** Used by shadow sync. */ + /* TODO(fclem): this should be part of #eevee::Light struct. But for some reason it gets cleared + * to zero after each sync cycle. */ + uint2 shadow_set_membership; #if USE_LIGHT_UNION union { @@ -1337,6 +1342,9 @@ struct ShadowTileMapData { float half_size; /** Offset in local space to the tilemap center in world units. Used for directional winmat. */ float2 center_offset; + /** Shadow set bitmask of the light using this tilemap. */ + uint2 shadow_set_membership; + uint2 _pad3; }; BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16) @@ -1362,6 +1370,9 @@ struct ShadowRenderView { int tilemap_lod; /** Updated region of the tilemap. */ int2 rect_min; + /** Shadow set bitmask of the light generating this view. */ + uint2 shadow_set_membership; + uint2 _pad0; }; BLI_STATIC_ASSERT_ALIGN(ShadowRenderView, 16) @@ -1725,9 +1736,10 @@ struct Surfel { int cluster_id; /** True if the light can bounce or be emitted by the surfel back face. */ bool32_t double_sided; + /** Surface receiver light set for light linking. */ + uint receiver_light_set; int _pad0; int _pad1; - int _pad2; /** Surface radiance: Emission + Direct Lighting. */ SurfelRadiance radiance_direct; /** Surface radiance: Indirect Lighting. Double buffered to avoid race conditions. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index d77feb648cf..fa6135a3c54 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -10,6 +10,7 @@ #include "BKE_global.hh" #include "BLI_math_matrix.hh" +#include "GPU_compute.hh" #include "eevee_instance.hh" @@ -28,14 +29,18 @@ ShadowTechnique ShadowModule::shadow_technique = ShadowTechnique::ATOMIC_RASTER; void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_, int2 origin_offset, int clipmap_level, - eShadowProjectionType projection_type_) + eShadowProjectionType projection_type_, + uint2 shadow_set_membership_) { - if ((projection_type != projection_type_) || (level != clipmap_level)) { + if ((projection_type != projection_type_) || (level != clipmap_level) || + (shadow_set_membership_ != shadow_set_membership)) + { set_dirty(); } projection_type = projection_type_; level = clipmap_level; light_type = eLightType::LIGHT_SUN; + shadow_set_membership = shadow_set_membership_; grid_shift = origin_offset - grid_offset; grid_offset = origin_offset; @@ -63,16 +68,23 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_, 1.0f); } -void ShadowTileMap::sync_cubeface( - eLightType light_type_, const float4x4 &object_mat_, float near_, float far_, eCubeFace face) +void ShadowTileMap::sync_cubeface(eLightType light_type_, + const float4x4 &object_mat_, + float near_, + float far_, + eCubeFace face, + uint2 shadow_set_membership_) { - if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face)) { + if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face) || + (shadow_set_membership_ != shadow_set_membership)) + { set_dirty(); } projection_type = SHADOW_PROJECTION_CUBEFACE; cubeface = face; grid_offset = int2(0); light_type = light_type_; + shadow_set_membership = shadow_set_membership_; if ((clip_near != near_) || (clip_far != far_)) { set_dirty(); @@ -237,7 +249,8 @@ void ShadowPunctual::end_sync(Light &light) float far = int_as_float(light.clip_far); for (int i : tilemaps_.index_range()) { eCubeFace face = eCubeFace(Z_NEG + i); - tilemaps_[face]->sync_cubeface(light.type, object_to_world, near, far, face); + tilemaps_[face]->sync_cubeface( + light.type, object_to_world, near, far, face, light.shadow_set_membership); } light.local.tilemaps_count = tilemaps_needed; @@ -372,7 +385,8 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera /* Equal spacing between cascades layers since we want uniform shadow density. */ int2 level_offset = origin_offset + shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i); - tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CASCADE); + tilemap->sync_orthographic( + object_mat, level_offset, level, SHADOW_PROJECTION_CASCADE, light.shadow_set_membership); /* Add shadow tile-maps grouped by lights to the GPU buffer. */ shadows_.tilemap_pool.tilemaps_data.append(*tilemap); @@ -429,7 +443,8 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera float2 light_space_camera_position = camera.position() * float2x3(object_mat.view<2, 3>()); int2 level_offset = int2(math::round(light_space_camera_position / tile_size)); - tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CLIPMAP); + tilemap->sync_orthographic( + object_mat, level_offset, level, SHADOW_PROJECTION_CLIPMAP, light.shadow_set_membership); /* Add shadow tile-maps grouped by lights to the GPU buffer. */ shadows_.tilemap_pool.tilemaps_data.append(*tilemap); @@ -1215,6 +1230,44 @@ int ShadowModule::max_view_per_tilemap() return max_view_count; } +/* Special culling pass to take shadow linking into consideration. */ +void ShadowModule::ShadowView::compute_visibility(ObjectBoundsBuf &bounds, + ObjectInfosBuf &infos, + uint resource_len, + bool /*debug_freeze*/) +{ + GPU_debug_group_begin("View.compute_visibility"); + + uint word_per_draw = this->visibility_word_per_draw(); + /* Switch between tightly packed and set of whole word per instance. */ + uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) : + resource_len * word_per_draw; + words_len = ceil_to_multiple_u(max_ii(1, words_len), 4); + /* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */ + visibility_buf_.resize(words_len); + + const uint32_t data = 0xFFFFFFFFu; + GPU_storagebuf_clear(visibility_buf_, data); + + if (do_visibility_) { + GPUShader *shader = inst_.shaders.static_shader_get(SHADOW_VIEW_VISIBILITY); + GPU_shader_bind(shader); + GPU_shader_uniform_1i(shader, "resource_len", resource_len); + GPU_shader_uniform_1i(shader, "view_len", view_len_); + GPU_shader_uniform_1i(shader, "visibility_word_per_draw", word_per_draw); + GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo_binding(shader, "bounds_buf")); + GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo_binding(shader, "visibility_buf")); + GPU_storagebuf_bind(render_view_buf_, GPU_shader_get_ssbo_binding(shader, "render_view_buf")); + GPU_storagebuf_bind(infos, DRW_OBJ_INFOS_SLOT); + GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT); + GPU_uniformbuf_bind(culling_, DRW_VIEW_CULLING_UBO_SLOT); + GPU_compute_dispatch(shader, divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE), 1, 1); + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } + + GPU_debug_group_end(); +} + void ShadowModule::set_view(View &view, int2 extent) { if (enabled_ == false) { diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.hh b/source/blender/draw/engines/eevee_next/eevee_shadow.hh index d9711f85f21..28828c3ccca 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.hh @@ -93,10 +93,15 @@ struct ShadowTileMap : public ShadowTileMapData { void sync_orthographic(const float4x4 &object_mat_, int2 origin_offset, int clipmap_level, - eShadowProjectionType projection_type_); + eShadowProjectionType projection_type_, + uint2 shadow_set_membership_ = ~uint2(0)); - void sync_cubeface( - eLightType light_type_, const float4x4 &object_mat, float near, float far, eCubeFace face); + void sync_cubeface(eLightType light_type_, + const float4x4 &object_mat, + float near, + float far, + eCubeFace face, + uint2 shadow_set_membership_ = ~uint2(0)); void debug_draw() const; @@ -278,8 +283,25 @@ class ShadowModule { /** \name Rendering * \{ */ + class ShadowView : public View { + Instance &inst_; + ShadowRenderViewBuf &render_view_buf_; + + public: + ShadowView(const char *name, Instance &inst, ShadowRenderViewBuf &render_view_buf) + : View(name, SHADOW_VIEW_MAX, true), inst_(inst), render_view_buf_(render_view_buf) + { + } + + protected: + virtual void compute_visibility(ObjectBoundsBuf &bounds, + ObjectInfosBuf &infos, + uint resource_len, + bool debug_freeze) override; + }; + /** Multi-View containing a maximum of 64 view to be rendered with the shadow pipeline. */ - View shadow_multi_view_ = {"ShadowMultiView", SHADOW_VIEW_MAX, true}; + ShadowView shadow_multi_view_ = {"ShadowMultiView", inst_, render_view_buf_}; /** Framebuffer with the atlas_tx attached. */ Framebuffer render_fb_ = {"shadow_write_framebuffer"}; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl index 4da0e481502..73a167e417c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl @@ -61,12 +61,13 @@ void main() /* Direct light. */ ClosureLightStack stack; stack.cl[0] = closure_light_new(cl, V); - light_eval_reflection(stack, P, Ng, V, vPz); + uchar receiver_light_set = gbuffer_light_link_receiver_unpack(gbuf.header); + light_eval_reflection(stack, P, Ng, V, vPz, receiver_light_set); vec3 radiance_front = stack.cl[0].light_shadowed; stack.cl[0] = closure_light_new(cl_transmit, V, gbuf.thickness); - light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness); + light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness, receiver_light_set); vec3 radiance_back = stack.cl[0].light_shadowed; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index ba5f3d5d6f2..501be6d57df 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -70,7 +70,8 @@ void main() /* TODO(fclem): If transmission (no SSS) is present, we could reduce LIGHT_CLOSURE_EVAL_COUNT * by 1 for this evaluation and skip evaluating the transmission closure twice. */ - light_eval_reflection(stack, P, Ng, V, vPz); + uchar receiver_light_set = gbuffer_light_link_receiver_unpack(gbuf.header); + light_eval_reflection(stack, P, Ng, V, vPz, receiver_light_set); if (use_transmission) { ClosureUndetermined cl_transmit = gbuffer_closure_get(gbuf, 0); @@ -85,7 +86,7 @@ void main() stack.cl[0] = closure_light_new(cl_transmit, V, gbuf.thickness); /* NOTE: Only evaluates `stack.cl[0]`. */ - light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness); + light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness, receiver_light_set); #if 1 /* TODO Limit to SSS. */ if (cl_transmit.type == CLOSURE_BSSRDF_BURLEY_ID) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl index baeea9b09ff..e48e9e648d6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_planar_frag.glsl @@ -56,12 +56,13 @@ void main() /* Direct light. */ ClosureLightStack stack; stack.cl[0] = closure_light_new(cl, V); - light_eval_reflection(stack, P, Ng, V, vPz); + uchar receiver_light_set = gbuffer_light_link_receiver_unpack(gbuf.header); + light_eval_reflection(stack, P, Ng, V, vPz, receiver_light_set); vec3 radiance_front = stack.cl[0].light_shadowed; stack.cl[0] = closure_light_new(cl_transmit, V, gbuf.thickness); - light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness); + light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness, receiver_light_set); vec3 radiance_back = stack.cl[0].light_shadowed; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl index a427fa2ade3..2d52ceb4435 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl @@ -37,7 +37,8 @@ void forward_lighting_eval(float thickness, out vec3 radiance, out vec3 transmit /* TODO(fclem): If transmission (no SSS) is present, we could reduce LIGHT_CLOSURE_EVAL_COUNT * by 1 for this evaluation and skip evaluating the transmission closure twice. */ - light_eval_reflection(stack, g_data.P, surface_N, V, vPz); + uchar receiver_light_set = receiver_light_set_get(drw_infos[resource_id]); + light_eval_reflection(stack, g_data.P, surface_N, V, vPz, receiver_light_set); #if defined(MAT_SUBSURFACE) || defined(MAT_REFRACTION) || defined(MAT_TRANSLUCENT) @@ -54,7 +55,7 @@ void forward_lighting_eval(float thickness, out vec3 radiance, out vec3 transmit stack.cl[0] = closure_light_new(cl_transmit, V, thickness); /* NOTE: Only evaluates `stack.cl[0]`. */ - light_eval_transmission(stack, g_data.P, surface_N, V, vPz, thickness); + light_eval_transmission(stack, g_data.P, surface_N, V, vPz, thickness, receiver_light_set); # if defined(MAT_SUBSURFACE) if (cl_transmit.type == CLOSURE_BSSRDF_BURLEY_ID) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl index 21362569098..84db5a2e3f1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl @@ -44,6 +44,8 @@ struct GBufferData { uint object_id; /* First world normal stored in the gbuffer. Only valid if `has_any_surface` is true. */ vec3 surface_N; + /* Index into `light_set_membership` bitmask of Lights for light linking. */ + uchar receiver_light_set; }; /* Result of Packing the GBuffer. */ @@ -310,6 +312,16 @@ bool gbuffer_is_refraction(vec4 gbuffer) return gbuffer.w < 1.0; } +uint gbuffer_light_link_receiver_pack(uchar receiver_light_set) +{ + return receiver_light_set << 26u; +} + +uint gbuffer_light_link_receiver_unpack(uint data) +{ + return data >> 26u; +} + uint gbuffer_header_pack(GBufferMode mode, uint bin) { return (mode << (4u * bin)); @@ -807,6 +819,9 @@ GBufferWriter gbuffer_pack(GBufferData data_in) gbuf.data_len = 0; gbuf.normal_len = 0; + /* Pack light linking data into header. */ + gbuf.header |= gbuffer_light_link_receiver_pack(data_in.receiver_light_set); + /* Check special configurations first. */ bool has_additional_data = false; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl index 8259f5bb27f..01c44375bb3 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl @@ -13,6 +13,8 @@ * - utility_tx */ +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) + #pragma BLENDER_REQUIRE(eevee_shadow_tracing_lib.glsl) #pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_light_lib.glsl) @@ -86,6 +88,11 @@ float light_power_get(LightData light, LightingType type) return light.power[type & 3u]; } +bool light_linking_affects_receiver(uvec2 light_set_membership, uchar receiver_light_set) +{ + return bitmask64_test(light_set_membership, receiver_light_set); +} + void light_eval_single_closure(LightData light, LightVector lv, inout ClosureLight cl, @@ -112,10 +119,15 @@ void light_eval_single(uint l_idx, vec3 P, vec3 Ng, vec3 V, - float thickness) + float thickness, + uchar receiver_light_set) { LightData light = light_buf[l_idx]; + if (!light_linking_affects_receiver(light.light_set_membership, receiver_light_set)) { + return; + } + #if defined(SPECIALIZED_SHADOW_PARAMS) int ray_count = shadow_ray_count; int ray_step_count = shadow_ray_step_count; @@ -171,37 +183,43 @@ void light_eval_single(uint l_idx, } } -void light_eval_transmission( - inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz, float thickness) +void light_eval_transmission(inout ClosureLightStack stack, + vec3 P, + vec3 Ng, + vec3 V, + float vPz, + float thickness, + uchar receiver_light_set) { #ifdef SKIP_LIGHT_EVAL return; #endif LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { - light_eval_single(l_idx, true, true, stack, P, Ng, V, thickness); + light_eval_single(l_idx, true, true, stack, P, Ng, V, thickness, receiver_light_set); } LIGHT_FOREACH_END LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, PIXEL, vPz, l_idx) { - light_eval_single(l_idx, false, true, stack, P, Ng, V, thickness); + light_eval_single(l_idx, false, true, stack, P, Ng, V, thickness, receiver_light_set); } LIGHT_FOREACH_END } -void light_eval_reflection(inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz) +void light_eval_reflection( + inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz, uchar receiver_light_set) { #ifdef SKIP_LIGHT_EVAL return; #endif LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) { - light_eval_single(l_idx, true, false, stack, P, Ng, V, 0.0); + light_eval_single(l_idx, true, false, stack, P, Ng, V, 0.0, receiver_light_set); } LIGHT_FOREACH_END LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, PIXEL, vPz, l_idx) { - light_eval_single(l_idx, false, false, stack, P, Ng, V, 0.0); + light_eval_single(l_idx, false, false, stack, P, Ng, V, 0.0, receiver_light_set); } LIGHT_FOREACH_END } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl index 654ec51c3a5..4e7bec2fdba 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl @@ -135,6 +135,8 @@ void main() render_view_buf[view_index].tilemap_tiles_index = tilemap_data.tiles_index; render_view_buf[view_index].tilemap_lod = lod; render_view_buf[view_index].rect_min = rect_min; + /* For shadow linking. */ + render_view_buf[view_index].shadow_set_membership = tilemap_data.shadow_set_membership; } } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_visibility_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_visibility_comp.glsl new file mode 100644 index 00000000000..d520ce56966 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_visibility_comp.glsl @@ -0,0 +1,94 @@ +/* SPDX-FileCopyrightText: 2022-2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Compute visibility of each resource bounds for a given shadow view. + * Checks for shadow linking properties and issue one draw call for each view. + */ +/* TODO(fclem): Could reject bounding boxes that are covering only invalid tiles. */ + +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) + +bool shadow_linking_affects_caster(uint view_id, uint resource_id) +{ + return bitmask64_test(render_view_buf[view_id].shadow_set_membership, + blocker_shadow_set_get(drw_infos[resource_id])); +} + +void mask_visibility_bit(uint view_id) +{ + if (view_len > 1) { + uint index = gl_GlobalInvocationID.x * uint(visibility_word_per_draw) + (view_id / 32u); + visibility_buf[index] &= ~(1u << view_id); + } + else { + atomicAnd(visibility_buf[gl_WorkGroupID.x], ~(1u << gl_LocalInvocationID.x)); + } +} + +/* Returns true if visibility needs to be disabled. */ +bool non_culling_tests(uint view_id, uint resource_id) +{ + if (shadow_linking_affects_caster(view_id, resource_id) == false) { + /* Object doesn't cast shadow from this light. */ + return true; + } + else if (drw_view_culling.bound_sphere.w == -1.0) { + /* View disabled. */ + return true; + } + return false; +} + +void main() +{ + if (int(gl_GlobalInvocationID.x) >= resource_len) { + return; + } + + ObjectBounds bounds = bounds_buf[gl_GlobalInvocationID.x]; + + if (drw_bounds_are_valid(bounds)) { + IsectBox box = isect_box_setup(bounds.bounding_corners[0].xyz, + bounds.bounding_corners[1].xyz, + bounds.bounding_corners[2].xyz, + bounds.bounding_corners[3].xyz); + Sphere bounding_sphere = shape_sphere(bounds.bounding_sphere.xyz, bounds.bounding_sphere.w); + Sphere inscribed_sphere = shape_sphere(bounds.bounding_sphere.xyz, + bounds._inner_sphere_radius); + + for (drw_view_id = 0u; drw_view_id < uint(view_len); drw_view_id++) { + if (non_culling_tests(drw_view_id, gl_GlobalInvocationID.x)) { + mask_visibility_bit(drw_view_id); + } + else if (drw_view_culling.bound_sphere.w == -1.0) { + /* View disabled. */ + mask_visibility_bit(drw_view_id); + } + else if (intersect_view(inscribed_sphere) == true) { + /* Visible. */ + } + else if (intersect_view(bounding_sphere) == false) { + /* Not visible. */ + mask_visibility_bit(drw_view_id); + } + else if (intersect_view(box) == false) { + /* Not visible. */ + mask_visibility_bit(drw_view_id); + } + } + } + else { + /* Culling is disabled, but we need to mask the bits for disabled views. */ + for (drw_view_id = 0u; drw_view_id < uint(view_len); drw_view_id++) { + if (non_culling_tests(drw_view_id, gl_GlobalInvocationID.x)) { + mask_visibility_bit(drw_view_id); + } + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl index 897d7c5bc25..4619dc44313 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl @@ -64,6 +64,7 @@ void main() surfel_buf[surfel_id].radiance_direct.back.rgb = is_double_sided ? g_emission : vec3(0); surfel_buf[surfel_id].radiance_direct.back.a = 0.0; surfel_buf[surfel_id].double_sided = is_double_sided; + surfel_buf[surfel_id].receiver_light_set = receiver_light_set_get(drw_infos[resource_id]); if (!capture_info_buf.capture_emission) { surfel_buf[surfel_id].radiance_direct.front.rgb = vec3(0.0); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl index 591f8667d7d..6e56ae0a27d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl @@ -93,6 +93,7 @@ void main() gbuf_data.surface_N = g_data.N; gbuf_data.thickness = thickness; gbuf_data.object_id = resource_id; + gbuf_data.receiver_light_set = receiver_light_set_get(drw_infos[resource_id]); GBufferWriter gbuf = gbuffer_pack(gbuf_data); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl index b8651e497ca..266272a5325 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl @@ -96,6 +96,7 @@ void main() gbuf_data.surface_N = g_data.N; gbuf_data.thickness = g_thickness; gbuf_data.object_id = resource_id; + gbuf_data.receiver_light_set = receiver_light_set_get(drw_infos[resource_id]); GBufferWriter gbuf = gbuffer_pack(gbuf_data); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl index a3e53922096..df952d22b85 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surfel_light_comp.glsl @@ -32,7 +32,7 @@ void main() cl_reflect.N = surfel.normal; cl_reflect.type = CLOSURE_BSDF_DIFFUSE_ID; stack.cl[0] = closure_light_new(cl_reflect, V); - light_eval_reflection(stack, P, Ng, V, 0.0); + light_eval_reflection(stack, P, Ng, V, 0.0, surfel.receiver_light_set); if (capture_info_buf.capture_indirect) { surfel_buf[index].radiance_direct.front.rgb += stack.cl[0].light_shadowed * @@ -43,7 +43,7 @@ void main() cl_transmit.N = -surfel.normal; cl_transmit.type = CLOSURE_BSDF_DIFFUSE_ID; stack.cl[0] = closure_light_new(cl_transmit, -V); - light_eval_reflection(stack, P, -Ng, -V, 0.0); + light_eval_reflection(stack, P, -Ng, -V, 0.0, surfel.receiver_light_set); if (capture_info_buf.capture_indirect) { surfel_buf[index].radiance_direct.back.rgb += stack.cl[0].light_shadowed * surfel.albedo_back; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh index efaeb4d722e..09c1d618ac7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "draw_defines.hh" #include "eevee_defines.hh" #include "gpu_shader_create_info.hh" @@ -270,6 +271,22 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_tile_store) .vertex_source("eevee_shadow_page_tile_vert.glsl") .fragment_source("eevee_shadow_page_tile_frag.glsl"); +/* Custom visibility check pass. */ +GPU_SHADER_CREATE_INFO(eevee_shadow_view_visibility) + .do_static_compilation(true) + .typedef_source("eevee_defines.hh") + .typedef_source("eevee_shader_shared.hh") + .local_group_size(DRW_VISIBILITY_GROUP_SIZE) + .define("DRW_VIEW_LEN", STRINGIFY(DRW_VIEW_MAX)) + .storage_buf(0, Qualifier::READ, "ObjectBounds", "bounds_buf[]") + .storage_buf(1, Qualifier::READ_WRITE, "uint", "visibility_buf[]") + .storage_buf(2, Qualifier::READ, "ShadowRenderView", "render_view_buf[SHADOW_VIEW_MAX]") + .push_constant(Type::INT, "resource_len") + .push_constant(Type::INT, "view_len") + .push_constant(Type::INT, "visibility_word_per_draw") + .compute_source("eevee_shadow_visibility_comp.glsl") + .additional_info("draw_view", "draw_view_culling", "draw_object_infos_new"); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/engines/workbench/workbench_private.hh b/source/blender/draw/engines/workbench/workbench_private.hh index d611b8efd4d..65d7bfaa388 100644 --- a/source/blender/draw/engines/workbench/workbench_private.hh +++ b/source/blender/draw/engines/workbench/workbench_private.hh @@ -412,6 +412,7 @@ class ShadowPass { protected: virtual void compute_visibility(ObjectBoundsBuf &bounds, + ObjectInfosBuf &infos, uint resource_len, bool debug_freeze) override; virtual VisibilityBuf &get_visibility_buffer() override; diff --git a/source/blender/draw/engines/workbench/workbench_shadow.cc b/source/blender/draw/engines/workbench/workbench_shadow.cc index 9b8955b8a62..48edefc4bad 100644 --- a/source/blender/draw/engines/workbench/workbench_shadow.cc +++ b/source/blender/draw/engines/workbench/workbench_shadow.cc @@ -208,6 +208,7 @@ void ShadowPass::ShadowView::set_mode(ShadowPass::PassType type) } void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds, + ObjectInfosBuf & /*infos*/, uint resource_len, bool /*debug_freeze*/) { diff --git a/source/blender/draw/intern/draw_manager.cc b/source/blender/draw/intern/draw_manager.cc index 6365261d9ba..e6fd642695c 100644 --- a/source/blender/draw/intern/draw_manager.cc +++ b/source/blender/draw/intern/draw_manager.cc @@ -185,7 +185,8 @@ void Manager::submit(PassMain &pass, View &view) bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d && (DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0); - view.compute_visibility(bounds_buf.current(), resource_len_, freeze_culling); + view.compute_visibility( + bounds_buf.current(), infos_buf.current(), resource_len_, freeze_culling); command::RecordingState state; state.inverted_view = view.is_inverted(); diff --git a/source/blender/draw/intern/draw_resource.hh b/source/blender/draw/intern/draw_resource.hh index ab6f0c6f01e..4ab7f1e7da5 100644 --- a/source/blender/draw/intern/draw_resource.hh +++ b/source/blender/draw/intern/draw_resource.hh @@ -73,6 +73,15 @@ inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active { object_attrs_len = 0; object_attrs_offset = 0; + light_and_shadow_set_membership = 0; + + LightLinking *light_linking = (ref.dupli_parent) != nullptr ? ref.dupli_parent->light_linking : + ref.object->light_linking; + if (light_linking) { + light_and_shadow_set_membership |= light_linking->runtime.receiver_light_set; + light_and_shadow_set_membership |= light_linking->runtime.blocker_shadow_set << 8; + } + bool is_holdout = (ref.object->base_flag & BASE_HOLDOUT) || (ref.object->visibility_flag & OB_HOLDOUT); diff --git a/source/blender/draw/intern/draw_shader_shared.hh b/source/blender/draw/intern/draw_shader_shared.hh index b8ee37731f9..65670aa03aa 100644 --- a/source/blender/draw/intern/draw_shader_shared.hh +++ b/source/blender/draw/intern/draw_shader_shared.hh @@ -173,7 +173,8 @@ struct ObjectInfos { float4 ob_color; uint index; - uint _pad2; + /** Used for Light Linking in EEVEE */ + uint light_and_shadow_set_membership; float random; eObjectInfoFlag flag; #endif @@ -185,6 +186,24 @@ struct ObjectInfos { }; BLI_STATIC_ASSERT_ALIGN(ObjectInfos, 16) +inline uint receiver_light_set_get(ObjectInfos object_infos) +{ +#if defined(GPU_SHADER) && !defined(DRAW_FINALIZE_SHADER) + return floatBitsToUint(object_infos.infos.y) & 0xFFu; +#else + return object_infos.light_and_shadow_set_membership & 0xFFu; +#endif +} + +inline uint blocker_shadow_set_get(ObjectInfos object_infos) +{ +#if defined(GPU_SHADER) && !defined(DRAW_FINALIZE_SHADER) + return (floatBitsToUint(object_infos.infos.y) >> 8u) & 0xFFu; +#else + return (object_infos.light_and_shadow_set_membership >> 8u) & 0xFFu; +#endif +} + struct ObjectBounds { /** * Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order. diff --git a/source/blender/draw/intern/draw_view.cc b/source/blender/draw/intern/draw_view.cc index 071040a7036..917b56396df 100644 --- a/source/blender/draw/intern/draw_view.cc +++ b/source/blender/draw/intern/draw_view.cc @@ -244,7 +244,10 @@ void View::compute_procedural_bounds() GPU_debug_group_end(); } -void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze) +void View::compute_visibility(ObjectBoundsBuf &bounds, + ObjectInfosBuf & /*infos*/, + uint resource_len, + bool debug_freeze) { if (debug_freeze && frozen_ == false) { data_freeze_[0] = static_cast(data_[0]); diff --git a/source/blender/draw/intern/draw_view.hh b/source/blender/draw/intern/draw_view.hh index 1c9b6a201dc..3c9f5df6b31 100644 --- a/source/blender/draw/intern/draw_view.hh +++ b/source/blender/draw/intern/draw_view.hh @@ -28,6 +28,7 @@ class Manager; /* TODO: de-duplicate. */ using ObjectBoundsBuf = StorageArrayBuffer; +using ObjectInfosBuf = StorageArrayBuffer; using VisibilityBuf = StorageArrayBuffer; class View { @@ -173,7 +174,10 @@ class View { protected: /** Called from draw manager. */ void bind(); - virtual void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze); + virtual void compute_visibility(ObjectBoundsBuf &bounds, + ObjectInfosBuf &infos, + uint resource_len, + bool debug_freeze); virtual VisibilityBuf &get_visibility_buffer(); void update_viewport_size(); diff --git a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl index 7bfd692d922..1f8b8ada716 100644 --- a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl +++ b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl @@ -11,8 +11,6 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_intersect_lib.glsl) -shared uint shared_result; - void mask_visibility_bit(uint view_id) { if (view_len > 1) { diff --git a/source/blender/gpu/shaders/common/gpu_shader_utildefines_lib.glsl b/source/blender/gpu/shaders/common/gpu_shader_utildefines_lib.glsl index 2ebc1d68cf1..3bc22cf3c66 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_utildefines_lib.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_utildefines_lib.glsl @@ -76,6 +76,16 @@ void set_flag_from_test(inout int value, bool test, int flag) /* Keep define to match C++ implementation. */ #define SET_FLAG_FROM_TEST(value, test, flag) set_flag_from_test(value, test, flag) +/** + * Return true if the bit inside bitmask at bit_index is set high. + * Assume the lower bits are inside first component of bitmask, + */ +bool bitmask64_test(uvec2 bitmask, uint bit_index) +{ + uint bitmask32 = (bit_index >= 32u) ? bitmask.y : bitmask.x; + return flag_test(bitmask32, 1u << (bit_index & 0x1Fu)); +} + /** * Pack two 16-bit uint into one 32-bit uint. */ diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 2f0ee869bca..90c315b0b9b 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -746,7 +746,7 @@ if(WITH_CYCLES OR WITH_GPU_RENDER_TESTS) if(WITH_GPU_RENDER_TESTS) list(APPEND gpu_render_tests ${render_tests}) - list(FILTER gpu_render_tests EXCLUDE REGEX light_group|light_linking|shadow_catcher|denoise|guiding|reports) + list(FILTER gpu_render_tests EXCLUDE REGEX light_group|shadow_catcher|denoise|guiding|reports) set(_gpu_render_tests_arguments) if(WITH_GPU_RENDER_TESTS_SILENT)