diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl index 475d456db7a..83c4234ce9d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl @@ -31,12 +31,12 @@ void main() frustum = isect_data_setup(pyramid); } - uint resource_id = resource_ids_buf[gl_GlobalInvocationID.x]; + uint _resource_id = resource_ids_buf[gl_GlobalInvocationID.x]; - IsectBox box = isect_data_setup(bounds_buf[resource_id].bounding_corners[0].xyz, - bounds_buf[resource_id].bounding_corners[1].xyz, - bounds_buf[resource_id].bounding_corners[2].xyz, - bounds_buf[resource_id].bounding_corners[3].xyz); + IsectBox box = isect_data_setup(bounds_buf[_resource_id].bounding_corners[0].xyz, + bounds_buf[_resource_id].bounding_corners[1].xyz, + bounds_buf[_resource_id].bounding_corners[2].xyz, + bounds_buf[_resource_id].bounding_corners[3].xyz); int clipped = 0; /* NDC space post projection [-1..1] (unclamped). */ diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh index 92452eefed2..443ac5f169a 100644 --- a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh @@ -126,13 +126,15 @@ GPU_SHADER_CREATE_INFO(workbench_next_prepass) GPU_SHADER_CREATE_INFO(workbench_color_material) .define("WORKBENCH_COLOR_MATERIAL") - .storage_buf(WB_MATERIAL_SLOT, Qualifier::READ, "vec4", "materials_data[]"); + .storage_buf(WB_MATERIAL_SLOT, Qualifier::READ, "vec4", "materials_data[]") + .storage_buf(WB_MATERIAL_START_SLOT, Qualifier::READ, "uint", "material_start_indices[]"); GPU_SHADER_CREATE_INFO(workbench_color_texture) .define("WORKBENCH_COLOR_TEXTURE") .define("WORKBENCH_TEXTURE_IMAGE_ARRAY") .define("WORKBENCH_COLOR_MATERIAL") .storage_buf(WB_MATERIAL_SLOT, Qualifier::READ, "vec4", "materials_data[]") + .storage_buf(WB_MATERIAL_START_SLOT, Qualifier::READ, "uint", "material_start_indices[]") .sampler(1, ImageType::FLOAT_2D, "imageTexture", Frequency::BATCH) .sampler(2, ImageType::FLOAT_2D_ARRAY, "imageTileArray", Frequency::BATCH) .sampler(3, ImageType::FLOAT_1D_ARRAY, "imageTileData", Frequency::BATCH) diff --git a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl index b663c029d5e..7f678e1290d 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl @@ -17,7 +17,8 @@ void workbench_material_data_get(int handle, #else # ifdef WORKBENCH_COLOR_MATERIAL - vec4 data = materials_data[handle]; + uint index = material_start_indices[resource_id] + sub_resource_id; + vec4 data = materials_data[index]; # else vec4 data = vec4(0.0); # endif diff --git a/source/blender/draw/engines/workbench/workbench_defines.hh b/source/blender/draw/engines/workbench/workbench_defines.hh index 4dfd69d9d50..86a072290e2 100644 --- a/source/blender/draw/engines/workbench/workbench_defines.hh +++ b/source/blender/draw/engines/workbench/workbench_defines.hh @@ -1,10 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +/* TEXTURE SLOTS */ #define WB_MATCAP_SLOT 0 #define WB_TEXTURE_SLOT 1 #define WB_TILE_ARRAY_SLOT 2 #define WB_TILE_DATA_SLOT 3 -#define WB_MATERIAL_SLOT 0 +/* UBO SLOTS */ #define WB_WORLD_SLOT 4 +/* SSBO SLOTS */ +#define WB_MATERIAL_SLOT 0 +#define WB_MATERIAL_START_SLOT 1 #define WB_RESOLVE_GROUP_SIZE 8 diff --git a/source/blender/draw/engines/workbench/workbench_engine.cc b/source/blender/draw/engines/workbench/workbench_engine.cc index dd3622b07d7..421cbde6313 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.cc +++ b/source/blender/draw/engines/workbench/workbench_engine.cc @@ -38,6 +38,8 @@ class Instance { DofPass dof_ps; AntiAliasingPass anti_aliasing_ps; + uint material_count = 0; + /* An array of nullptr GPUMaterial pointers so we can call DRW_cache_object_surface_material_get. * They never get actually used. */ Vector dummy_gpu_materials = {1, nullptr, {}}; @@ -68,6 +70,7 @@ class Instance { resolution, GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW); + material_count = 0; opaque_ps.sync(scene_state, resources); transparent_ps.sync(scene_state, resources); @@ -82,6 +85,7 @@ class Instance { void end_sync() { resources.material_buf.push_update(); + resources.per_obj_material_start_buf.push_update(); } void object_sync(Manager &manager, ObjectRef &ob_ref) @@ -177,6 +181,7 @@ class Instance { void mesh_sync(Manager &manager, ObjectRef &ob_ref, const ObjectState &object_state) { ResourceHandle handle = manager.resource_handle(ob_ref); + resources.per_obj_material_start_buf.get_or_resize(handle.resource_index()) = material_count; bool has_transparent_material = false; if (object_state.sculpt_pbvh) { @@ -186,7 +191,7 @@ class Instance { } else { if (object_state.use_per_material_batches) { - const int material_count = DRW_cache_object_material_count_get(ob_ref.object); + const int slot_count = DRW_cache_object_material_count_get(ob_ref.object); struct GPUBatch **batches; if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) { @@ -194,20 +199,16 @@ class Instance { } else { batches = DRW_cache_object_surface_material_get( - ob_ref.object, get_dummy_gpu_materials(material_count), material_count); + ob_ref.object, get_dummy_gpu_materials(slot_count), slot_count); } if (batches) { - for (auto i : IndexRange(material_count)) { + for (auto i : IndexRange(slot_count)) { if (batches[i] == nullptr) { continue; } - /* TODO(fclem): This create a cull-able instance for each sub-object. This is done - * for simplicity to reduce complexity. But this increase the overhead per object. - * Instead, we should use an indirection buffer to the material buffer. */ - ResourceHandle _handle = i == 0 ? handle : manager.resource_handle(ob_ref); - Material &mat = resources.material_buf.get_or_resize(_handle.resource_index()); + Material &mat = resources.material_buf.get_or_resize(material_count++); if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, i + 1)) { mat = Material(*_mat); @@ -225,7 +226,10 @@ class Instance { get_material_image(ob_ref.object, i + 1, image, iuser, sampler_state); } - draw_mesh(ob_ref, mat, batches[i], _handle, image, sampler_state, iuser); + if (i != 0) { + handle = manager.resource_sub_handle(handle); + } + draw_mesh(ob_ref, mat, batches[i], handle, image, sampler_state, iuser); } } } @@ -247,7 +251,7 @@ class Instance { } if (batch) { - Material &mat = resources.material_buf.get_or_resize(handle.resource_index()); + Material &mat = resources.material_buf.get_or_resize(material_count++); if (object_state.color_type == V3D_SHADING_OBJECT_COLOR) { mat = Material(*ob_ref.object); diff --git a/source/blender/draw/engines/workbench/workbench_mesh_passes.cc b/source/blender/draw/engines/workbench/workbench_mesh_passes.cc index 9e6d746cfa0..5f7303a9f2c 100644 --- a/source/blender/draw/engines/workbench/workbench_mesh_passes.cc +++ b/source/blender/draw/engines/workbench/workbench_mesh_passes.cc @@ -22,8 +22,9 @@ void MeshPass::init_pass(SceneResources &resources, DRWState state, int clip_pla PassMain::init(); state_set(state, clip_planes); bind_texture(WB_MATCAP_SLOT, resources.matcap_tx); - bind_ssbo(WB_MATERIAL_SLOT, &resources.material_buf); bind_ubo(WB_WORLD_SLOT, resources.world_buf); + bind_ssbo(WB_MATERIAL_SLOT, &resources.material_buf); + bind_ssbo(WB_MATERIAL_START_SLOT, &resources.per_obj_material_start_buf); if (clip_planes > 0) { bind_ubo(DRW_CLIPPING_UBO_SLOT, resources.clip_planes_buf); } diff --git a/source/blender/draw/engines/workbench/workbench_private.hh b/source/blender/draw/engines/workbench/workbench_private.hh index 8ce55ed9699..fe53d58b9cb 100644 --- a/source/blender/draw/engines/workbench/workbench_private.hh +++ b/source/blender/draw/engines/workbench/workbench_private.hh @@ -148,6 +148,7 @@ struct SceneResources { TextureFromPool depth_in_front_tx = "wb_depth_in_front_tx"; StorageVectorBuffer material_buf = {"material_buf"}; + StorageVectorBuffer per_obj_material_start_buf = {"per_obj_material_start_buf"}; UniformBuffer world_buf = {}; UniformArrayBuffer clip_planes_buf; diff --git a/source/blender/draw/intern/draw_handle.hh b/source/blender/draw/intern/draw_handle.hh index 5f96bfa5dcd..bf92a87b17c 100644 --- a/source/blender/draw/intern/draw_handle.hh +++ b/source/blender/draw/intern/draw_handle.hh @@ -26,24 +26,28 @@ struct DupliObject; namespace blender::draw { struct ResourceHandle { - uint raw; + union { + uint raw; + struct { + uint index : 26; + uint sub_index : 5; + uint inverted_handedness : 1; + }; + }; ResourceHandle() = default; ResourceHandle(uint raw_) : raw(raw_){}; ResourceHandle(uint index, bool inverted_handedness) - { - raw = index; - SET_FLAG_FROM_TEST(raw, inverted_handedness, 0x80000000u); - } + : index(index), sub_index(0), inverted_handedness(inverted_handedness){}; bool has_inverted_handedness() const { - return (raw & 0x80000000u) != 0; + return inverted_handedness != 0; } uint resource_index() const { - return (raw & 0x7FFFFFFFu); + return index; } }; diff --git a/source/blender/draw/intern/draw_manager.cc b/source/blender/draw/intern/draw_manager.cc index f34650255a5..933d2fa11af 100644 --- a/source/blender/draw/intern/draw_manager.cc +++ b/source/blender/draw/intern/draw_manager.cc @@ -55,6 +55,7 @@ void Manager::begin_sync() #endif resource_len_ = 0; attribute_len_ = 0; + sub_handle_indices_.clear(); /* TODO(fclem): Resize buffers if too big, but with an hysteresis threshold. */ object_active = DST.draw_ctx.obact; diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh index 51d5c841711..c24f08d8c4d 100644 --- a/source/blender/draw/intern/draw_manager.hh +++ b/source/blender/draw/intern/draw_manager.hh @@ -109,6 +109,9 @@ class Manager { /** Number of object attribute recorded. */ uint attribute_len_ = 0; + Vector sub_handle_indices_; + int max_sub_handles_ = 32; + Object *object_active = nullptr; public: @@ -134,6 +137,12 @@ class Manager { const float3 &bounds_center, const float3 &bounds_half_extent); + /** + * Returns a copy of the resource handle with a unique sub-index. + * Or a new full copy handle when running out of sub-indices. + */ + ResourceHandle resource_sub_handle(const ResourceHandle handle); + /** * Populate additional per resource data on demand. */ @@ -196,6 +205,7 @@ inline ResourceHandle Manager::resource_handle(const ObjectRef ref) matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object); bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object); infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object); + sub_handle_indices_.append(0); return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0); } @@ -204,6 +214,7 @@ inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix) matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix); bounds_buf.current().get_or_resize(resource_len_).sync(); infos_buf.current().get_or_resize(resource_len_).sync(); + sub_handle_indices_.append(0); return ResourceHandle(resource_len_++, false); } @@ -214,9 +225,34 @@ inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix, matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix); bounds_buf.current().get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent); infos_buf.current().get_or_resize(resource_len_).sync(); + sub_handle_indices_.append(0); return ResourceHandle(resource_len_++, false); } +inline ResourceHandle Manager::resource_sub_handle(const ResourceHandle handle) +{ + const uint i = handle.resource_index(); + + /* Always call resource_sub_handle_with a new handle or its last created sub-handle. + * This avoids reaching a point where a handle has been already saturated and each call to this + * function creates a new copy.*/ + BLI_assert(handle.sub_index == sub_handle_indices_[i]); + + if (++sub_handle_indices_[i] < max_sub_handles_) { + ResourceHandle sub_handle = handle; + sub_handle.sub_index = sub_handle_indices_[i]; + return sub_handle; + } + else { + /* Can't add more sub handles, make a full copy instead. */ + matrix_buf.current().get_or_resize(resource_len_) = matrix_buf.current()[i]; + bounds_buf.current().get_or_resize(resource_len_) = bounds_buf.current()[i]; + infos_buf.current().get_or_resize(resource_len_) = infos_buf.current()[i]; + sub_handle_indices_.append(0); + return ResourceHandle(resource_len_++, handle.has_inverted_handedness()); + } +} + inline void Manager::extract_object_attributes(ResourceHandle handle, const ObjectRef &ref, Span materials) diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 53ef194b4f5..31251d2051e 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -88,22 +88,22 @@ uniform int drw_resourceChunk; /* This is in the case we want to do a special instance drawcall for one object but still want to * have the right resourceId and all the correct ubo datas. */ uniform int drw_ResourceID; -# define resource_id drw_ResourceID +# define _resource_id_ drw_ResourceID # else -# define resource_id (gpu_BaseInstance + instanceId) +# define _resource_id_ (gpu_BaseInstance + instanceId) # endif /* Use this to declare and pass the value if * the fragment shader uses the resource_id. */ # if defined(EEVEE_GENERATED_INTERFACE) # define RESOURCE_ID_VARYING -# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# define PASS_RESOURCE_ID resourceIDFrag = _resource_id_; # elif defined(USE_GEOMETRY_SHADER) # define RESOURCE_ID_VARYING flat out int resourceIDGeom; -# define PASS_RESOURCE_ID resourceIDGeom = resource_id; +# define PASS_RESOURCE_ID resourceIDGeom = _resource_id_; # else # define RESOURCE_ID_VARYING flat out int resourceIDFrag; -# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# define PASS_RESOURCE_ID resourceIDFrag = _resource_id_; # endif # endif /* USE_GPU_SHADER_CREATE_INFO */ @@ -114,23 +114,23 @@ uniform int drw_ResourceID; #ifdef USE_GPU_SHADER_CREATE_INFO /* TODO(fclem): Rename PASS_RESOURCE_ID to DRW_RESOURCE_ID_VARYING_SET */ # if defined(UNIFORM_RESOURCE_ID) -# define resource_id drw_ResourceID +# define _resource_id_ drw_ResourceID # define PASS_RESOURCE_ID # elif defined(GPU_VERTEX_SHADER) # if defined(UNIFORM_RESOURCE_ID_NEW) -# define resource_id (drw_ResourceID >> DRW_VIEW_SHIFT) +# define _resource_id_ (drw_ResourceID >> DRW_VIEW_SHIFT) # else -# define resource_id gpu_InstanceIndex +# define _resource_id_ gpu_InstanceIndex # endif -# define PASS_RESOURCE_ID drw_ResourceID_iface.resource_index = resource_id; +# define PASS_RESOURCE_ID drw_ResourceID_iface.resource_index = _resource_id_; # elif defined(GPU_GEOMETRY_SHADER) -# define resource_id drw_ResourceID_iface_in[0].resource_index -# define PASS_RESOURCE_ID drw_ResourceID_iface_out.resource_index = resource_id; +# define _resource_id_ drw_ResourceID_iface_in[0].resource_index +# define PASS_RESOURCE_ID drw_ResourceID_iface_out.resource_index = _resource_id_; # elif defined(GPU_FRAGMENT_SHADER) -# define resource_id drw_ResourceID_iface.resource_index +# define _resource_id_ drw_ResourceID_iface.resource_index # endif /* TODO(fclem): Remove. */ @@ -149,18 +149,21 @@ uniform int drw_ResourceID; # define RESOURCE_ID_VARYING # endif -# define resource_id resourceIDGeom -# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0]; +# define _resource_id_ resourceIDGeom +# define PASS_RESOURCE_ID resourceIDFrag = _resource_id_[0]; # endif # if defined(GPU_FRAGMENT_SHADER) # if !defined(EEVEE_GENERATED_INTERFACE) flat in int resourceIDFrag; # endif -# define resource_id resourceIDFrag +# define _resource_id_ resourceIDFrag # endif #endif +#define resource_id int(_resource_id_ & 0x03FFFFFFu) +#define sub_resource_id int((_resource_id_ >> 26) & 0x1Fu) + /* Breaking this across multiple lines causes issues for some older GLSL compilers. */ /* clang-format off */ #if !defined(GPU_INTEL) && !defined(GPU_DEPRECATED_AMD_DRIVER) && (!defined(OS_MAC) || defined(GPU_METAL)) && !defined(INSTANCED_ATTR) && !defined(DRW_LEGACY_MODEL_MATRIX) diff --git a/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl b/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl index 11bf862a911..17dd56c31e7 100644 --- a/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl +++ b/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl @@ -53,7 +53,7 @@ void main() DrawPrototype proto = prototype_buf[proto_id]; uint group_id = proto.group_id; bool is_inverted = (proto.resource_handle & 0x80000000u) != 0; - uint resource_index = (proto.resource_handle & 0x7FFFFFFFu); + uint resource_index = (proto.resource_handle & 0x03FFFFFFu); /* Visibility test result. */ uint visible_instance_len = 0; @@ -117,7 +117,7 @@ void main() } else { for (uint i = dst_index; i < dst_index + visible_instance_len; i++) { - resource_id_buf[i] = resource_index; + resource_id_buf[i] = proto.resource_handle & 0x7FFFFFFFu; } } }