Draw: Use ResourceHandleRange

Optimize instancing performance by syncing all instances at once.
Part of #130291

At the moment, it only works for certain Object types in Workbench and
Overlay.
Everything else follows a path similar to the previous one.

Performance on instancing heavy scenes can range from 1.5x to 3x faster
depending on hardware and platform.

Pull Request: https://projects.blender.org/blender/blender/pulls/140378
This commit is contained in:
Miguel Pozo
2025-08-26 17:08:47 +02:00
parent 7e26323724
commit 8adb3e758f
3 changed files with 407 additions and 129 deletions

View File

@@ -10,13 +10,18 @@
#include "CLG_log.h"
#include "BLI_function_ref.hh"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_sys_types.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLF_api.hh"
@@ -29,6 +34,7 @@
#include "BKE_editmesh.hh"
#include "BKE_global.hh"
#include "BKE_grease_pencil.h"
#include "BKE_idprop.hh"
#include "BKE_lattice.hh"
#include "BKE_layer.hh"
#include "BKE_main.hh"
@@ -72,10 +78,12 @@
#include "WM_api.hh"
#include "DRW_render.hh"
#include "draw_cache.hh"
#include "draw_color_management.hh"
#include "draw_common_c.hh"
#include "draw_context_private.hh"
#include "draw_handle.hh"
#include "draw_manager_text.hh"
#include "draw_shader.hh"
#include "draw_subdivision.hh"
@@ -548,10 +556,8 @@ struct DupliCacheManager {
Object *ob = nullptr;
ID *ob_data = nullptr;
bool operator==(const DupliObject *ob_dupli)
{
return this->ob == ob_dupli->ob && this->ob_data == ob_dupli->ob_data;
}
DupliKey() = default;
DupliKey(const DupliObject *ob_dupli) : ob(ob_dupli->ob), ob_data(ob_dupli->ob_data) {}
uint64_t hash() const
{
@@ -586,8 +592,7 @@ void DupliCacheManager::try_add(blender::draw::ObjectRef &ob_ref)
return;
}
last_key_.ob = ob_ref.dupli_object_->ob;
last_key_.ob_data = ob_ref.dupli_object_->ob_data;
last_key_ = ob_ref.dupli_object_;
if (dupli_set_ == nullptr) {
dupli_set_ = MEM_new<blender::Set<DupliKey>>("DupliCacheManager::dupli_set_");
@@ -654,14 +659,259 @@ void DupliCacheManager::extract_all(ExtractionGraph &extraction)
namespace blender::draw {
ObjectRef::ObjectRef(DEGObjectIterData &iter_data, Object *ob)
: dupli_object_(iter_data.dupli_object_current),
dupli_parent_(iter_data.dupli_parent),
object(ob)
ObjectRef::ObjectRef(Object *ob, Object *dupli_parent, DupliObject *dupli_object)
: dupli_object_(dupli_object), dupli_parent_(dupli_parent), object(ob)
{
}
ObjectRef::ObjectRef(Object *ob) : object(ob) {}
ObjectRef::ObjectRef(Object &ob, Object *dupli_parent, const VectorList<DupliObject *> &duplis)
: dupli_object_(duplis[0]), dupli_parent_(dupli_parent), duplis_(&duplis), object(&ob)
{
}
} // namespace blender::draw
/** \} */
/* -------------------------------------------------------------------- */
/** \name Scene Iteration
* \{ */
namespace blender::draw {
static bool supports_handle_ranges(Object *ob)
{
if (ob->type == OB_MESH) {
/* Hair drawing doesn't support handle ranges. */
LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
const int draw_as = (psys->part->draw_as == PART_DRAW_REND) ? psys->part->ren_as :
psys->part->draw_as;
if (draw_as == PART_DRAW_PATH && DRW_object_is_visible_psys_in_active_context(ob, psys)) {
return false;
}
}
/* Smoke drawing doesn't support handle ranges. */
return !BKE_modifiers_findby_type(ob, eModifierType_Fluid);
}
return ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_POINTCLOUD, OB_GREASE_PENCIL);
}
enum class InstancesFlags : uint8_t {
IsNegativeScale = 1 << 0,
};
ENUM_OPERATORS(InstancesFlags, InstancesFlags::IsNegativeScale);
struct InstancesKey {
uint64_t hash_value;
Object *object;
ID *ob_data;
const blender::bke::GeometrySet *preview_base_geometry;
int preview_instance_index;
InstancesFlags flags;
InstancesKey(Object *object,
ID *ob_data,
InstancesFlags flags,
const blender::bke::GeometrySet *preview_base_geometry,
int preview_instance_index)
: object(object),
ob_data(ob_data),
preview_base_geometry(preview_base_geometry),
preview_instance_index(preview_instance_index),
flags(flags)
{
hash_value = get_default_hash(object);
hash_value = get_default_hash(hash_value, ob_data);
hash_value = get_default_hash(hash_value, preview_base_geometry);
hash_value = get_default_hash(hash_value, preview_instance_index);
hash_value = get_default_hash(hash_value, uint8_t(flags));
}
uint64_t hash() const
{
return hash_value;
}
bool operator<(const InstancesKey &k) const
{
if (hash_value != k.hash_value) {
return hash_value < k.hash_value;
}
if (object != k.object) {
return object < k.object;
}
if (ob_data != k.ob_data) {
return ob_data < k.ob_data;
}
if (flags != k.flags) {
return flags < k.flags;
}
if (preview_base_geometry != k.preview_base_geometry) {
return preview_base_geometry < k.preview_base_geometry;
}
if (preview_instance_index != k.preview_instance_index) {
return preview_instance_index < k.preview_instance_index;
}
return false;
}
bool operator==(const InstancesKey &k) const
{
if (hash_value != k.hash_value) {
return false;
}
if (object != k.object) {
return false;
}
if (ob_data != k.ob_data) {
return false;
}
if (flags != k.flags) {
return false;
}
if (preview_base_geometry != k.preview_base_geometry) {
return false;
}
if (preview_instance_index != k.preview_instance_index) {
return false;
}
return true;
}
};
void foreach_obref_in_scene(DRWContext &draw_ctx,
FunctionRef<bool(Object &)> should_draw_object_cb,
FunctionRef<void(ObjectRef &)> draw_object_cb)
{
DupliList duplilist;
Map<InstancesKey, VectorList<DupliObject *>> dupli_map;
Object tmp_object;
ObjectRuntimeHandle tmp_runtime;
Depsgraph *depsgraph = draw_ctx.depsgraph;
eEvaluationMode eval_mode = DEG_get_mode(depsgraph);
View3D *v3d = draw_ctx.v3d;
/* EEVEE is not supported for now. */
const bool engines_support_handle_ranges = (v3d && v3d->shading.type <= OB_SOLID) ||
BKE_scene_uses_blender_workbench(draw_ctx.scene);
DEGObjectIterSettings deg_iter_settings = {nullptr};
deg_iter_settings.depsgraph = depsgraph;
deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET;
if (v3d && v3d->flag2 & V3D_SHOW_VIEWER) {
deg_iter_settings.viewer_path = &v3d->viewer_path;
}
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if (!DEG_iterator_object_is_visible(eval_mode, ob)) {
continue;
}
int visibility = BKE_object_visibility(ob, eval_mode);
bool ob_visible = visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES);
if (ob_visible && should_draw_object_cb(*ob)) {
ObjectRef ob_ref(ob);
draw_object_cb(ob_ref);
}
bool instances_visible = (visibility & OB_VISIBLE_INSTANCES) &&
((ob->transflag & OB_DUPLI) ||
ob->runtime->geometry_set_eval != nullptr);
if (!instances_visible) {
continue;
}
duplilist.clear();
object_duplilist(
draw_ctx.depsgraph, draw_ctx.scene, ob, deg_iter_settings.included_objects, duplilist);
if (duplilist.is_empty()) {
continue;
}
dupli_map.clear();
for (DupliObject &dupli : duplilist) {
if (!DEG_iterator_dupli_is_visible(&dupli, eval_mode)) {
continue;
}
/* TODO: Optimize.
* We can't check the dupli.ob since visibility may be different than the dupli itself.
* But we should be able to check the dupli visibility without creating a temp object. */
#if 0
if (!should_draw_object_cb(*dupli.ob)) {
continue;
}
#endif
if (!engines_support_handle_ranges || !supports_handle_ranges(dupli.ob)) {
/* Sync the dupli as a single object. */
if (!evil::DEG_iterator_temp_object_from_dupli(
ob, &dupli, eval_mode, false, &tmp_object, &tmp_runtime) ||
!should_draw_object_cb(tmp_object))
{
evil::DEG_iterator_temp_object_free_properties(&dupli, &tmp_object);
continue;
}
tmp_object.light_linking = ob->light_linking;
SET_FLAG_FROM_TEST(tmp_object.transflag, is_negative_m4(dupli.mat), OB_NEG_SCALE);
tmp_object.runtime->object_to_world = float4x4(dupli.mat);
tmp_object.runtime->world_to_object = invert(tmp_object.runtime->object_to_world);
blender::draw::ObjectRef ob_ref(&tmp_object, ob, &dupli);
draw_object_cb(ob_ref);
evil::DEG_iterator_temp_object_free_properties(&dupli, &tmp_object);
continue;
}
InstancesFlags flags = InstancesFlags(0);
{
SET_FLAG_FROM_TEST(flags, is_negative_m4(dupli.mat), InstancesFlags::IsNegativeScale);
}
InstancesKey key(dupli.ob,
dupli.ob_data,
flags,
dupli.preview_base_geometry,
dupli.preview_instance_index);
dupli_map.lookup_or_add_default(key).append(&dupli);
}
for (const auto &[key, instances] : dupli_map.items()) {
DupliObject *first_dupli = instances.first();
if (!evil::DEG_iterator_temp_object_from_dupli(
ob, first_dupli, eval_mode, false, &tmp_object, &tmp_runtime) ||
!should_draw_object_cb(tmp_object))
{
evil::DEG_iterator_temp_object_free_properties(first_dupli, &tmp_object);
continue;
}
tmp_object.light_linking = ob->light_linking;
SET_FLAG_FROM_TEST(
tmp_object.transflag, bool(key.flags & InstancesFlags::IsNegativeScale), OB_NEG_SCALE);
/* Should use DrawInstances data instead. */
tmp_object.runtime->object_to_world = float4x4();
tmp_object.runtime->world_to_object = float4x4();
blender::draw::ObjectRef ob_ref(tmp_object, ob, instances);
draw_object_cb(ob_ref);
evil::DEG_iterator_temp_object_free_properties(first_dupli, &tmp_object);
}
}
DEG_OBJECT_ITER_END;
}
} // namespace blender::draw
@@ -1168,7 +1418,6 @@ static void drw_draw_render_loop_3d(DRWContext &draw_ctx, RenderEngineType *engi
Depsgraph *depsgraph = draw_ctx.depsgraph;
View3D *v3d = draw_ctx.v3d;
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
/* Check if scene needs to perform the populate loop */
const bool internal_engine = (engine_type->flag & RE_INTERNAL) != 0;
const bool draw_type_render = v3d->shading.type == OB_RENDER;
@@ -1177,28 +1426,18 @@ static void drw_draw_render_loop_3d(DRWContext &draw_ctx, RenderEngineType *engi
const bool do_populate_loop = internal_engine || overlays_on || !draw_type_render ||
gpencil_engine_needed;
auto should_draw_object = [&](Object &ob) -> bool {
return BKE_object_is_visible_in_viewport(v3d, &ob);
};
draw_ctx.enable_engines(gpencil_engine_needed, engine_type);
draw_ctx.engines_data_validate();
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
/* Only iterate over objects for internal engines or when overlays are enabled */
if (do_populate_loop) {
DEGObjectIterSettings deg_iter_settings = {nullptr};
deg_iter_settings.depsgraph = depsgraph;
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
if (v3d->flag2 & V3D_SHOW_VIEWER) {
deg_iter_settings.viewer_path = &v3d->viewer_path;
}
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;
}
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
continue;
}
blender::draw::ObjectRef ob_ref(data_, ob);
foreach_obref_in_scene(draw_ctx, should_draw_object, [&](ObjectRef &ob_ref) {
drw_engines_cache_populate(ob_ref, duplis, extraction);
}
DEG_OBJECT_ITER_END;
});
}
});
@@ -1505,33 +1744,32 @@ void DRW_render_object_iter(
Depsgraph *depsgraph,
std::function<void(blender::draw::ObjectRef &, RenderEngine *, Depsgraph *)> callback)
{
DRWContext &draw_ctx = drw_get();
using namespace blender::draw;
const int object_type_exclude_viewport = draw_ctx.v3d ?
draw_ctx.v3d->object_type_exclude_viewport :
0;
DRWContext &draw_ctx = drw_get();
View3D *v3d = draw_ctx.v3d;
auto should_draw_object = [&](Object &ob) -> bool {
if (v3d) {
return BKE_object_is_visible_in_viewport(v3d, &ob);
}
return true;
};
draw_ctx.sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
DEGObjectIterSettings deg_iter_settings = {nullptr};
deg_iter_settings.depsgraph = depsgraph;
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) == 0) {
blender::draw::ObjectRef ob_ref(data_, ob);
if (ob_ref.is_dupli() == false) {
blender::draw::drw_batch_cache_validate(ob);
}
else {
duplis.try_add(ob_ref);
}
callback(ob_ref, engine, depsgraph);
if (ob_ref.is_dupli() == false) {
blender::draw::drw_batch_cache_generate_requested(ob, *extraction.graph);
}
/* Batch generation for duplis happens after iter_callback. */
foreach_obref_in_scene(draw_ctx, should_draw_object, [&](ObjectRef &ob_ref) {
if (ob_ref.is_dupli() == false) {
blender::draw::drw_batch_cache_validate(ob_ref.object);
}
}
DEG_OBJECT_ITER_END;
else {
duplis.try_add(ob_ref);
}
callback(ob_ref, engine, depsgraph);
if (ob_ref.is_dupli() == false) {
blender::draw::drw_batch_cache_generate_requested(ob_ref.object, *extraction.graph);
}
/* Batch generation for duplis happens after iter_callback. */
});
});
}
@@ -1705,46 +1943,38 @@ void DRW_draw_select_loop(Depsgraph *depsgraph,
const int object_type_exclude_select = (v3d->object_type_exclude_viewport |
v3d->object_type_exclude_select);
bool filter_exclude = false;
DEGObjectIterSettings deg_iter_settings = {nullptr};
deg_iter_settings.depsgraph = depsgraph;
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
if (v3d->flag2 & V3D_SHOW_VIEWER) {
deg_iter_settings.viewer_path = &v3d->viewer_path;
}
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
continue;
}
if (use_pose_exception && (ob->mode & OB_MODE_POSE)) {
if ((ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
continue;
auto should_draw_object = [&](Object &ob) {
if (use_pose_exception && (ob.mode & OB_MODE_POSE)) {
if ((ob.base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
return false;
}
}
else {
if ((ob->base_flag & BASE_SELECTABLE) == 0) {
continue;
if ((ob.base_flag & BASE_SELECTABLE) == 0) {
return false;
}
}
if ((object_type_exclude_select & (1 << ob->type)) == 0) {
if ((object_type_exclude_select & (1 << ob.type)) == 0) {
if (object_filter_fn != nullptr) {
if (ob->base_flag & BASE_FROM_DUPLI) {
if (ob.base_flag & BASE_FROM_DUPLI) {
/* pass (use previous filter_exclude value) */
}
else {
filter_exclude = (object_filter_fn(ob, object_filter_user_data) == false);
filter_exclude = (object_filter_fn(&ob, object_filter_user_data) == false);
}
if (filter_exclude) {
continue;
return false;
}
}
blender::draw::ObjectRef ob_ref(data_, ob);
drw_engines_cache_populate(ob_ref, duplis, extraction);
}
}
DEG_OBJECT_ITER_END;
return true;
};
foreach_obref_in_scene(draw_ctx, should_draw_object, [&](ObjectRef &ob_ref) {
drw_engines_cache_populate(ob_ref, duplis, extraction);
});
}
});
@@ -1798,35 +2028,27 @@ void DRW_draw_depth_loop(Depsgraph *depsgraph,
draw_ctx.acquire_data();
draw_ctx.enable_engines(use_gpencil);
draw_ctx.engines_init_and_sync([&](DupliCacheManager &duplis, ExtractionGraph &extraction) {
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
DEGObjectIterSettings deg_iter_settings = {nullptr};
deg_iter_settings.depsgraph = draw_ctx.depsgraph;
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
if (v3d->flag2 & V3D_SHOW_VIEWER) {
deg_iter_settings.viewer_path = &v3d->viewer_path;
}
auto should_draw_object = [&](Object &ob) {
if (!BKE_object_is_visible_in_viewport(v3d, &ob)) {
return false;
}
if (use_only_selected && !(ob.base_flag & BASE_SELECTED)) {
return false;
}
if ((ob.base_flag & BASE_SELECTABLE) == 0) {
return false;
}
return true;
};
if (use_only_active_object) {
blender::draw::ObjectRef ob_ref(draw_ctx.obact);
drw_engines_cache_populate(ob_ref, duplis, extraction);
}
else {
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;
}
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
continue;
}
if (use_only_selected && !(ob->base_flag & BASE_SELECTED)) {
continue;
}
if ((ob->base_flag & BASE_SELECTABLE) == 0) {
continue;
}
blender::draw::ObjectRef ob_ref(data_, ob);
foreach_obref_in_scene(draw_ctx, should_draw_object, [&](ObjectRef &ob_ref) {
drw_engines_cache_populate(ob_ref, duplis, extraction);
}
DEG_OBJECT_ITER_END;
});
}
});
@@ -1862,6 +2084,8 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d)
return;
}
using namespace blender::draw;
/* Make sure select engine gets the correct vertex size. */
UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
@@ -1875,26 +2099,25 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d)
}
if (RETOPOLOGY_ENABLED(v3d) && !XRAY_ENABLED(v3d)) {
DEGObjectIterSettings deg_iter_settings = {nullptr};
deg_iter_settings.depsgraph = depsgraph;
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if (ob->type != OB_MESH) {
auto should_draw_object = [&](Object &ob) {
if (ob.type != OB_MESH) {
/* The iterator has evaluated meshes for all solid objects.
* It also has non-mesh objects however, which are not supported here. */
continue;
return false;
}
if (DRW_object_is_in_edit_mode(ob)) {
if (DRW_object_is_in_edit_mode(&ob)) {
/* Only background (non-edit) objects are used for occlusion. */
continue;
return false;
}
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
continue;
if (!BKE_object_is_visible_in_viewport(v3d, &ob)) {
return false;
}
blender::draw::ObjectRef ob_ref(data_, ob);
return true;
};
foreach_obref_in_scene(draw_ctx, should_draw_object, [&](ObjectRef &ob_ref) {
drw_engines_cache_populate(ob_ref, duplis, extraction);
}
DEG_OBJECT_ITER_END;
});
}
});

View File

@@ -215,6 +215,9 @@ class ObjectRef {
/** Object that created the dupli-list the current object is part of. */
Object *const dupli_parent_ = nullptr;
/** List of (render-compatible) duplis when rendering a ranges. */
const VectorList<DupliObject *> *duplis_ = nullptr;
/** Unique handle per object ref. */
ResourceHandleRange handle_ = {};
ResourceHandleRange sculpt_handle_ = {};
@@ -222,23 +225,34 @@ class ObjectRef {
public:
Object *const object;
ObjectRef(DEGObjectIterData &iter_data, Object *ob);
explicit ObjectRef(Object *ob);
explicit ObjectRef(Object *ob,
Object *dupli_parent = nullptr,
DupliObject *dupli_object = nullptr);
explicit ObjectRef(Object &ob, Object *dupli_parent, const VectorList<DupliObject *> &duplis);
/* Is the object coming from a Dupli system. */
bool is_dupli() const
{
return dupli_object_ != nullptr;
return dupli_parent_ != nullptr;
}
bool is_active(const Object *active_object) const
{
return (dupli_object_ ? dupli_parent_ : object) == active_object;
return (dupli_parent_ ? dupli_parent_ : object) == active_object;
}
float random() const
{
if (dupli_object_ == nullptr) {
if (duplis_) {
/* NOTE: The random property is only used by EEVEE, which currently doesn't support
instancing optimizations. However, ObjectInfos always call this function so the code is still
reachable even if its result won't be used. */
// BLI_assert_unreachable();
/* TODO: This should fill a span instead. */
return 0.0;
}
if (dupli_parent_ == nullptr) {
/* TODO(fclem): this is rather costly to do at draw time. Maybe we can
* put it in ob->runtime and make depsgraph ensure it is up to date. */
return BLI_hash_int_2d(BLI_hash_string(object->id.name + 2), 0) * (1.0f / (float)0xFFFFFFFF);
@@ -248,6 +262,14 @@ class ObjectRef {
bool find_rgba_attribute(const GPUUniformAttr &attr, float r_value[4]) const
{
if (duplis_) {
/* NOTE: This function is only called for EEVEE, which currently doesn't support instancing
* optimizations, so this code should be unreachable. */
BLI_assert_unreachable();
/* TODO: r_value should be a Span. */
return false;
}
/* If requesting instance data, check the parent particle system and object. */
if (attr.use_dupli) {
return BKE_object_dupli_find_rgba_attribute(
@@ -258,7 +280,6 @@ class ObjectRef {
LightLinking *light_linking() const
{
/* TODO: Could this be handled directly by deg_iterator_duplis_step? */
return dupli_parent_ ? dupli_parent_->light_linking : object->light_linking;
}
@@ -285,6 +306,14 @@ class ObjectRef {
* systems need to be offset appropriately. */
float4x4 particles_matrix() const
{
if (duplis_) {
/* NOTE: Objects with particles don't support instancing optimizations yet, so this code
* should be unreachable. */
BLI_assert_unreachable();
/* TODO: This should fill a span instead. */
return float4x4::identity();
}
/* TODO: Pass particle systems as a separate ObRef? */
float4x4 dupli_mat = float4x4::identity();
if (dupli_parent_ && dupli_object_) {

View File

@@ -136,10 +136,10 @@ class Manager {
* Create a new resource handle for the given object, but optionally override model matrix and
* bounds.
*/
ResourceHandle resource_handle(const ObjectRef &ref,
const float4x4 *model_matrix,
const float3 *bounds_center,
const float3 *bounds_half_extent);
ResourceHandleRange resource_handle(const ObjectRef &ref,
const float4x4 *model_matrix,
const float3 *bounds_center,
const float3 *bounds_half_extent);
/**
* Get resource id for a loose matrix. The draw-calls for this resource handle won't be culled
* and there won't be any associated object info / bounds. Assumes correct handedness / winding.
@@ -326,17 +326,42 @@ inline ResourceHandleRange Manager::resource_handle(const ObjectRef &ref, float
bool is_active_object = ref.is_active(object_active);
bool is_edit_mode = object_active && DRW_object_is_in_edit_mode(object_active) &&
ref.object->mode == object_active->mode;
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object, inflate_bounds);
infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object, is_edit_mode);
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
if (ref.duplis_) {
uint start = resource_len_;
ObjectBounds proto_bounds;
proto_bounds.sync(*ref.object, inflate_bounds);
ObjectInfos proto_info;
proto_info.sync(ref, is_active_object, is_edit_mode);
for (const DupliObject *dupli : *ref.duplis_) {
matrix_buf.current().get_or_resize(resource_len_).sync(float4x4(dupli->mat));
bounds_buf.current().get_or_resize(resource_len_) = proto_bounds;
ObjectInfos &info = infos_buf.current().get_or_resize(resource_len_);
info = proto_info;
info.random = dupli->random_id * (1.0f / (float)0xFFFFFFFF);
resource_len_++;
}
return ResourceHandleRange(start, resource_len_ - start);
}
else {
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object, inflate_bounds);
infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object, is_edit_mode);
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
}
}
inline ResourceHandle Manager::resource_handle(const ObjectRef &ref,
const float4x4 *model_matrix,
const float3 *bounds_center,
const float3 *bounds_half_extent)
inline ResourceHandleRange Manager::resource_handle(const ObjectRef &ref,
const float4x4 *model_matrix,
const float3 *bounds_center,
const float3 *bounds_half_extent)
{
BLI_assert(!ref.duplis_);
bool is_active_object = ref.is_active(object_active);
bool is_edit_mode = object_active && DRW_object_is_in_edit_mode(object_active) &&
ref.object->mode == object_active->mode;
@@ -377,6 +402,7 @@ inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix,
inline ResourceHandle Manager::resource_handle_for_psys(const ObjectRef &ref,
const float4x4 &model_matrix)
{
BLI_assert(!ref.duplis_);
bool is_active_object = ref.is_active(object_active);
bool is_edit_mode = object_active && DRW_object_is_in_edit_mode(object_active) &&
ref.object->mode == object_active->mode;