WIP: Vulkan: Workbench #107886

Closed
Jeroen Bakker wants to merge 88 commits from Jeroen-Bakker:vulkan-draw-manager-workbench into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
104 changed files with 3421 additions and 1751 deletions
Showing only changes of commit 1c6556643a - Show all commits

View File

@ -90,11 +90,18 @@ set(USD_EXTRA_ARGS
-DTBB_LIBRARIES=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
-DTbb_TBB_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
-DTBB_tbb_LIBRARY_RELEASE=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
# USD wants the tbb debug lib set even when you are doing a release build
# Otherwise it will error out during the cmake configure phase.
-DTBB_LIBRARIES_DEBUG=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
)
# Ray: I'm not sure if the other platforms relied on this or not but this is no longer
# needed for windows. If mac/lin confirm, this can be removed.
if(NOT WIN32)
LIST(append USD_EXTRA_ARGS
# USD wants the tbb debug lib set even when you are doing a release build
# Otherwise it will error out during the cmake configure phase.
-DTBB_LIBRARIES_DEBUG=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${SHAREDLIBEXT}
)
endif()
ExternalProject_Add(external_usd
URL file://${PACKAGE_DIR}/${USD_FILE}
DOWNLOAD_DIR ${DOWNLOAD_DIR}

View File

@ -104,12 +104,12 @@ if %ERRORLEVEL% NEQ 0 (
set StatusFile=%BUILD_DIR%\%1_%2.log
set original_path=%path%
set oiio_paths=%Staging%\%BuildDir%%ARCH%R\Release\openimageio\bin;%Staging%\%BuildDir%%ARCH%D\Debug\openimageio\bin
set boost_paths=%Staging%\%BuildDir%%ARCH%R\Release\boost\lib;%Staging%\%BuildDir%%ARCH%D\Debug\boost\lib
set openexr_paths=%Staging%\%BuildDir%%ARCH%R\Release\openexr\bin;%Staging%\%BuildDir%%ARCH%D\Debug\openexr\bin
set imath_paths=%Staging%\%BuildDir%%ARCH%R\Release\imath\bin;%Staging%\%BuildDir%%ARCH%D\Debug\imath\bin
set tbb_paths=%Staging%\%BuildDir%%ARCH%R\Release\tbb\bin;%Staging%\%BuildDir%%ARCH%D\Debug\tbb\bin
set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads\nasm-2.12.01\;%path%;%boost_paths%;%oiio_paths%;%openexr_paths%;%imath_paths%;%tbb_paths%
set oiio_paths=%Staging%\%BuildDir%%ARCH%R\Release\openimageio\bin
set boost_paths=%Staging%\%BuildDir%%ARCH%R\Release\boost\lib
set openexr_paths=%Staging%\%BuildDir%%ARCH%R\Release\openexr\bin
set imath_paths=%Staging%\%BuildDir%%ARCH%R\Release\imath\bin
set tbb_paths=%Staging%\%BuildDir%%ARCH%R\Release\tbb\bin
set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads\nasm-2.12.01\;%original_path%;%boost_paths%;%oiio_paths%;%openexr_paths%;%imath_paths%;%tbb_paths%
mkdir %STAGING%\%BuildDir%%ARCH%R
cd %Staging%\%BuildDir%%ARCH%R
echo %DATE% %TIME% : Start > %StatusFile%
@ -125,6 +125,12 @@ if "%NODEBUG%" == "1" goto exit
cd %BUILD_DIR%
mkdir %STAGING%\%BuildDir%%ARCH%D
cd %Staging%\%BuildDir%%ARCH%D
set oiio_paths=%Staging%\%BuildDir%%ARCH%D\Debug\openimageio\bin
set boost_paths=%Staging%\%BuildDir%%ARCH%D\Debug\boost\lib
set openexr_paths=%Staging%\%BuildDir%%ARCH%D\Debug\openexr\bin
set imath_paths=%Staging%\%BuildDir%%ARCH%D\Debug\imath\bin
set tbb_paths=%Staging%\%BuildDir%%ARCH%D\Debug\tbb\bin
set path=%BUILD_DIR%\downloads\mingw\mingw64\msys\1.0\bin\;%BUILD_DIR%\downloads\nasm-2.12.01\;%original_path%;%boost_paths%;%oiio_paths%;%openexr_paths%;%imath_paths%;%tbb_paths%
cmake -G "%CMAKE_BUILDER%" %CMAKE_BUILD_ARCH% -Thost=x64 %SOURCE_DIR% -DPACKAGE_DIR=%BUILD_DIR%/packages -DDOWNLOAD_DIR=%BUILD_DIR%/downloads -DCMAKE_BUILD_TYPE=Debug -DBUILD_MODE=Debug -DHARVEST_TARGET=%HARVEST_DIR%/%HARVESTROOT%%VSVER_SHORT%/ %CMAKE_DEBUG_OPTIONS%
echo %DATE% %TIME% : Debug Configuration done >> %StatusFile%
if "%dobuild%" == "1" (

View File

@ -30,7 +30,7 @@ int blender_device_threads(BL::Scene &b_scene)
return 0;
}
void adjust_device_info_from_preferences(DeviceInfo &info, PointerRNA cpreferences)
void static adjust_device_info_from_preferences(DeviceInfo &info, PointerRNA cpreferences)
{
if (!get_boolean(cpreferences, "peer_memory")) {
info.has_peer_memory = false;

View File

@ -610,6 +610,9 @@ def km_window(params):
("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("wm.save_mainfile",
{"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True},
{"properties": [("incremental", True)]}),
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
# Quick menu and toolbar
@ -2521,7 +2524,7 @@ def km_nla_generic(_params):
sidebar_key={"type": 'N', "value": 'PRESS'},
),
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'},
{"properties": [("use_upper_stack_evaluation", False)]}),
{"properties": [("use_upper_stack_evaluation", True)]}),
("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS'}, None),
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("isolate_action", True)]}),
@ -4510,6 +4513,12 @@ def km_face_mask(params):
{"properties": [("deselect", True)]}),
("paint.face_select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
("paint.face_select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
("paint.face_select_loop", {"type": params.select_mouse, "value": 'PRESS', "alt": True},
{"properties": [('extend', False), ('select', True)]}),
("paint.face_select_loop", {"type": params.select_mouse, "value": 'PRESS', "alt": True, "shift": True},
{"properties": [('extend', True), ('select', True)]}),
("paint.face_select_loop", {"type": params.select_mouse, "value": 'PRESS', "alt": True, "shift": True, "ctrl": True},
{"properties": [('extend', True), ('select', False)]}),
])
return keymap
@ -4591,7 +4600,7 @@ def km_pose(params):
("armature.layers_show_all", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, None),
("armature.armature_layers", {"type": 'M', "value": 'PRESS', "shift": True}, None),
("pose.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
@ -5110,8 +5119,6 @@ def km_weight_paint(params):
("paint.weight_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("paint.weight_sample", {"type": params.action_mouse, "value": 'PRESS', "ctrl": True}, None),
("paint.weight_sample_group", {"type": params.action_mouse, "value": 'PRESS', "shift": True}, None),
("paint.weight_gradient", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("type", 'LINEAR')]}),
("paint.weight_gradient", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
{"properties": [("type", 'RADIAL')]}),
("paint.weight_set", {"type": 'K', "value": 'PRESS', "shift": True}, None),
@ -5522,7 +5529,7 @@ def km_armature(params):
("armature.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
# Special transforms.
op_tool_optional(
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
(op_tool_cycle, "builtin.bone_size"), params),
op_tool_optional(
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},

View File

@ -188,6 +188,9 @@ def km_window(params):
("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("wm.save_mainfile",
{"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True},
{"properties": [("incremental", True)]}),
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
# Quick menu and toolbar

View File

@ -224,6 +224,16 @@ class TEXT_MT_view(Menu):
layout.separator()
props = layout.operator("wm.context_cycle_int", text="Zoom In")
props.data_path = "space_data.font_size"
props.reverse = False
props = layout.operator("wm.context_cycle_int", text="Zoom Out")
props.data_path = "space_data.font_size"
props.reverse = True
layout.separator()
layout.menu("TEXT_MT_view_navigation")
layout.separator()

View File

@ -283,6 +283,10 @@ class TOPBAR_MT_file(Menu):
layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
sub = layout.row()
sub.enabled = context.blend_data.is_saved
sub.operator("wm.save_mainfile", text="Save Incremental").incremental = True
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.save_as_mainfile", text="Save As...")
layout.operator_context = 'INVOKE_AREA'

View File

@ -2311,6 +2311,7 @@ class VIEW3D_MT_grease_pencil_add(Menu):
layout = self.layout
layout.operator("object.grease_pencil_add", text="Empty", icon='EMPTY_AXIS').type = 'EMPTY'
layout.operator("object.grease_pencil_add", text="Stroke", icon='STROKE').type = 'STROKE'
layout.operator("object.grease_pencil_add", text="Suzanne", icon='MONKEY').type = 'MONKEY'
class VIEW3D_MT_add(Menu):

View File

@ -11,6 +11,7 @@
#include "BLI_generic_span.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.hh"
#include "BLI_set.hh"
#include "BKE_anonymous_attribute_id.hh"
@ -928,6 +929,20 @@ void gather_attributes(AttributeAccessor src_attributes,
const IndexMask &selection,
MutableAttributeAccessor dst_attributes);
/**
* Copy attribute values from groups groups defined by \a src_offsets to groups defined by \a
* dst_offsets. The group indices are gathered to the result by \a selection. The size of each
* source and result group must be the same.
*/
void gather_attributes_group_to_group(AttributeAccessor src_attributes,
eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip,
OffsetIndices<int> src_offsets,
OffsetIndices<int> dst_offsets,
const IndexMask &selection,
MutableAttributeAccessor dst_attributes);
void copy_attributes(const AttributeAccessor src_attributes,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,

View File

@ -174,20 +174,6 @@ void ramp_blend(int type, float r_col[3], float fac, const float col[3]);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Copy/Paste
* \{ */
void BKE_material_copybuf_clear(void);
void BKE_material_copybuf_free(void);
void BKE_material_copybuf_copy(struct Main *bmain, struct Material *ma);
/**
* \return true when the material was modified.
*/
bool BKE_material_copybuf_paste(struct Main *bmain, struct Material *ma);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Default Materials
* \{ */

View File

@ -137,14 +137,13 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
return false;
}
/* Reduce user-count for current action. */
/* Unassign current action. */
if (adt->action) {
id_us_min((ID *)adt->action);
adt->action = NULL;
}
if (act == NULL) {
/* Just clearing the action. */
adt->action = NULL;
return true;
}

View File

@ -1028,6 +1028,58 @@ void gather_attributes(const AttributeAccessor src_attributes,
});
}
template<typename T>
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const Span<T> src,
MutableSpan<T> dst)
{
selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i]));
});
}
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const GSpan src,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
gather_group_to_group(src_offsets, dst_offsets, selection, src.typed<T>(), dst.typed<T>());
});
}
void gather_attributes_group_to_group(const AttributeAccessor src_attributes,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip,
const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
MutableAttributeAccessor dst_attributes)
{
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (meta_data.domain != domain) {
return true;
}
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}
if (skip.contains(id.name())) {
return true;
}
const GVArraySpan src = *src_attributes.lookup(id, domain);
bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, domain, meta_data.data_type);
gather_group_to_group(src_offsets, dst_offsets, selection, src, dst.span);
dst.finish();
return true;
});
}
void copy_attributes(const AttributeAccessor src_attributes,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,

View File

@ -970,18 +970,32 @@ void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace
/** \name Blend File Write (Partial)
* \{ */
void BKE_blendfile_write_partial_begin(Main *bmain_src)
static void blendfile_write_partial_clear_flags(Main *bmain_src)
{
BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false);
ListBase *lbarray[INDEX_ID_MAX];
int a = set_listbasepointers(bmain_src, lbarray);
while (a--) {
LISTBASE_FOREACH (ID *, id, lbarray[a]) {
id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
id->flag &= ~(LIB_CLIPBOARD_MARK);
}
}
}
void BKE_blendfile_write_partial_begin(Main *bmain)
{
blendfile_write_partial_clear_flags(bmain);
}
void BKE_blendfile_write_partial_tag_ID(ID *id, bool set)
{
if (set) {
id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
id->flag |= LIB_CLIPBOARD_MARK;
}
else {
id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
id->flag &= ~LIB_CLIPBOARD_MARK;
}
}
@ -1080,7 +1094,7 @@ bool BKE_blendfile_write_partial(Main *bmain_src,
void BKE_blendfile_write_partial_end(Main *bmain_src)
{
BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false);
blendfile_write_partial_clear_flags(bmain_src);
}
/** \} */

View File

@ -1176,62 +1176,28 @@ void CurvesGeometry::remove_points(const IndexMask &points_to_delete,
*this = curves_copy_point_selection(*this, points_to_copy, propagation_info);
}
template<typename T>
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const Span<T> src,
MutableSpan<T> dst)
{
selection.foreach_index(GrainSize(256), [&](const int64_t src_i, const int64_t dst_i) {
dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i]));
});
}
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const GSpan src,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
gather_group_to_group(src_offsets, dst_offsets, selection, src.typed<T>(), dst.typed<T>());
});
}
CurvesGeometry curves_copy_curve_selection(
const CurvesGeometry &curves,
const IndexMask &curves_to_copy,
const AnonymousAttributePropagationInfo &propagation_info)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
CurvesGeometry dst_curves(0, curves_to_copy.size());
MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
offset_indices::gather_group_sizes(
curves.points_by_curve(), curves_to_copy, new_curve_offsets.drop_back(1));
offset_indices::accumulate_counts_to_offsets(new_curve_offsets);
dst_curves.resize(new_curve_offsets.last(), dst_curves.curves_num());
const OffsetIndices src_points_by_curve = curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = offset_indices::gather_selected_offsets(
points_by_curve, curves_to_copy, dst_curves.offsets_for_write());
dst_curves.resize(dst_points_by_curve.total_size(), dst_curves.curves_num());
const AttributeAccessor src_attributes = curves.attributes();
MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (meta_data.domain != ATTR_DOMAIN_POINT) {
return true;
}
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}
const GVArraySpan src = *src_attributes.lookup(id);
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, meta_data.domain, meta_data.data_type);
gather_group_to_group(src_points_by_curve, dst_points_by_curve, curves_to_copy, src, dst.span);
dst.finish();
return true;
});
gather_attributes_group_to_group(src_attributes,
ATTR_DOMAIN_POINT,
propagation_info,
{},
points_by_curve,
dst_points_by_curve,
curves_to_copy,
dst_attributes);
gather_attributes(
src_attributes, ATTR_DOMAIN_CURVE, propagation_info, {}, curves_to_copy, dst_attributes);

View File

@ -1650,8 +1650,8 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface
return;
}
/* for vertex surface loop through tfaces and find uv color
* that provides highest alpha */
/* For vertex surface loop through `looptris` and find UV color
* that provides highest alpha. */
if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
struct ImagePool *pool = BKE_image_pool_new();

View File

@ -93,7 +93,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
int stroke_i = 0;
LISTBASE_FOREACH_INDEX (bGPDstroke *, gps, &gpf.strokes, stroke_i) {
/* TODO: check if gps->editcurve is not nullptr and parse bezier curve instead. */
/* TODO: check if `gps->editcurve` is not nullptr and parse bezier curve instead. */
/* Write curve attributes. */
stroke_cyclic.span[stroke_i] = (gps->flag & GP_STROKE_CYCLIC) != 0;

View File

@ -2272,7 +2272,7 @@ static bool lib_override_library_resync(Main *bmain,
RNA_OVERRIDE_APPLY_FLAG_NOP);
/* Clear the old shape key pointer again, otherwise it won't make ID management code happy
* when freeing (at least from user count side of things). */
* when freeing (at least from user count side of things). */
if (key_override_old_p != nullptr) {
*key_override_old_p = nullptr;
}

View File

@ -77,8 +77,6 @@
static CLG_LogRef LOG = {"bke.material"};
static void material_clear_data(ID *id);
static void material_init_data(ID *id)
{
Material *material = (Material *)id;
@ -136,26 +134,6 @@ static void material_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const
/* TODO: Duplicate Engine Settings and set runtime to nullptr. */
}
/**
* Ensure pointers to allocated memory is cleared
* (the kind of data that would have to be copied).
*
* \note Keep in sync with #material_free_data.
* \note Doesn't handle animation data (`ma->adt`).
*/
static void material_clear_data(ID *id)
{
Material *material = (Material *)id;
BLI_listbase_clear(&material->gpumaterial);
material->texpaintslot = nullptr;
material->gp_style = nullptr;
material->nodetree = nullptr;
material->preview = nullptr;
id->icon_id = 0;
}
static void material_free_data(ID *id)
{
Material *material = (Material *)id;
@ -1934,170 +1912,6 @@ void ramp_blend(int type, float r_col[3], const float fac, const float col[3])
}
}
/* -------------------------------------------------------------------- */
/** \name Material Copy/Paste
*
* As materials may reference other data, the clipboard only stores a subset of all possible data.
* The material and it's node-tree are stored and nothing else.
* Notably the following variables are *not* part of the clipboard.
*
* - The #ID (name, fake-user, custom-properties .. etc).
* - Animation data (#Material::adt, #bNodeTree::adt).
* - Texture paint slots (#Material::texpaintslot)
* as this is cache and references other ID's.
* - Grease pencil style (#Material::gp_style)
* could be supported but ID's and pointers would need to be handled carefully.
*
* When pasting, some data in the destination material is left as-is:
* - The #ID.
* - Animation data, with the exception that pasting a material without a node-tree
* will clear the existing materials node-tree & its animation.
* Note that applying existing animation to the pasted material might not make sense
* and may reference data-paths that don't resolve (depending on the kind of material).
* The user might need to clear the animation in this case.
*
* \{ */
static Material matcopybuf;
static short matcopied = 0;
void BKE_material_copybuf_clear(void)
{
if (matcopied) {
BKE_material_copybuf_free();
}
matcopybuf = blender::dna::shallow_zero_initialize();
matcopied = 0;
}
/**
* Some members should *never* be set, ensure this is always the case.
* Call at the beginning & end of functions that manipulate the clipboard.
*/
static void material_copybuf_assert_is_valid()
{
BLI_assert(!matcopybuf.id.icon_id);
BLI_assert(!matcopybuf.id.py_instance);
BLI_assert(!matcopybuf.adt);
BLI_assert(!matcopybuf.preview);
if (matcopybuf.nodetree) {
BLI_assert(!matcopybuf.nodetree->id.py_instance);
BLI_assert(!matcopybuf.nodetree->adt);
BLI_assert(!matcopybuf.nodetree->owner_id);
}
}
void BKE_material_copybuf_free(void)
{
material_copybuf_assert_is_valid();
material_free_data(&matcopybuf.id);
matcopied = 0;
}
void BKE_material_copybuf_copy(Main *bmain, Material *ma)
{
material_copybuf_assert_is_valid();
if (matcopied) {
BKE_material_copybuf_free();
}
matcopybuf = blender::dna::shallow_copy(*ma);
/* Not essential, but we never want to use any ID values from the source,
* this ensures that never happens. */
memset(&matcopybuf.id, 0, sizeof(ID));
/* Ensure dangling pointers are never copied back into a material. */
material_clear_data(&matcopybuf.id);
/* Unhandled by materials generic data functions. */
matcopybuf.adt = nullptr;
if (ma->nodetree != nullptr) {
/* Never copy animation data. */
struct {
AnimData *adt;
} backup;
backup.adt = ma->nodetree->adt;
ma->nodetree->adt = nullptr;
matcopybuf.nodetree = blender::bke::ntreeCopyTree_ex(ma->nodetree, bmain, false);
matcopybuf.nodetree->owner_id = nullptr;
ma->nodetree->adt = backup.adt;
}
/* TODO: Duplicate Engine Settings and set runtime to nullptr. */
matcopied = 1;
material_copybuf_assert_is_valid();
}
bool BKE_material_copybuf_paste(Main *bmain, Material *ma)
{
material_copybuf_assert_is_valid();
if (matcopied == 0) {
return false;
}
/* `matcopybuf` never has animation data, no need to check. */
const bool has_animdata = (ma->adt != nullptr || (ma->nodetree && ma->nodetree->adt));
const bool has_node_tree = (ma->nodetree || matcopybuf.nodetree);
AnimData *backup_nodetree_adt = nullptr;
if (ma->nodetree && matcopybuf.nodetree) {
/* Keep data to apply back to the new node-tree. */
std::swap(backup_nodetree_adt, ma->nodetree->adt);
}
/* Handles freeing nodes and and other run-time data (previews) for e.g. */
material_free_data(&ma->id);
/* Copy from `matcopybuf` preserving some members.
* NOTE: animation data isn't stored in the clipboard, any existing animation will be left as-is.
* Any undesired animation will have to be manually cleared by the user. */
{
struct {
ID id;
AnimData *adt;
} backup;
backup.id = ma->id;
backup.adt = ma->adt;
*ma = blender::dna::shallow_copy(matcopybuf);
ma->id = backup.id;
ma->adt = backup.adt;
}
if (matcopybuf.nodetree != nullptr) {
BLI_assert(matcopybuf.nodetree->adt == nullptr);
ma->nodetree = blender::bke::ntreeCopyTree_ex(matcopybuf.nodetree, bmain, false);
ma->nodetree->owner_id = &ma->id;
/* Restore animation pointer (if set). */
BLI_assert(ma->nodetree->adt == nullptr);
ma->nodetree->adt = backup_nodetree_adt;
}
if (has_node_tree || has_animdata) {
/* Important to run this when the embedded tree if freed,
* otherwise the depsgraph holds a reference to the (now freed) `ma->nodetree`.
* Also run this when a new node-tree is set to ensure it's accounted for.
* This also applies to animation data which is likely to be stored in the depsgraph. */
DEG_relations_tag_update(bmain);
}
material_copybuf_assert_is_valid();
return true;
}
/** \} */
void BKE_material_eval(struct Depsgraph *depsgraph, Material *material)
{
DEG_debug_print_eval(depsgraph, __func__, material->id.name, material);

View File

@ -1178,7 +1178,7 @@ static int mesh_tessface_calc(Mesh &mesh,
/* NOTE: quad detection issue - fourth vertex-index vs fourth loop-index:
* Polygons take care of their loops ordering, hence not of their vertices ordering.
* Currently, our tfaces' fourth vertex index might be 0 even for a quad.
* Currently, the #TFace fourth vertex index might be 0 even for a quad.
* However, we know our fourth loop index is never 0 for quads
* (because they are sorted for polygons, and our quads are still mere copies of their polygons).
* So we pass nullptr as #MFace pointer, and #mesh_loops_to_tessdata

View File

@ -1776,7 +1776,7 @@ void nodeModifySocketType(bNodeTree *ntree,
if (sock->type != socktype->type) {
/* Only reallocate the default value if the type changed so that UI data like min and max
* isn't removed. This assumes that the default value is stored in the same format for all
* socket types with the same #eNodeSocketDatatype. */
* socket types with the same #eNodeSocketDatatype. */
socket_id_user_decrement(sock);
MEM_freeN(sock->default_value);
sock->default_value = nullptr;

View File

@ -228,7 +228,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
/* Embedded ID pointers are not remapped (besides exceptions), ensure it still matches
* actual data. Note that `snode->id` was already processed (and therefore potentially
* remapped) above.*/
* remapped) above. */
if (!is_readonly) {
snode->nodetree = (snode->id == NULL) ? NULL : ntreeFromID(snode->id);
if (path != NULL) {

View File

@ -149,7 +149,7 @@ class ImplicitSharingInfo : NonCopyable, NonMovable {
* data can be freed though. */
const_cast<ImplicitSharingInfo *>(this)->delete_data_only();
/* Also remove the "fake" weak user that indicated that there was at least one strong
* user.*/
* user. */
this->remove_weak_user_and_delete_if_last();
}
}

View File

@ -38,7 +38,7 @@ template<typename T> class OffsetIndices {
/** Return the total number of elements in the referenced arrays. */
T total_size() const
{
return offsets_.size() == 1 ? 0 : offsets_.last();
return offsets_.size() > 1 ? offsets_.last() : 0;
}
/**
@ -146,6 +146,10 @@ void copy_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, Mutable
/** Gather the number of indices in each indexed group to sizes. */
void gather_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, MutableSpan<int> sizes);
/** Build new offsets that contains only the groups chosen by \a selection. */
OffsetIndices<int> gather_selected_offsets(OffsetIndices<int> src_offsets,
const IndexMask &selection,
MutableSpan<int> dst_offsets);
/**
* Create a map from indexed elements to the source indices, in other words from the larger array
* to the smaller array.

View File

@ -61,7 +61,7 @@ template<typename T> class SharedCache {
}
/** Retrieve the cached data. */
const T &data()
const T &data() const
{
BLI_assert(cache_->mutex.is_cached());
return cache_->data;

View File

@ -436,6 +436,15 @@ void BLI_str_rstrip(char *str) ATTR_NONNULL(1);
*/
int BLI_str_rstrip_float_zero(char *str, char pad) ATTR_NONNULL(1);
/**
* Strip trailing digits.
* ABC123 -> ABC
*
* \param str:
* \return The number of digits stripped.
*/
int BLI_str_rstrip_digits(char *str) ATTR_NONNULL();
/**
* Return index of a string in a string array.
*

View File

@ -17,7 +17,13 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifndef NOMINMAX
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
#else
# include <windows.h>
#endif
#undef rad
#undef rad1

View File

@ -343,7 +343,7 @@ bool BLI_dir_create_recursive(const char *dirname)
if (mode != 0) {
/* The file exists, either it's a directory (ok), or not,
* in which case this function can't do anything useful
* (the caller could remove it and re-run this function). */
* (the caller could remove it and re-run this function). */
return S_ISDIR(mode) ? true : false;
}

View File

@ -341,7 +341,7 @@ static int64_t get_size_before_gap(const Span<int16_t> indices)
BLI_assert(indices.size() >= 2);
if (indices[1] > indices[0] + 1) {
/* For sparse indices, often the next gap is just after the next index.
* In this case we can skip the logarithmic check below.*/
* In this case we can skip the logarithmic check below. */
return 1;
}
return unique_sorted_indices::find_size_of_next_range(indices);

View File

@ -38,6 +38,19 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
});
}
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
const IndexMask &selection,
MutableSpan<int> dst_offsets)
{
if (selection.is_empty()) {
return {};
}
BLI_assert(selection.size() == (dst_offsets.size() - 1));
gather_group_sizes(src_offsets, selection, dst_offsets);
accumulate_counts_to_offsets(dst_offsets);
return OffsetIndices<int>(dst_offsets);
}
void build_reverse_map(OffsetIndices<int> offsets, MutableSpan<int> r_map)
{
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {

View File

@ -1058,6 +1058,17 @@ int BLI_str_rstrip_float_zero(char *str, const char pad)
return totstrip;
}
int BLI_str_rstrip_digits(char *str)
{
int totstrip = 0;
int str_len = strlen(str);
while (str_len > 0 && isdigit(str[--str_len])) {
str[str_len] = '\0';
totstrip++;
}
return totstrip;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -125,7 +125,7 @@ TEST(string, StrCopyUTF8_TerminateEncodingEarly)
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Concatinate
/** \name String Concatenate
* \{ */
TEST(string, StrCat)

View File

@ -86,7 +86,7 @@ class AntiAliasing {
pass.framebuffer_set(&output_fb_);
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_RESOLVE));
/** \note use color_tx as dummy if AA is diabled. */
/** \note use color_tx as dummy if AA is disabled. */
pass.bind_texture("blendTex", anti_aliasing_enabled_ ? &blend_weight_tx_ : &color_tx);
pass.bind_texture("colorTex", &color_tx);
pass.bind_texture("revealTex", &reveal_tx);

View File

@ -103,7 +103,7 @@ class Instance {
objects.init(v3d, scene);
lights.init(v3d);
/* TODO(fclem): Vfx. */
/* TODO(@fclem): VFX. */
// vfx.init(use_vfx_, camera_, rv3d);
anti_aliasing.init(v3d, scene);
}

View File

@ -13,7 +13,7 @@
namespace blender::draw::greasepencil {
enum eShaderType {
/* SMAA antialiasing */
/* SMAA anti-aliasing. */
ANTIALIASING_EDGE_DETECT = 0,
ANTIALIASING_BLEND_WEIGHT,
ANTIALIASING_RESOLVE,

View File

@ -133,7 +133,7 @@ BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
#endif
struct gpObject {
/** Wether or not to apply lighting to the GPencil object. */
/** Weather or not to apply lighting to the GPencil object. */
bool1 is_shadeless;
/** Switch between 2d and 3D stroke order. */
bool1 stroke_order3d;

View File

@ -18,8 +18,10 @@
namespace blender::draw::overlay {
/* Selection engine reuse most of the Overlay engine by creating selection IDs for each
* selectable component and using a special shaders for drawing.*/
/**
* Selection engine reuse most of the Overlay engine by creating selection IDs for each
* selectable component and using a special shaders for drawing.
*/
class Instance {
const SelectionType selection_type_;

View File

@ -160,12 +160,12 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
/* Aspect uses 9 bits */
float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
/* Store if inversed in the 9th bit. */
/* Store if inverted in the 9th bit. */
if (asp > 1.0f) {
packed |= 1 << 8;
}
/* Rotation uses 9 bits */
/* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine
/* Rotation are in [-90..90] degree range, so we can encode the sign of the angle + the cosine
* because the cosine will always be positive. */
packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
/* Store sine sign in 9th bit. */
@ -197,7 +197,7 @@ static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
cfra, [&](GreasePencilDrawing &drawing) { drawings.append(&drawing); });
/* First, count how many vertices and triangles are needed for the whole object. Also record the
* offsets into the curves for the verticies and triangles. */
* offsets into the curves for the vertices and triangles. */
int total_points_num = 0;
int total_triangles_num = 0;
int v_offset = 0;
@ -240,7 +240,7 @@ static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
}
/* One vertex is stored before and after as padding. Cyclic strokes have one extra
* vertex.*/
* vertex. */
total_points_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;
total_triangles_num += (curves.points_num() + num_cyclic) * 2;
total_triangles_num += drawing.triangles().size();

View File

@ -3354,7 +3354,7 @@ static int click_select_channel_object(bContext *C,
* to avoid getting stuck there, see: #48747. */
ED_object_base_activate_with_mode_exit_if_needed(C, base); /* adds notifier */
/* Similar to outliner, do not change active element when selecting elements in range.*/
/* Similar to outliner, do not change active element when selecting elements in range. */
if ((adt) && (adt->flag & ADT_UI_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
adt->flag |= ADT_UI_ACTIVE;
}

View File

@ -51,6 +51,17 @@
#include "armature_intern.h"
/* -------------------------------------------------------------------- */
/** \name Local Utilities
* \{ */
static void pose_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
{
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer_pose.blend");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Apply Pose as Rest Pose
* \{ */
@ -789,7 +800,8 @@ static int pose_copy_exec(bContext *C, wmOperator *op)
* existing on its own.
*/
BKE_copybuffer_copy_tag_ID(&ob_copy.id);
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer_pose.blend");
pose_copybuffer_filepath_get(filepath, sizeof(filepath));
BKE_copybuffer_copy_end(temp_bmain, filepath, op->reports);
/* We clear the lists so no datablocks gets freed,
* This is required because objects in temp bmain shares same pointers
@ -842,27 +854,27 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
/* Read copy buffer .blend file. */
char filepath[FILE_MAX];
Main *tmp_bmain = BKE_main_new();
STRNCPY(tmp_bmain->filepath, BKE_main_blendfile_path_from_global());
Main *temp_bmain = BKE_main_new();
STRNCPY(temp_bmain->filepath, BKE_main_blendfile_path_from_global());
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer_pose.blend");
if (!BKE_copybuffer_read(tmp_bmain, filepath, op->reports, FILTER_ID_OB)) {
pose_copybuffer_filepath_get(filepath, sizeof(filepath));
if (!BKE_copybuffer_read(temp_bmain, filepath, op->reports, FILTER_ID_OB)) {
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is empty");
BKE_main_free(tmp_bmain);
BKE_main_free(temp_bmain);
return OPERATOR_CANCELLED;
}
/* Make sure data from this file is usable for pose paste. */
if (BLI_listbase_count_at_most(&tmp_bmain->objects, 2) != 1) {
if (BLI_listbase_count_at_most(&temp_bmain->objects, 2) != 1) {
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is not from pose mode");
BKE_main_free(tmp_bmain);
BKE_main_free(temp_bmain);
return OPERATOR_CANCELLED;
}
Object *object_from = tmp_bmain->objects.first;
Object *object_from = temp_bmain->objects.first;
bPose *pose_from = object_from->pose;
if (pose_from == NULL) {
BKE_report(op->reports, RPT_ERROR, "Internal clipboard has no pose");
BKE_main_free(tmp_bmain);
BKE_main_free(temp_bmain);
return OPERATOR_CANCELLED;
}
@ -889,7 +901,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
}
}
}
BKE_main_free(tmp_bmain);
BKE_main_free(temp_bmain);
/* Update event for pose and deformation children. */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);

View File

@ -1688,7 +1688,7 @@ static int gpencil_weight_sample_invoke(bContext *C, wmOperator *UNUSED(op), con
}
}
/* Set brush weight, based on points found.*/
/* Set brush weight, based on points found. */
if (closest_count > 0) {
if (closest_count == 1) {
brush->weight = closest_weight[0];

View File

@ -22,8 +22,9 @@ extern "C" {
namespace blender::ed::greasepencil {
void create_blank(Main &bmain, Object &object, int frame_numer);
void create_stroke(Main &bmain, Object &object, float4x4 matrix, int frame_numer);
void create_blank(Main &bmain, Object &object, int frame_number);
void create_stroke(Main &bmain, Object &object, float4x4 matrix, int frame_number);
void create_suzanne(Main &bmain, Object &object, float4x4 matrix, const int frame_number);
} // namespace blender::ed::greasepencil
#endif

View File

@ -427,6 +427,8 @@ void paintface_select_linked(struct bContext *C,
struct Object *ob,
const int mval[2],
bool select);
void paintface_select_loop(struct bContext *C, struct Object *ob, const int mval[2], bool select);
/**
* Grow the selection of faces.
* \param face_step: If true will also select faces that only touch on the corner.

View File

@ -52,8 +52,8 @@ typedef enum {
TFM_TIME_SLIDE,
TFM_TIME_SCALE,
TFM_TIME_EXTEND,
TFM_TIME_DUPLICATE,
TFM_BAKE_TIME,
/* TFM_TIME_DUPLICATE (deprecated). */
TFM_BAKE_TIME = 26,
TFM_DEPRECATED, /* was BEVEL */
TFM_BWEIGHT,
TFM_ALIGN,

View File

@ -1332,7 +1332,7 @@ void ED_view3d_shade_update(struct Main *bmain, struct View3D *v3d, struct ScrAr
#define OVERLAY_RETOPOLOGY_ENABLED(overlay) \
(((overlay).edit_flag & V3D_OVERLAY_EDIT_RETOPOLOGY) != 0)
#ifdef __APPLE__
/* Apple silicon tile depth test requires a higher value to reduce drawing artifacts.*/
/* Apple silicon tile depth test requires a higher value to reduce drawing artifacts. */
# define OVERLAY_RETOPOLOGY_MIN_OFFSET_ENABLED 0.0015f
# define OVERLAY_RETOPOLOGY_MIN_OFFSET_DISABLED 0.0015f
#else

View File

@ -12,7 +12,9 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_math_vector.hh"
#include "BLI_task.hh"
#include "BLI_vector_set.hh"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@ -350,6 +352,177 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
paintface_flush_flags(C, ob, true, false);
}
static int find_closest_edge_in_poly(ARegion *region,
blender::Span<blender::int2> edges,
blender::Span<int> poly_edges,
blender::Span<blender::float3> vert_positions,
const int mval[2])
{
using namespace blender;
int closest_edge_index;
const float2 mval_f = {float(mval[0]), float(mval[1])};
float min_distance = FLT_MAX;
for (const int i : poly_edges) {
float2 screen_coordinate;
const int2 edge = edges[i];
const float3 edge_vert_average = math::midpoint(vert_positions[edge[0]],
vert_positions[edge[1]]);
eV3DProjStatus status = ED_view3d_project_float_object(
region, edge_vert_average, screen_coordinate, V3D_PROJ_TEST_CLIP_DEFAULT);
if (status != V3D_PROJ_RET_OK) {
continue;
}
const float distance = math::distance_squared(mval_f, screen_coordinate);
if (distance < min_distance) {
min_distance = distance;
closest_edge_index = i;
}
}
return closest_edge_index;
}
static int get_opposing_edge_index(const blender::IndexRange poly,
const blender::Span<int> corner_edges,
const int current_edge_index)
{
const int index_in_poly = corner_edges.slice(poly).first_index(current_edge_index);
/* Assumes that edge index of opposing face edge is always off by 2 on quads. */
if (index_in_poly >= 2) {
return corner_edges[poly[index_in_poly - 2]];
}
/* Cannot be out of bounds because of the preceding if statement: if i < 2 then i+2 < 4. */
return corner_edges[poly[index_in_poly + 2]];
}
/**
* Follow quads around the mesh by finding opposing edges.
* \return True if the search has looped back on itself, finding the same index twice.
*/
static bool follow_face_loop(const int poly_start_index,
const int edge_start_index,
const blender::OffsetIndices<int> polys,
const blender::VArray<bool> &hide_poly,
const blender::Span<int> corner_edges,
const blender::GroupedSpan<int> edge_to_poly_map,
blender::VectorSet<int> &r_loop_polys)
{
using namespace blender;
int current_poly_index = poly_start_index;
int current_edge_index = edge_start_index;
while (current_edge_index > 0) {
int next_poly_index = -1;
for (const int poly_index : edge_to_poly_map[current_edge_index]) {
if (poly_index != current_poly_index) {
next_poly_index = poly_index;
break;
}
}
/* Edge might only have 1 poly connected. */
if (next_poly_index == -1) {
return false;
}
/* Only works on quads. */
if (polys[next_poly_index].size() != 4) {
return false;
}
/* Happens if we looped around the mesh. */
if (r_loop_polys.contains(next_poly_index)) {
return true;
}
/* Hidden polygons stop selection. */
if (hide_poly[next_poly_index]) {
return false;
}
r_loop_polys.add(next_poly_index);
const IndexRange next_poly = polys[next_poly_index];
current_edge_index = get_opposing_edge_index(next_poly, corner_edges, current_edge_index);
current_poly_index = next_poly_index;
}
return false;
}
void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const bool select)
{
using namespace blender;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
ED_view3d_select_id_validate(&vc);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
if (!ob_eval) {
return;
}
uint poly_pick_index = uint(-1);
if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &poly_pick_index)) {
return;
}
ARegion *region = CTX_wm_region(C);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
ED_view3d_init_mats_rv3d(ob_eval, rv3d);
Mesh *mesh = BKE_mesh_from_object(ob);
const Span<int> corner_edges = mesh->corner_edges();
const Span<float3> verts = mesh->vert_positions();
const OffsetIndices polys = mesh->polys();
const Span<int2> edges = mesh->edges();
const IndexRange poly = polys[poly_pick_index];
const int closest_edge_index = find_closest_edge_in_poly(
region, edges, corner_edges.slice(poly), verts, mval);
Array<int> edge_to_poly_offsets;
Array<int> edge_to_poly_indices;
const GroupedSpan<int> edge_to_poly_map = bke::mesh::build_edge_to_poly_map(
polys, corner_edges, mesh->totedge, edge_to_poly_offsets, edge_to_poly_indices);
VectorSet<int> polys_to_select;
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
".hide_poly", ATTR_DOMAIN_FACE, false);
const Span<int> polys_to_closest_edge = edge_to_poly_map[closest_edge_index];
const bool traced_full_loop = follow_face_loop(polys_to_closest_edge[0],
closest_edge_index,
polys,
hide_poly,
corner_edges,
edge_to_poly_map,
polys_to_select);
if (!traced_full_loop) {
/* Trace the other way. */
follow_face_loop(polys_to_closest_edge[1],
closest_edge_index,
polys,
hide_poly,
corner_edges,
edge_to_poly_map,
polys_to_select);
}
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
".select_poly", ATTR_DOMAIN_FACE);
select_poly.span.fill_indices(polys_to_select.as_span(), select);
select_poly.finish();
paintface_flush_flags(C, ob, true, false);
}
static bool poly_has_selected_neighbor(blender::Span<int> poly_edges,
blender::Span<blender::int2> edges,
blender::Span<bool> select_vert,

View File

@ -1643,7 +1643,16 @@ static int object_grease_pencil_add_exec(bContext *C, wmOperator *op)
greasepencil::create_stroke(*bmain, *object, mat, scene->r.cfra);
break;
}
case GP_MONKEY:
case GP_MONKEY: {
const float radius = RNA_float_get(op->ptr, "radius");
const float3 scale(radius);
float4x4 mat;
ED_object_new_primitive_matrix(C, object, loc, rot, scale, mat.ptr());
greasepencil::create_suzanne(*bmain, *object, mat, scene->r.cfra);
break;
}
case GP_LRT_OBJECT:
case GP_LRT_SCENE:
case GP_LRT_COLLECTION: {

View File

@ -11,6 +11,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
#include "DNA_curve_types.h"
#include "DNA_light_types.h"
#include "DNA_lightprobe_types.h"
@ -24,12 +25,15 @@
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
#include "BKE_appdir.h"
#include "BKE_blender_copybuffer.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_curve.h"
@ -38,6 +42,8 @@
#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_lightprobe.h"
#include "BKE_linestyle.h"
#include "BKE_main.h"
@ -95,6 +101,11 @@ static bool object_materials_supported_poll_ex(bContext *C, const Object *ob);
/** \name Local Utilities
* \{ */
static void material_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
{
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer_material.blend");
}
static bool object_array_for_shading_edit_mode_enabled_filter(const Object *ob, void *user_data)
{
bContext *C = static_cast<bContext *>(user_data);
@ -2731,8 +2742,7 @@ void TEXTURE_OT_slot_move(wmOperatorType *ot)
/** \name Material Copy Operator
* \{ */
/* material copy/paste */
static int copy_material_exec(bContext *C, wmOperator * /*op*/)
static int copy_material_exec(bContext *C, wmOperator *op)
{
Material *ma = static_cast<Material *>(
CTX_data_pointer_get_type(C, "material", &RNA_Material).data);
@ -2741,7 +2751,19 @@ static int copy_material_exec(bContext *C, wmOperator * /*op*/)
return OPERATOR_CANCELLED;
}
BKE_material_copybuf_copy(CTX_data_main(C), ma);
char filepath[FILE_MAX];
Main *bmain = CTX_data_main(C);
/* Mark is the material to use (others may be expanded). */
BKE_copybuffer_copy_begin(bmain);
BKE_copybuffer_copy_tag_ID(&ma->id);
material_copybuffer_filepath_get(filepath, sizeof(filepath));
BKE_copybuffer_copy_end(bmain, filepath, op->reports);
/* We are all done! */
BKE_report(op->reports, RPT_INFO, "Copied material to internal clipboard");
return OPERATOR_FINISHED;
}
@ -2767,8 +2789,47 @@ void MATERIAL_OT_copy(wmOperatorType *ot)
/** \name Material Paste Operator
* \{ */
/**
* Clear ID's as freeing the data-block doesn't handle reference counting.
*/
static int paste_material_nodetree_ids_decref(LibraryIDLinkCallbackData *cb_data)
{
if (cb_data->cb_flag & IDWALK_CB_USER) {
id_us_min(*cb_data->id_pointer);
}
*cb_data->id_pointer = nullptr;
return IDWALK_RET_NOP;
}
/**
* Re-map ID's from the clipboard to ID's in `bmain`, by name.
*/
static int paste_material_nodetree_ids_relink_or_clear(LibraryIDLinkCallbackData *cb_data)
{
Main *bmain = static_cast<Main *>(cb_data->user_data);
ID **id_p = cb_data->id_pointer;
if (*id_p) {
if (cb_data->cb_flag & IDWALK_CB_USER) {
id_us_min(*id_p);
}
ListBase *lb = which_libbase(bmain, GS((*id_p)->name));
ID *id_local = static_cast<ID *>(
BLI_findstring(lb, (*id_p)->name + 2, offsetof(ID, name) + 2));
*id_p = id_local;
if (cb_data->cb_flag & IDWALK_CB_USER) {
id_us_plus(id_local);
}
else if (cb_data->cb_flag & IDWALK_CB_USER_ONE) {
id_us_ensure_real(id_local);
}
id_lib_extern(id_local);
}
return IDWALK_RET_NOP;
}
static int paste_material_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Material *ma = static_cast<Material *>(
CTX_data_pointer_get_type(C, "material", &RNA_Material).data);
@ -2777,11 +2838,133 @@ static int paste_material_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (!BKE_material_copybuf_paste(CTX_data_main(C), ma)) {
BKE_report(op->reports, RPT_WARNING, "No material in the internal clipboard to paste");
/* Read copy buffer .blend file. */
char filepath[FILE_MAX];
Main *temp_bmain = BKE_main_new();
STRNCPY(temp_bmain->filepath, BKE_main_blendfile_path_from_global());
material_copybuffer_filepath_get(filepath, sizeof(filepath));
/* NOTE(@ideasman42) The node tree might reference different kinds of ID types.
* It's not clear-cut which ID types should be included, although it's unlikely
* users would want an entire scene & it's objects to be included.
* Filter a subset of ID types with some reasons for including them. */
const uint64_t ntree_filter = (
/* Material is necessary for reading the clipboard. */
FILTER_ID_MA |
/* Node-groups. */
FILTER_ID_NT |
/* Image textures. */
FILTER_ID_IM |
/* Internal text (scripts). */
FILTER_ID_TXT |
/* Texture coordinates may reference objects.
* Note that object data is *not* included. */
FILTER_ID_OB);
if (!BKE_copybuffer_read(temp_bmain, filepath, op->reports, ntree_filter)) {
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is empty");
BKE_main_free(temp_bmain);
return OPERATOR_CANCELLED;
}
/* There may be multiple materials,
* check for a property that marks this as the active material. */
Material *ma_from = nullptr;
LISTBASE_FOREACH (Material *, ma_iter, &temp_bmain->materials) {
if (ma_iter->id.flag & LIB_CLIPBOARD_MARK) {
ma_from = ma_iter;
break;
}
}
/* Make sure data from this file is usable for material paste. */
if (ma_from == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Internal clipboard is not from a material");
BKE_main_free(temp_bmain);
return OPERATOR_CANCELLED;
}
/* Keep animation by moving local animation to the paste node-tree. */
if (ma->nodetree && ma_from->nodetree) {
BLI_assert(ma_from->nodetree->adt == nullptr);
std::swap(ma->nodetree->adt, ma_from->nodetree->adt);
}
/* Needed to update #SpaceNode::nodetree else a stale pointer is used. */
if (ma->nodetree) {
bNodeTree *nodetree = ma->nodetree;
BKE_libblock_remap(bmain, ma->nodetree, ma_from->nodetree, ID_REMAP_FORCE_UI_POINTERS);
/* Free & clear data here, so user counts are handled, otherwise it's
* freed as part of #BKE_main_free which doesn't handle user-counts. */
/* Walk over all the embedded nodes ID's (non-recursively). */
BKE_library_foreach_ID_link(
bmain, &nodetree->id, paste_material_nodetree_ids_decref, nullptr, IDWALK_NOP);
ntreeFreeEmbeddedTree(nodetree);
MEM_freeN(nodetree);
ma->nodetree = nullptr;
}
/* Swap data-block content, while swapping isn't always needed,
* it means memory is properly freed in the case of allocations.. */
#define SWAP_MEMBER(member) std::swap(ma->member, ma_from->member)
/* Intentionally skip:
* - Texture painting slots.
* - Preview render.
* - Grease pencil styles (we could although they reference many ID's themselves).
*/
SWAP_MEMBER(flag);
SWAP_MEMBER(r);
SWAP_MEMBER(g);
SWAP_MEMBER(b);
SWAP_MEMBER(a);
SWAP_MEMBER(specr);
SWAP_MEMBER(specg);
SWAP_MEMBER(specb);
SWAP_MEMBER(spec);
SWAP_MEMBER(roughness);
SWAP_MEMBER(metallic);
SWAP_MEMBER(use_nodes);
SWAP_MEMBER(index);
SWAP_MEMBER(nodetree);
SWAP_MEMBER(line_col);
SWAP_MEMBER(line_priority);
SWAP_MEMBER(vcol_alpha);
SWAP_MEMBER(alpha_threshold);
SWAP_MEMBER(refract_depth);
SWAP_MEMBER(blend_method);
SWAP_MEMBER(blend_shadow);
SWAP_MEMBER(blend_flag);
SWAP_MEMBER(lineart);
#undef SWAP_MEMBER
/* The node-tree from the clipboard is now assigned to the local material,
* however the ID's it references are still part of `temp_bmain`.
* These data-blocks references must be cleared or replaces with references to `bmain`.
* TODO(@ideasman42): support merging indirectly referenced data-blocks besides the material,
* this would be useful for pasting materials with node-groups between files. */
if (ma->nodetree) {
ma->nodetree->owner_id = nullptr;
/* Map remote ID's to local ones. */
BKE_library_foreach_ID_link(
bmain, &ma->nodetree->id, paste_material_nodetree_ids_relink_or_clear, bmain, IDWALK_NOP);
}
BKE_main_free(temp_bmain);
/* Important to run this when the embedded tree if freed,
* otherwise the depsgraph holds a reference to the (now freed) `ma->nodetree`.
* Also run this when a new node-tree is set to ensure it's accounted for.
* This also applies to animation data which is likely to be stored in the depsgraph.
* Always call instead of checking when it *might* be needed. */
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
@ -2870,7 +3053,22 @@ static void paste_mtex_copybuf(ID *id)
**mtex = blender::dna::shallow_copy(mtexcopybuf);
id_us_plus((ID *)mtexcopybuf.tex);
/* NOTE(@ideasman42): the simple memory copy has no special handling for ID data-blocks.
* Ideally this would use `BKE_copybuffer_*` API's, however for common using
* copy-pasting between slots, the case a users expects to copy between files
* seems quite niche. So, do primitive ID validation. */
/* WARNING: This isn't a fool-proof solution as it's possible memory locations are reused,
* or that the ID was relocated in memory since it was copied.
* it does however guard against references to dangling pointers. */
if ((*mtex)->tex && (BLI_findindex(&G_MAIN->textures, (*mtex)->tex) == -1)) {
(*mtex)->tex = nullptr;
}
if ((*mtex)->object && (BLI_findindex(&G_MAIN->objects, (*mtex)->object) == -1)) {
(*mtex)->object = nullptr;
}
id_us_plus((ID *)(*mtex)->tex);
id_lib_extern((ID *)(*mtex)->object);
}
}

View File

@ -369,6 +369,7 @@ void PAINT_OT_face_select_all(wmOperatorType *ot);
void PAINT_OT_face_select_more(wmOperatorType *ot);
void PAINT_OT_face_select_less(wmOperatorType *ot);
void PAINT_OT_face_select_hide(wmOperatorType *ot);
void PAINT_OT_face_select_loop(wmOperatorType *ot);
void PAINT_OT_face_vert_reveal(wmOperatorType *ot);

View File

@ -1540,6 +1540,7 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_face_select_more);
WM_operatortype_append(PAINT_OT_face_select_less);
WM_operatortype_append(PAINT_OT_face_select_hide);
WM_operatortype_append(PAINT_OT_face_select_loop);
WM_operatortype_append(PAINT_OT_face_vert_reveal);

View File

@ -767,6 +767,34 @@ void PAINT_OT_face_select_less(wmOperatorType *ot)
ot->srna, "face_step", true, "Face Step", "Also deselect faces that only touch on a corner");
}
static int paintface_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
const bool select = RNA_boolean_get(op->ptr, "select");
const bool extend = RNA_boolean_get(op->ptr, "extend");
if (!extend) {
paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false);
}
view3d_operator_needs_opengl(C);
paintface_select_loop(C, CTX_data_active_object(C), event->mval, select);
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void PAINT_OT_face_select_loop(wmOperatorType *ot)
{
ot->name = "Select Loop";
ot->description = "Select face loop under the cursor";
ot->idname = "PAINT_OT_face_select_loop";
ot->invoke = paintface_select_loop_invoke;
ot->poll = facemask_paint_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "select", true, "Select", "If false, faces will be deselected");
RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
}
static int vert_select_all_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);

View File

@ -84,7 +84,8 @@ void ED_operatormacros_action(void)
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "ACTION_OT_duplicate");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_transform");
RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_DUPLICATE);
RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_TRANSLATE);
RNA_boolean_set(otmacro->ptr, "use_automerge_and_split", true);
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
}

View File

@ -497,8 +497,8 @@ void ED_operatormacros_graph(void)
"Make a copy of all selected keyframes and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "GRAPH_OT_duplicate");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_transform");
RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_DUPLICATE);
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(otmacro->ptr, "use_automerge_and_split", true);
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
}

View File

@ -57,6 +57,7 @@ set(SRC
tree/tree_element_driver.cc
tree/tree_element_gpencil_layer.cc
tree/tree_element_id.cc
tree/tree_element_id_curve.cc
tree/tree_element_id_library.cc
tree/tree_element_id_mesh.cc
tree/tree_element_id_scene.cc
@ -78,6 +79,7 @@ set(SRC
tree/tree_element_driver.hh
tree/tree_element_gpencil_layer.hh
tree/tree_element_id.hh
tree/tree_element_id_curve.hh
tree/tree_element_id_library.hh
tree/tree_element_id_mesh.hh
tree/tree_element_id_scene.hh

View File

@ -74,6 +74,18 @@ static void outliner_show_active(SpaceOutliner *space_outliner,
TreeElement *te,
ID *id);
/* -------------------------------------------------------------------- */
/** \name Local Utilities
* \{ */
static void outliner_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
{
/* NOTE: this uses the same path as the 3D viewport. */
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer.blend");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Highlight on Cursor Motion Operator
* \{ */
@ -817,7 +829,7 @@ static int outliner_id_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
outliner_copybuffer_filepath_get(filepath, sizeof(filepath));
BKE_copybuffer_copy_end(bmain, filepath, op->reports);
BKE_reportf(op->reports, RPT_INFO, "Copied %d selected data-block(s)", num_ids);
@ -851,7 +863,7 @@ static int outliner_id_paste_exec(bContext *C, wmOperator *op)
char filepath[FILE_MAX];
const short flag = FILE_AUTOSELECT | FILE_ACTIVE_COLLECTION;
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
outliner_copybuffer_filepath_get(filepath, sizeof(filepath));
const int num_pasted = BKE_copybuffer_paste(C, filepath, flag, op->reports, 0);
if (num_pasted == 0) {

View File

@ -552,24 +552,13 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
case ID_LI:
case ID_SCE:
case ID_ME:
case ID_CU_LEGACY:
BLI_assert_msg(0, "ID type expected to be expanded through new tree-element design");
break;
case ID_OB: {
outliner_add_object_contents(space_outliner, te, tselem, (Object *)id);
break;
}
case ID_CU_LEGACY: {
Curve *cu = (Curve *)id;
if (outliner_animdata_test(cu->adt)) {
outliner_add_element(space_outliner, &te->subtree, cu, te, TSE_ANIM_DATA, 0);
}
for (int a = 0; a < cu->totcol; a++) {
outliner_add_element(space_outliner, &te->subtree, cu->mat[a], te, TSE_SOME_ID, a);
}
break;
}
case ID_MB: {
MetaBall *mb = (MetaBall *)id;

View File

@ -20,6 +20,7 @@
#include "../outliner_intern.hh"
#include "common.hh"
#include "tree_element_id_curve.hh"
#include "tree_element_id_library.hh"
#include "tree_element_id_mesh.hh"
#include "tree_element_id_scene.hh"
@ -42,8 +43,9 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t
return std::make_unique<TreeElementIDScene>(legacy_te, (Scene &)id);
case ID_ME:
return std::make_unique<TreeElementIDMesh>(legacy_te, (Mesh &)id);
case ID_OB:
case ID_CU_LEGACY:
return std::make_unique<TreeElementIDCurve>(legacy_te, (Curve &)id);
case ID_OB:
case ID_MB:
case ID_MA:
case ID_TE:

View File

@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spoutliner
*/
#include "DNA_curve_types.h"
#include "DNA_listBase.h"
#include "DNA_outliner_types.h"
#include "../outliner_intern.hh"
#include "tree_element_id_curve.hh"
namespace blender::ed::outliner {
TreeElementIDCurve::TreeElementIDCurve(TreeElement &legacy_te, Curve &curve)
: TreeElementID(legacy_te, curve.id), curve_(curve)
{
}
bool TreeElementIDCurve::isExpandValid() const
{
return true;
}
void TreeElementIDCurve::expand(SpaceOutliner &space_outliner) const
{
expand_animation_data(space_outliner, curve_.adt);
expandMaterials(space_outliner);
}
void TreeElementIDCurve::expandMaterials(SpaceOutliner &space_outliner) const
{
for (int a = 0; a < curve_.totcol; a++) {
outliner_add_element(
&space_outliner, &legacy_te_.subtree, curve_.mat[a], &legacy_te_, TSE_SOME_ID, a);
}
}
} // namespace blender::ed::outliner

View File

@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spoutliner
*/
#pragma once
#include "tree_element_id.hh"
namespace blender::ed::outliner {
class TreeElementIDCurve final : public TreeElementID {
Curve &curve_;
public:
TreeElementIDCurve(TreeElement &legacy_te, Curve &curve);
void expand(SpaceOutliner &) const override;
bool isExpandValid() const override;
private:
void expandMaterials(SpaceOutliner &) const;
};
} // namespace blender::ed::outliner

View File

@ -951,11 +951,11 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
if ((len < (numstr_size[1] * 2.5f)) ||
((len < (numstr_size[0] + bg_margin + bg_margin)) && (fabs(rot_90_vec[0]) < 0.5f)))
{
/* Super short, or quite short and also shallow angle. Position below line.*/
/* Super short, or quite short and also shallow angle. Position below line. */
posit[1] = MIN2(co_ss[0][1], co_ss[2][1]) - numstr_size[1] - bg_margin - bg_margin;
}
else if (fabs(rot_90_vec[0]) < 0.2f) {
/* Very shallow angle. Shift down by text height.*/
/* Very shallow angle. Shift down by text height. */
posit[1] -= numstr_size[1];
}

View File

@ -41,6 +41,17 @@
# include "BLI_math_base.h" /* M_PI */
#endif
/* -------------------------------------------------------------------- */
/** \name Local Utilities
* \{ */
static void view3d_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
{
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer.blend");
}
/** \} */
/* ************************** copy paste ***************************** */
static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
@ -60,7 +71,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
view3d_copybuffer_filepath_get(filepath, sizeof(filepath));
BKE_copybuffer_copy_end(bmain, filepath, op->reports);
BKE_reportf(op->reports, RPT_INFO, "Copied %d selected object(s)", num_copied);
@ -92,7 +103,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op)
flag |= FILE_ACTIVE_COLLECTION;
}
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "copybuffer.blend");
view3d_copybuffer_filepath_get(filepath, sizeof(filepath));
const int num_pasted = BKE_copybuffer_paste(C, filepath, flag, op->reports, FILTER_ID_OB);
if (num_pasted == 0) {

View File

@ -782,7 +782,7 @@ static void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
bAnimContext ac;
const bool canceled = (t->state == TRANS_CANCEL);
const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
const bool duplicate = (t->flag & T_AUTOMERGE) != 0;
/* initialize relevant anim-context 'context' data */
if (ANIM_animdata_get_context(C, &ac) == 0) {

View File

@ -136,7 +136,7 @@ static void bezt_to_transdata(TransData *td,
static bool graph_edit_is_translation_mode(TransInfo *t)
{
return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE);
return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE);
}
static bool graph_edit_use_local_center(TransInfo *t)
@ -983,7 +983,7 @@ static void special_aftertrans_update__graph(bContext *C, TransInfo *t)
const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
const bool canceled = (t->state == TRANS_CANCEL);
const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
const bool duplicate = (t->flag & T_AUTOMERGE) != 0;
/* initialize relevant anim-context 'context' data */
if (ANIM_animdata_get_context(C, &ac) == 0) {

View File

@ -630,21 +630,19 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->options |= CTX_NO_PET;
}
if (t->obedit_type == OB_MESH) {
if (op && (prop = RNA_struct_find_property(op->ptr, "use_automerge_and_split")) &&
RNA_property_is_set(op->ptr, prop))
{
if (RNA_property_boolean_get(op->ptr, prop)) {
t->flag |= T_AUTOMERGE | T_AUTOSPLIT;
}
if (op && (prop = RNA_struct_find_property(op->ptr, "use_automerge_and_split")) &&
RNA_property_is_set(op->ptr, prop))
{
if (RNA_property_boolean_get(op->ptr, prop)) {
t->flag |= T_AUTOMERGE | T_AUTOSPLIT;
}
else {
char automerge = t->scene->toolsettings->automerge;
if (automerge & AUTO_MERGE) {
t->flag |= T_AUTOMERGE;
if (automerge & AUTO_MERGE_AND_SPLIT) {
t->flag |= T_AUTOSPLIT;
}
}
else if (t->obedit_type == OB_MESH) {
char automerge = t->scene->toolsettings->automerge;
if (automerge & AUTO_MERGE) {
t->flag |= T_AUTOMERGE;
if (automerge & AUTO_MERGE_AND_SPLIT) {
t->flag |= T_AUTOSPLIT;
}
}
}

View File

@ -1167,17 +1167,6 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
case TFM_TIME_SCALE:
initTimeScale(t);
break;
case TFM_TIME_DUPLICATE:
/* same as TFM_TIME_EXTEND, but we need the mode info for later
* so that duplicate-culling will work properly
*/
if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
initTranslation(t);
}
else {
initTimeTranslate(t);
}
break;
case TFM_TIME_EXTEND:
/* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
* Editors because they have only 1D transforms for time values) or TFM_TRANSLATION

View File

@ -13,6 +13,7 @@
#include "BLI_task.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BKE_unit.h"
#include "ED_screen.h"
@ -171,34 +172,31 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
void initShrinkFatten(TransInfo *t)
{
/* If not in mesh edit mode, fallback to Resize. */
if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) {
float no_mouse_dir_constraint[3];
zero_v3(no_mouse_dir_constraint);
initResize(t, no_mouse_dir_constraint);
BKE_report(t->reports, RPT_ERROR, "'Shrink/Fatten' meshes is only supported in edit mode");
t->state = TRANS_CANCEL;
}
else {
t->mode = TFM_SHRINKFATTEN;
t->transform = applyShrinkFatten;
t->handleEvent = shrinkfatten_handleEvent;
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
t->mode = TFM_SHRINKFATTEN;
t->transform = applyShrinkFatten;
t->handleEvent = shrinkfatten_handleEvent;
t->idx_max = 0;
t->num.idx_max = 0;
t->snap[0] = 1.0f;
t->snap[1] = t->snap[0] * 0.1f;
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
copy_v3_fl(t->num.val_inc, t->snap[0]);
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_LENGTH;
t->idx_max = 0;
t->num.idx_max = 0;
t->snap[0] = 1.0f;
t->snap[1] = t->snap[0] * 0.1f;
t->flag |= T_NO_CONSTRAINT;
copy_v3_fl(t->num.val_inc, t->snap[0]);
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_LENGTH;
if (t->keymap) {
/* Workaround to use the same key as the modal keymap. */
t->custom.mode.data = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE);
}
t->flag |= T_NO_CONSTRAINT;
if (t->keymap) {
/* Workaround to use the same key as the modal keymap. */
t->custom.mode.data = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE);
}
}

View File

@ -1380,7 +1380,8 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot)
Transform_Properties(ot,
P_ORIENT_AXIS | P_ORIENT_MATRIX | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR |
P_ALIGN_SNAP | P_GPENCIL_EDIT | P_CENTER | P_VIEW3D_NAVIGATION);
P_ALIGN_SNAP | P_GPENCIL_EDIT | P_CENTER | P_VIEW3D_NAVIGATION |
P_POST_TRANSFORM);
}
static int transform_from_gizmo_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)

View File

@ -67,6 +67,17 @@ static bool lib_id_preview_editing_poll(bContext *C)
return true;
}
static ID *lib_id_load_custom_preview_id_get(bContext *C, const wmOperator *op)
{
/* #invoke() gets the ID from context and saves it in the custom data. */
if (op->customdata) {
return static_cast<ID *>(op->customdata);
}
PointerRNA idptr = CTX_data_pointer_get(C, "id");
return static_cast<ID *>(idptr.data);
}
static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
{
char filepath[FILE_MAX];
@ -78,8 +89,12 @@ static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
PointerRNA idptr = CTX_data_pointer_get(C, "id");
ID *id = (ID *)idptr.data;
ID *id = lib_id_load_custom_preview_id_get(C, op);
if (!id) {
BKE_report(
op->reports, RPT_ERROR, "Failed to set preview: no ID in context (incorrect context?)");
return OPERATOR_CANCELLED;
}
BKE_previewimg_id_custom_set(id, filepath);
@ -88,6 +103,18 @@ static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
/**
* Obtain the ID from context, and spawn a File Browser to select the preview image. The
* File Browser may re-use the Asset Browser under the cursor, and clear the file-list on
* confirmation, leading to failure to obtain the ID at that point. So get it before spawning the
* File Browser (store it in the operator custom data).
*/
static int lib_id_load_custom_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
op->customdata = lib_id_load_custom_preview_id_get(C, op);
return WM_operator_filesel(C, op, event);
}
static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
{
ot->name = "Load Custom Preview";
@ -97,7 +124,7 @@ static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
/* api callbacks */
ot->poll = lib_id_preview_editing_poll;
ot->exec = lib_id_load_custom_preview_exec;
ot->invoke = WM_operator_filesel;
ot->invoke = lib_id_load_custom_preview_invoke;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;

View File

@ -67,11 +67,12 @@ bool LazyFunction::always_used_inputs_available(const Params &params) const
void Params::set_default_remaining_outputs()
{
for (const int i : fn_.outputs().index_range()) {
const Span<Output> outputs = fn_.outputs();
for (const int i : outputs.index_range()) {
if (this->output_was_set(i)) {
continue;
}
const Output &fn_output = fn_.outputs()[i];
const Output &fn_output = outputs[i];
const CPPType &type = *fn_output.type;
void *data_ptr = this->get_output_data_ptr(i);
type.value_initialize(data_ptr);

View File

@ -19,6 +19,7 @@ set(SRC
intern/add_curves_on_mesh.cc
intern/curve_constraints.cc
intern/fillet_curves.cc
intern/mesh_copy_selection.cc
intern/mesh_flip_faces.cc
intern/mesh_merge_by_distance.cc
intern/mesh_primitive_cuboid.cc
@ -43,6 +44,7 @@ set(SRC
GEO_mesh_merge_by_distance.hh
GEO_mesh_primitive_cuboid.hh
GEO_mesh_split_edges.hh
GEO_mesh_copy_selection.hh
GEO_mesh_to_curve.hh
GEO_mesh_to_volume.hh
GEO_point_merge_by_distance.hh

View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <optional>
#include "BLI_index_mask.hh"
#include "BKE_attribute.h"
struct Mesh;
namespace blender {
namespace fn {
template<typename T> class Field;
}
namespace bke {
class AnonymousAttributePropagationInfo;
}
} // namespace blender
namespace blender::geometry {
std::optional<Mesh *> mesh_copy_selection(
const Mesh &src_mesh,
const fn::Field<bool> &selection,
eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info);
std::optional<Mesh *> mesh_copy_selection_keep_verts(
const Mesh &src_mesh,
const fn::Field<bool> &selection,
eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info);
std::optional<Mesh *> mesh_copy_selection_keep_edges(
const Mesh &mesh,
const fn::Field<bool> &selection,
eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -0,0 +1,520 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_index_mask.hh"
#include "BKE_attribute.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_mesh.hh"
#include "GEO_mesh_copy_selection.hh"
namespace blender::geometry {
static void create_reverse_map(const IndexMask &mask, MutableSpan<int> r_map)
{
#ifdef DEBUG
r_map.fill(-1);
#endif
mask.foreach_index_optimized<int>(
GrainSize(4096), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; });
}
static void remap_verts(const OffsetIndices<int> src_polys,
const OffsetIndices<int> dst_polys,
const int src_verts_num,
const IndexMask &vert_mask,
const IndexMask &edge_mask,
const IndexMask &poly_mask,
const Span<int2> src_edges,
const Span<int> src_corner_verts,
MutableSpan<int2> dst_edges,
MutableSpan<int> dst_corner_verts)
{
Array<int> map(src_verts_num);
create_reverse_map(vert_mask, map);
threading::parallel_invoke(
vert_mask.size() > 1024,
[&]() {
poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
const IndexRange src_poly = src_polys[src_i];
const IndexRange dst_poly = dst_polys[dst_i];
for (const int i : src_poly.index_range()) {
dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]];
}
});
},
[&]() {
edge_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
dst_edges[dst_i][0] = map[src_edges[src_i][0]];
dst_edges[dst_i][1] = map[src_edges[src_i][1]];
});
});
}
static void remap_edges(const OffsetIndices<int> src_polys,
const OffsetIndices<int> dst_polys,
const int src_edges_num,
const IndexMask &edge_mask,
const IndexMask &poly_mask,
const Span<int> src_corner_edges,
MutableSpan<int> dst_corner_edges)
{
Array<int> map(src_edges_num);
create_reverse_map(edge_mask, map);
poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
const IndexRange src_poly = src_polys[src_i];
const IndexRange dst_poly = dst_polys[dst_i];
for (const int i : src_poly.index_range()) {
dst_corner_edges[dst_poly[i]] = map[src_corner_edges[src_poly[i]]];
}
});
}
/** A vertex is selected if it's used by a selected edge. */
static IndexMask vert_selection_from_edge(const Span<int2> edges,
const IndexMask &edge_mask,
const int verts_num,
IndexMaskMemory &memory)
{
Array<bool> array(verts_num, false);
edge_mask.foreach_index_optimized<int>([&](const int i) {
array[edges[i][0]] = true;
array[edges[i][1]] = true;
});
return IndexMask::from_bools(array, memory);
}
static IndexMask mapped_corner_selection_from_poly(const OffsetIndices<int> polys,
const IndexMask &poly_mask,
const Span<int> corner_verts_or_edges,
const int verts_or_edges_num,
IndexMaskMemory &memory)
{
Array<bool> array(verts_or_edges_num, false);
poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) {
array.as_mutable_span().fill_indices(corner_verts_or_edges.slice(polys[i]), true);
});
return IndexMask::from_bools(array, memory);
}
/** A vertex is selected if it is used by a selected face. */
static IndexMask vert_selection_from_poly(const OffsetIndices<int> polys,
const IndexMask &poly_mask,
const Span<int> corner_verts,
const int verts_num,
IndexMaskMemory &memory)
{
return mapped_corner_selection_from_poly(polys, poly_mask, corner_verts, verts_num, memory);
}
/** An edge is selected if it is used by a selected face. */
static IndexMask edge_selection_from_poly(const OffsetIndices<int> polys,
const IndexMask &poly_mask,
const Span<int> corner_edges,
const int edges_num,
IndexMaskMemory &memory)
{
return mapped_corner_selection_from_poly(polys, poly_mask, corner_edges, edges_num, memory);
}
/** An edge is selected if both of its vertices are selected. */
static IndexMask edge_selection_from_vert(const Span<int2> edges,
const Span<bool> vert_selection,
IndexMaskMemory &memory)
{
return IndexMask::from_predicate(
edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
const int2 edge = edges[i];
return vert_selection[edge[0]] && vert_selection[edge[1]];
});
}
static IndexMask poly_selection_from_mapped_corner(const OffsetIndices<int> polys,
const Span<int> corner_verts_or_edges,
const Span<bool> vert_or_edge_selection,
IndexMaskMemory &memory)
{
return IndexMask::from_predicate(
polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
const Span<int> indices = corner_verts_or_edges.slice(polys[i]);
return std::all_of(indices.begin(), indices.end(), [&](const int i) {
return vert_or_edge_selection[i];
});
});
}
/** A face is selected if all of its vertices are selected. */
static IndexMask poly_selection_from_vert(const OffsetIndices<int> polys,
const Span<int> corner_verts,
const Span<bool> vert_selection,
IndexMaskMemory &memory)
{
return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory);
}
/** A face is selected if all of its edges are selected. */
static IndexMask poly_selection_from_edge(const OffsetIndices<int> polys,
const Span<int> corner_edges,
const Span<bool> edge_mask,
IndexMaskMemory &memory)
{
return poly_selection_from_mapped_corner(polys, corner_edges, edge_mask, memory);
}
/** Create a mesh with no built-in attributes. */
static Mesh *create_mesh_no_attributes(const Mesh &params_mesh,
const int verts_num,
const int edges_num,
const int polys_num,
const int corners_num)
{
Mesh *mesh = BKE_mesh_new_nomain(0, 0, polys_num, 0);
mesh->totvert = verts_num;
mesh->totedge = edges_num;
mesh->totloop = corners_num;
CustomData_free_layer_named(&mesh->vdata, "position", 0);
CustomData_free_layer_named(&mesh->edata, ".edge_verts", 0);
CustomData_free_layer_named(&mesh->ldata, ".corner_vert", 0);
CustomData_free_layer_named(&mesh->ldata, ".corner_edge", 0);
BKE_mesh_copy_parameters_for_eval(mesh, &params_mesh);
return mesh;
}
static void copy_loose_vert_hint(const Mesh &src, Mesh &dst)
{
const auto &src_cache = src.runtime->loose_verts_cache;
if (src_cache.is_cached() && src_cache.data().count == 0) {
dst.tag_loose_verts_none();
}
}
static void copy_loose_edge_hint(const Mesh &src, Mesh &dst)
{
const auto &src_cache = src.runtime->loose_edges_cache;
if (src_cache.is_cached() && src_cache.data().count == 0) {
dst.tag_loose_edges_none();
}
}
std::optional<Mesh *> mesh_copy_selection(
const Mesh &src_mesh,
const fn::Field<bool> &selection_field,
const eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Span<int2> src_edges = src_mesh.edges();
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> src_corner_edges = src_mesh.corner_edges();
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
const bke::MeshFieldContext context(src_mesh, selection_domain);
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
if (selection.is_empty()) {
return std::nullopt;
}
if (const std::optional<bool> single = selection.get_if_single()) {
return *single ? std::nullopt : std::make_optional<Mesh *>(nullptr);
}
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
IndexMask vert_mask;
IndexMask edge_mask;
IndexMask poly_mask;
switch (selection_domain) {
case ATTR_DOMAIN_POINT: {
const VArraySpan<bool> span(selection);
threading::parallel_invoke(
src_mesh.totvert > 1024,
[&]() { vert_mask = IndexMask::from_bools(span, memory.local()); },
[&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
[&]() {
poly_mask = poly_selection_from_vert(
src_polys, src_corner_verts, span, memory.local());
});
break;
}
case ATTR_DOMAIN_EDGE: {
const VArraySpan<bool> span(selection);
threading::parallel_invoke(
src_edges.size() > 1024,
[&]() {
edge_mask = IndexMask::from_bools(span, memory.local());
vert_mask = vert_selection_from_edge(
src_edges, edge_mask, src_mesh.totvert, memory.local());
},
[&]() {
poly_mask = poly_selection_from_edge(
src_polys, src_corner_edges, span, memory.local());
});
break;
}
case ATTR_DOMAIN_FACE: {
const VArraySpan<bool> span(selection);
poly_mask = IndexMask::from_bools(span, memory.local());
threading::parallel_invoke(
poly_mask.size() > 1024,
[&]() {
vert_mask = vert_selection_from_poly(
src_polys, poly_mask, src_corner_verts, src_mesh.totvert, memory.local());
},
[&]() {
edge_mask = edge_selection_from_poly(
src_polys, poly_mask, src_corner_edges, src_mesh.totedge, memory.local());
});
break;
}
default:
BLI_assert_unreachable();
break;
}
if (vert_mask.is_empty()) {
return nullptr;
}
if (vert_mask.size() == src_mesh.totvert) {
return std::nullopt;
}
Mesh *dst_mesh = create_mesh_no_attributes(
src_mesh, vert_mask.size(), edge_mask.size(), poly_mask.size(), 0);
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
dst_attributes.add<int2>(".edge_verts", ATTR_DOMAIN_EDGE, bke::AttributeInitConstruct());
MutableSpan<int2> dst_edges = dst_mesh->edges_for_write();
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
dst_mesh->totloop = dst_polys.total_size();
dst_attributes.add<int>(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
MutableSpan<int> dst_corner_verts = dst_mesh->corner_verts_for_write();
MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
threading::parallel_invoke(
vert_mask.size() > 1024,
[&]() {
remap_verts(src_polys,
dst_polys,
src_mesh.totvert,
vert_mask,
edge_mask,
poly_mask,
src_edges,
src_corner_verts,
dst_edges,
dst_corner_verts);
},
[&]() {
remap_edges(src_polys,
dst_polys,
src_edges.size(),
edge_mask,
poly_mask,
src_corner_edges,
dst_corner_edges);
},
[&]() {
bke::gather_attributes(
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_mask, dst_attributes);
bke::gather_attributes(src_attributes,
ATTR_DOMAIN_EDGE,
propagation_info,
{".edge_verts"},
edge_mask,
dst_attributes);
bke::gather_attributes(
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
bke::gather_attributes_group_to_group(src_attributes,
ATTR_DOMAIN_CORNER,
propagation_info,
{".corner_edge", ".corner_vert"},
src_polys,
dst_polys,
poly_mask,
dst_attributes);
});
if (selection_domain == ATTR_DOMAIN_EDGE) {
copy_loose_vert_hint(src_mesh, *dst_mesh);
}
else if (selection_domain == ATTR_DOMAIN_FACE) {
copy_loose_vert_hint(src_mesh, *dst_mesh);
copy_loose_edge_hint(src_mesh, *dst_mesh);
}
return dst_mesh;
}
std::optional<Mesh *> mesh_copy_selection_keep_verts(
const Mesh &src_mesh,
const fn::Field<bool> &selection_field,
const eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Span<int2> src_edges = src_mesh.edges();
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> src_corner_edges = src_mesh.corner_edges();
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
const bke::MeshFieldContext context(src_mesh, selection_domain);
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
if (selection.is_empty()) {
return std::nullopt;
}
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
IndexMask edge_mask;
IndexMask poly_mask;
switch (selection_domain) {
case ATTR_DOMAIN_POINT: {
const VArraySpan<bool> span(selection);
threading::parallel_invoke(
src_edges.size() > 1024,
[&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
[&]() {
poly_mask = poly_selection_from_vert(
src_polys, src_corner_verts, span, memory.local());
});
break;
}
case ATTR_DOMAIN_EDGE: {
const VArraySpan<bool> span(selection);
threading::parallel_invoke(
src_edges.size() > 1024,
[&]() { edge_mask = IndexMask::from_bools(span, memory.local()); },
[&]() {
poly_mask = poly_selection_from_edge(
src_polys, src_corner_edges, span, memory.local());
});
break;
}
case ATTR_DOMAIN_FACE: {
const VArraySpan<bool> span(selection);
poly_mask = IndexMask::from_bools(span, memory.local());
edge_mask = edge_selection_from_poly(
src_polys, poly_mask, src_corner_edges, src_edges.size(), memory.local());
break;
}
default:
BLI_assert_unreachable();
break;
}
Mesh *dst_mesh = create_mesh_no_attributes(
src_mesh, src_mesh.totvert, edge_mask.size(), poly_mask.size(), 0);
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
dst_mesh->totloop = dst_polys.total_size();
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
threading::parallel_invoke(
[&]() {
remap_edges(src_polys,
dst_polys,
src_edges.size(),
edge_mask,
poly_mask,
src_corner_edges,
dst_corner_edges);
},
[&]() {
bke::copy_attributes(
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
bke::gather_attributes(
src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_mask, dst_attributes);
bke::gather_attributes(
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
bke::gather_attributes_group_to_group(src_attributes,
ATTR_DOMAIN_CORNER,
propagation_info,
{".corner_edge"},
src_polys,
dst_polys,
poly_mask,
dst_attributes);
});
/* Positions are not changed by the operation, so the bounds are the same. */
dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
copy_loose_vert_hint(src_mesh, *dst_mesh);
return dst_mesh;
}
std::optional<Mesh *> mesh_copy_selection_keep_edges(
const Mesh &src_mesh,
const fn::Field<bool> &selection_field,
const eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const OffsetIndices src_polys = src_mesh.polys();
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
const bke::MeshFieldContext context(src_mesh, selection_domain);
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
if (selection.is_empty()) {
return std::nullopt;
}
IndexMaskMemory memory;
IndexMask poly_mask;
switch (selection_domain) {
case ATTR_DOMAIN_POINT:
poly_mask = poly_selection_from_vert(
src_polys, src_mesh.corner_verts(), VArraySpan(selection), memory);
break;
case ATTR_DOMAIN_EDGE:
poly_mask = poly_selection_from_edge(
src_polys, src_mesh.corner_edges(), VArraySpan(selection), memory);
break;
case ATTR_DOMAIN_FACE:
poly_mask = IndexMask::from_bools(selection, memory);
break;
default:
BLI_assert_unreachable();
break;
}
Mesh *dst_mesh = create_mesh_no_attributes(
src_mesh, src_mesh.totvert, src_mesh.totedge, poly_mask.size(), 0);
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
dst_mesh->totloop = dst_polys.total_size();
dst_attributes.add<int>(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes);
bke::gather_attributes(
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
bke::gather_attributes_group_to_group(src_attributes,
ATTR_DOMAIN_CORNER,
propagation_info,
{},
src_polys,
dst_polys,
poly_mask,
dst_attributes);
/* Positions are not changed by the operation, so the bounds are the same. */
dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
copy_loose_vert_hint(src_mesh, *dst_mesh);
return dst_mesh;
}
} // namespace blender::geometry

View File

@ -185,7 +185,7 @@ static bool can_rotate(const Span<PackIsland *> islands, const UVPackIsland_Para
return true;
}
/** Angle rounding helper for "D4" transforms. */
/** Angle rounding helper for "D4" transforms. */
static float angle_match(float angle_radians, float target_radians)
{
if (fabsf(angle_radians - target_radians) < DEG2RADF(0.1f)) {
@ -194,7 +194,7 @@ static float angle_match(float angle_radians, float target_radians)
return angle_radians;
}
/** Angle rounding helper for "D4" transforms. */
/** Angle rounding helper for "D4" transforms. */
static float plusminus_90_angle(float angle_radians)
{
angle_radians = angle_radians - floorf((angle_radians + M_PI_2) / M_PI) * M_PI;
@ -375,7 +375,7 @@ class UVAABBIsland {
*/
static void pack_islands_alpaca_turbo(const int64_t exclude_index,
const rctf &exclude,
const Span<UVAABBIsland *> islands,
const Span<std::unique_ptr<UVAABBIsland>> islands,
const float target_aspect_y,
MutableSpan<uv_phi> r_phis,
rctf *r_extent)
@ -390,9 +390,9 @@ static void pack_islands_alpaca_turbo(const int64_t exclude_index,
/* Visit every island in order, except the excluded islands at the start. */
for (int64_t index = exclude_index; index < islands.size(); index++) {
UVAABBIsland *island = islands[index];
const float dsm_u = island->uv_diagonal.x;
const float dsm_v = island->uv_diagonal.y;
UVAABBIsland &island = *islands[index];
const float dsm_u = island.uv_diagonal.x;
const float dsm_v = island.uv_diagonal.y;
bool restart = false;
if (zigzag) {
@ -409,7 +409,7 @@ static void pack_islands_alpaca_turbo(const int64_t exclude_index,
}
/* Place the island. */
uv_phi &phi = r_phis[island->index];
uv_phi &phi = r_phis[island.index];
phi.rotation = 0.0f;
phi.translation.x = u0 + dsm_u * 0.5f;
phi.translation.y = v0 + dsm_v * 0.5f;
@ -486,7 +486,7 @@ static void update_hole_rotate(float2 &hole,
*/
static void pack_islands_alpaca_rotate(const int64_t exclude_index,
const rctf &exclude,
const Span<UVAABBIsland *> islands,
const Span<std::unique_ptr<UVAABBIsland>> islands,
const float target_aspect_y,
MutableSpan<uv_phi> r_phis,
rctf *r_extent)
@ -506,30 +506,30 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
/* Visit every island in order, except the excluded islands at the start. */
for (int64_t index = exclude_index; index < islands.size(); index++) {
UVAABBIsland *island = islands[index];
uv_phi &phi = r_phis[island->index];
const float uvdiag_x = island->uv_diagonal.x * island->aspect_y;
float min_dsm = std::min(uvdiag_x, island->uv_diagonal.y);
float max_dsm = std::max(uvdiag_x, island->uv_diagonal.y);
UVAABBIsland &island = *islands[index];
uv_phi &phi = r_phis[island.index];
const float uvdiag_x = island.uv_diagonal.x * island.aspect_y;
float min_dsm = std::min(uvdiag_x, island.uv_diagonal.y);
float max_dsm = std::max(uvdiag_x, island.uv_diagonal.y);
if (min_dsm < hole_diagonal.x && max_dsm < hole_diagonal.y) {
/* Place island in the hole. */
if (hole_rotate == (min_dsm == island->uv_diagonal.x)) {
if (hole_rotate == (min_dsm == island.uv_diagonal.x)) {
phi.rotation = DEG2RADF(90.0f);
phi.translation.x = hole[0] + island->uv_diagonal.y * 0.5f / island->aspect_y;
phi.translation.y = hole[1] + island->uv_diagonal.x * 0.5f * island->aspect_y;
phi.translation.x = hole[0] + island.uv_diagonal.y * 0.5f / island.aspect_y;
phi.translation.y = hole[1] + island.uv_diagonal.x * 0.5f * island.aspect_y;
}
else {
phi.rotation = 0.0f;
phi.translation.x = hole[0] + island->uv_diagonal.x * 0.5f;
phi.translation.y = hole[1] + island->uv_diagonal.y * 0.5f;
phi.translation.x = hole[0] + island.uv_diagonal.x * 0.5f;
phi.translation.y = hole[1] + island.uv_diagonal.y * 0.5f;
}
/* Update space left in the hole. */
float p[6];
p[0] = hole[0];
p[1] = hole[1];
p[2] = hole[0] + (hole_rotate ? max_dsm : min_dsm) / island->aspect_y;
p[2] = hole[0] + (hole_rotate ? max_dsm : min_dsm) / island.aspect_y;
p[3] = hole[1] + (hole_rotate ? min_dsm : max_dsm);
p[4] = hole[0] + (hole_rotate ? hole_diagonal.y : hole_diagonal.x);
p[5] = hole[1] + (hole_rotate ? hole_diagonal.x : hole_diagonal.y);
@ -546,7 +546,7 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
restart = (next_v1 < v0 + min_dsm);
}
else {
restart = (next_u1 < u0 + min_dsm / island->aspect_y);
restart = (next_u1 < u0 + min_dsm / island.aspect_y);
}
if (restart) {
update_hole_rotate(hole, hole_diagonal, hole_rotate, u0, v0, next_u1, next_v1);
@ -559,25 +559,25 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
/* Place the island. */
if (zigzag == (min_dsm == uvdiag_x)) {
phi.rotation = DEG2RADF(90.0f);
phi.translation.x = u0 + island->uv_diagonal.y * 0.5f / island->aspect_y;
phi.translation.y = v0 + island->uv_diagonal.x * 0.5f * island->aspect_y;
phi.translation.x = u0 + island.uv_diagonal.y * 0.5f / island.aspect_y;
phi.translation.y = v0 + island.uv_diagonal.x * 0.5f * island.aspect_y;
}
else {
phi.rotation = 0.0f;
phi.translation.x = u0 + island->uv_diagonal.x * 0.5f;
phi.translation.y = v0 + island->uv_diagonal.y * 0.5f;
phi.translation.x = u0 + island.uv_diagonal.x * 0.5f;
phi.translation.y = v0 + island.uv_diagonal.y * 0.5f;
}
/* Move according to the "Alpaca rules", with rotation. */
if (zigzag) {
/* Move upwards. */
v0 += min_dsm;
next_u1 = max_ff(next_u1, u0 + max_dsm / island->aspect_y);
next_u1 = max_ff(next_u1, u0 + max_dsm / island.aspect_y);
next_v1 = max_ff(next_v1, v0);
}
else {
/* Move sideways. */
u0 += min_dsm / island->aspect_y;
u0 += min_dsm / island.aspect_y;
next_v1 = max_ff(next_v1, v0 + max_dsm);
next_u1 = max_ff(next_u1, u0);
}
@ -592,7 +592,7 @@ static void pack_islands_alpaca_rotate(const int64_t exclude_index,
*/
static void pack_islands_fast(const int64_t exclude_index,
const rctf &exclude,
const Span<UVAABBIsland *> aabbs,
const Span<std::unique_ptr<UVAABBIsland>> aabbs,
const bool rotate,
const float target_aspect_y,
MutableSpan<uv_phi> r_phis,
@ -607,7 +607,7 @@ static void pack_islands_fast(const int64_t exclude_index,
}
/** Frits Göbel, 1979. */
static void pack_gobel(const Span<UVAABBIsland *> aabbs,
static void pack_gobel(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
const float scale,
const int m,
MutableSpan<uv_phi> r_phis)
@ -643,7 +643,7 @@ static void pack_gobel(const Span<UVAABBIsland *> aabbs,
}
}
/* Attempt to find an "Optimal" packing of the islands, e.g. assuming squares or circles. */
static void pack_islands_optimal_pack(const Span<UVAABBIsland *> aabbs,
static void pack_islands_optimal_pack(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
const UVPackIsland_Params &params,
MutableSpan<uv_phi> r_phis,
rctf *r_extent)
@ -694,7 +694,7 @@ static void pack_islands_optimal_pack(const Span<UVAABBIsland *> aabbs,
}
/* Wrapper around #BLI_box_pack_2d. */
static void pack_island_box_pack_2d(const Span<UVAABBIsland *> aabbs,
static void pack_island_box_pack_2d(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
const UVPackIsland_Params &params,
MutableSpan<uv_phi> r_phis,
rctf *r_extent)
@ -1093,7 +1093,7 @@ class UVMinimumEnclosingSquareFinder {
* If that square is smaller than `r_extent`, then update `r_phis` accordingly.
* \return True if `r_phis` and `r_extent` are modified.
*/
static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
static bool rotate_inside_square(const Span<std::unique_ptr<UVAABBIsland>> island_indices,
const Span<PackIsland *> islands,
const UVPackIsland_Params &params,
const float scale,
@ -1109,7 +1109,7 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
}
if (params.shape_method == ED_UVPACK_SHAPE_AABB) {
/* AABB margin calculations are not preserved under rotations. */
if (island_indices.size() > 1) { /* Unless there's only one island...*/
if (island_indices.size() > 1) { /* Unless there's only one island. */
if (params.target_aspect_y != 1.0f) {
/* TODO: Check for possible 90 degree rotation. */
@ -1146,10 +1146,10 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
/* Now we have all the points in the correct space, compute the 2D convex hull. */
const float(*source)[2] = reinterpret_cast<const float(*)[2]>(square_finder.points.data());
square_finder.indices.resize(square_finder.points.size()); /* Allocate worst-case.*/
square_finder.indices.resize(square_finder.points.size()); /* Allocate worst-case. */
int convex_size = BLI_convexhull_2d(
source, int(square_finder.points.size()), square_finder.indices.data());
square_finder.indices.resize(convex_size); /* Resize to actual size.*/
square_finder.indices.resize(convex_size); /* Resize to actual size. */
/* Run the computation to find the best angle. (Slow!) */
const float quad_180 = square_finder.update(DEG2RADF(-180.0f));
@ -1194,7 +1194,7 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> island_indices,
* Performance would normally be `O(n^4)`, however the occupancy
* bitmap_radix is fixed, which gives a reduced time complexity of `O(n^3)`.
*/
static int64_t pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
static int64_t pack_island_xatlas(const Span<std::unique_ptr<UVAABBIsland>> island_indices,
const Span<PackIsland *> islands,
const float scale,
const float margin,
@ -1402,69 +1402,69 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
const bool all_can_rotate = can_rotate(islands, params);
/* First, copy information from our input into the AABB structure. */
Array<UVAABBIsland *> aabbs(islands.size());
Array<std::unique_ptr<UVAABBIsland>> aabbs(islands.size());
for (const int64_t i : islands.index_range()) {
PackIsland *pack_island = islands[i];
float island_scale = scale;
if (!pack_island->can_scale_(params)) {
island_scale = 1.0f;
}
UVAABBIsland *aabb = new UVAABBIsland();
std::unique_ptr<UVAABBIsland> aabb = std::make_unique<UVAABBIsland>();
aabb->index = i;
aabb->uv_diagonal.x = pack_island->half_diagonal_.x * 2 * island_scale + 2 * margin;
aabb->uv_diagonal.y = pack_island->half_diagonal_.y * 2 * island_scale + 2 * margin;
aabb->aspect_y = pack_island->aspect_y;
aabbs[i] = aabb;
aabbs[i] = std::move(aabb);
}
/* Sort from "biggest" to "smallest". */
if (all_can_rotate) {
std::stable_sort(aabbs.begin(),
aabbs.end(),
[&params, &islands](const UVAABBIsland *a, const UVAABBIsland *b) {
const bool can_translate_a = islands[a->index]->can_translate_(params);
const bool can_translate_b = islands[b->index]->can_translate_(params);
if (can_translate_a != can_translate_b) {
return can_translate_b; /* Locked islands are placed first. */
}
/* TODO: Fix when (params.target_aspect_y != 1.0f) */
std::stable_sort(
aabbs.begin(),
aabbs.end(),
[&](const std::unique_ptr<UVAABBIsland> &a, const std::unique_ptr<UVAABBIsland> &b) {
const bool can_translate_a = islands[a->index]->can_translate_(params);
const bool can_translate_b = islands[b->index]->can_translate_(params);
if (can_translate_a != can_translate_b) {
return can_translate_b; /* Locked islands are placed first. */
}
/* TODO: Fix when (params.target_aspect_y != 1.0f) */
/* Choose the AABB with the longest large edge. */
float a_u = a->uv_diagonal.x * a->aspect_y;
float a_v = a->uv_diagonal.y;
float b_u = b->uv_diagonal.x * b->aspect_y;
float b_v = b->uv_diagonal.y;
if (a_u > a_v) {
std::swap(a_u, a_v);
}
if (b_u > b_v) {
std::swap(b_u, b_v);
}
float diff_u = a_u - b_u;
float diff_v = a_v - b_v;
diff_v += diff_u * 0.05f; /* Robust sort, smooth over round-off errors. */
if (diff_v == 0.0f) { /* Tie break. */
return diff_u > 0.0f;
}
return diff_v > 0.0f;
});
/* Choose the AABB with the longest large edge. */
float a_u = a->uv_diagonal.x * a->aspect_y;
float a_v = a->uv_diagonal.y;
float b_u = b->uv_diagonal.x * b->aspect_y;
float b_v = b->uv_diagonal.y;
if (a_u > a_v) {
std::swap(a_u, a_v);
}
if (b_u > b_v) {
std::swap(b_u, b_v);
}
float diff_u = a_u - b_u;
float diff_v = a_v - b_v;
diff_v += diff_u * 0.05f; /* Robust sort, smooth over round-off errors. */
if (diff_v == 0.0f) { /* Tie break. */
return diff_u > 0.0f;
}
return diff_v > 0.0f;
});
}
else {
std::stable_sort(
aabbs.begin(),
aabbs.end(),
[&](const std::unique_ptr<UVAABBIsland> &a, const std::unique_ptr<UVAABBIsland> &b) {
const bool can_translate_a = islands[a->index]->can_translate_(params);
const bool can_translate_b = islands[b->index]->can_translate_(params);
if (can_translate_a != can_translate_b) {
return can_translate_b; /* Locked islands are placed first. */
}
std::stable_sort(aabbs.begin(),
aabbs.end(),
[&params, &islands](const UVAABBIsland *a, const UVAABBIsland *b) {
const bool can_translate_a = islands[a->index]->can_translate_(params);
const bool can_translate_b = islands[b->index]->can_translate_(params);
if (can_translate_a != can_translate_b) {
return can_translate_b; /* Locked islands are placed first. */
}
/* Choose the AABB with larger rectangular area. */
return b->uv_diagonal.x * b->uv_diagonal.y <
a->uv_diagonal.x * a->uv_diagonal.y;
});
/* Choose the AABB with larger rectangular area. */
return b->uv_diagonal.x * b->uv_diagonal.y < a->uv_diagonal.x * a->uv_diagonal.y;
});
}
/* If some of the islands are locked, we build a summary about them here. */
@ -1499,7 +1499,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
alpaca_cutoff = std::max(alpaca_cutoff, locked_island_count); /* ...TODO... */
Span<UVAABBIsland *> slow_aabbs = aabbs.as_span().take_front(
Span<std::unique_ptr<UVAABBIsland>> slow_aabbs = aabbs.as_span().take_front(
std::min(alpaca_cutoff, islands.size()));
rctf extent = {0.0f, 1e30f, 0.0f, 1e30f};
@ -1546,13 +1546,6 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
r_phis,
&final_extent);
/* Housekeeping. */
for (const int64_t i : aabbs.index_range()) {
UVAABBIsland *aabb = aabbs[i];
aabbs[i] = nullptr;
delete aabb;
}
return get_aspect_scaled_extent(final_extent, params);
}

View File

@ -1070,8 +1070,8 @@ id<MTLBuffer> MTLBatch::get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_typ
case GPU_PRIM_LINE_LOOP: {
int line = 0;
for (line = 0; line < output_prim_count - 1; line++) {
data[line * 3 + 0] = line + 0;
data[line * 3 + 1] = line + 1;
data[line * 2 + 0] = line + 0;
data[line * 2 + 1] = line + 1;
}
/* Closing line. */
data[line * 2 + 0] = line + 0;

View File

@ -1265,7 +1265,7 @@ bool MTLContext::ensure_buffer_bindings(
const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
/* Explicit lookup location for SSBO in bind table.*/
/* Explicit lookup location for SSBO in bind table. */
const uint32_t ssbo_location = ssbo.location;
/* buffer(N) index of where to bind the SSBO. */
const uint32_t buffer_index = ssbo.buffer_index;
@ -1444,7 +1444,7 @@ bool MTLContext::ensure_buffer_bindings(
const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
/* Explicit lookup location for UBO in bind table.*/
/* Explicit lookup location for UBO in bind table. */
const uint32_t ssbo_location = ssbo.location;
/* buffer(N) index of where to bind the UBO. */
const uint32_t buffer_index = ssbo.buffer_index;

View File

@ -11,7 +11,7 @@
#include "BKE_global.h"
#include "CLG_log.h"
/** Options for organising Metal GPU debug captures. */
/** Options for organizing Metal GPU debug captures. */
/* Maximum nested debug group depth. Groups beyond this will still have the pass name pulled into
* the RenderCommandEncoder, but will not display in the trace.
* Use -1 for unlimited. */

View File

@ -5,6 +5,7 @@
#pragma once
#include <atomic>
#include <ctime>
#include <functional>
#include <map>
#include <mutex>
@ -233,17 +234,20 @@ class MTLCircularBuffer {
struct MTLBufferHandle {
gpu::MTLBuffer *buffer;
uint64_t buffer_size;
time_t insert_time;
inline MTLBufferHandle(gpu::MTLBuffer *buf)
{
this->buffer = buf;
this->buffer_size = this->buffer->get_size();
this->insert_time = std::time(nullptr);
}
inline MTLBufferHandle(uint64_t compare_size)
{
this->buffer = nullptr;
this->buffer_size = compare_size;
this->insert_time = 0;
}
};
@ -356,7 +360,6 @@ class MTLBufferPool {
/* Debug statistics. */
std::atomic<int> per_frame_allocation_count_;
std::atomic<int64_t> allocations_in_pool_;
std::atomic<int64_t> buffers_in_pool_;
#endif
@ -401,6 +404,7 @@ class MTLBufferPool {
/* MTLBuffer::free() can be called from separate threads, due to usage within animation
* system/worker threads. */
std::atomic<MTLSafeFreeList *> current_free_list_;
std::atomic<int64_t> allocations_in_pool_;
public:
void init(id<MTLDevice> device);

View File

@ -11,6 +11,12 @@
using namespace blender;
using namespace blender::gpu;
/* Memory size in bytes macros, used as pool flushing frequency thresholds. */
#define MEMORY_SIZE_2GB 2147483648LL
#define MEMORY_SIZE_1GB 1073741824LL
#define MEMORY_SIZE_512MB 536870912LL
#define MEMORY_SIZE_256MB 268435456LL
namespace blender::gpu {
/* -------------------------------------------------------------------- */
@ -28,9 +34,10 @@ void MTLBufferPool::init(id<MTLDevice> mtl_device)
/* Debug statistics. */
total_allocation_bytes_ = 0;
per_frame_allocation_count_ = 0;
allocations_in_pool_ = 0;
buffers_in_pool_ = 0;
#endif
/* Track pool allocation size. */
allocations_in_pool_ = 0;
/* Free pools -- Create initial safe free pool */
BLI_assert(current_free_list_ == nullptr);
@ -159,11 +166,13 @@ gpu::MTLBuffer *MTLBufferPool::allocate_aligned(uint64_t size,
#if MTL_DEBUG_MEMORY_STATISTICS == 1
/* Debug. */
allocations_in_pool_ -= new_buffer->get_size();
buffers_in_pool_--;
BLI_assert(allocations_in_pool_ >= 0);
#endif
/* Decrement size of pool. */
BLI_assert(allocations_in_pool_ >= 0);
allocations_in_pool_ -= new_buffer->get_size();
/* Ensure buffer memory is correctly backed. */
BLI_assert(new_buffer->get_metal_buffer());
}
@ -275,6 +284,59 @@ void MTLBufferPool::update_memory_pools()
}
}
/* Release memory allocations which have not been used in a while.
* This ensures memory pressure stays low for scenes with compounding complexity during
* animation.
* If memory is continually used, then we do not want to free this memory as it will be
* re-allocated during a short time period. */
const time_t time_now = std::time(nullptr);
for (auto buffer_pool_list : buffer_pools_.items()) {
MTLBufferPoolOrderedList *pool_allocations = buffer_pool_list.value;
MTLBufferPoolOrderedList::iterator pool_iterator = pool_allocations->begin();
while (pool_iterator != pool_allocations->end()) {
const MTLBufferHandle handle = *pool_iterator;
const time_t time_passed = time_now - handle.insert_time;
/* Free allocations if a certain amount of time has passed.
* Deletion frequency depends on how much excess memory
* the application is using. */
time_t deletion_time_threshold_s = 600;
/* Spare pool memory >= 2GB. */
if (allocations_in_pool_ >= MEMORY_SIZE_2GB) {
deletion_time_threshold_s = 2;
}
else
/* Spare pool memory >= 1GB. */
if (allocations_in_pool_ >= MEMORY_SIZE_1GB)
{
deletion_time_threshold_s = 4;
}
/* Spare pool memory >= 512MB.*/
else if (allocations_in_pool_ >= MEMORY_SIZE_512MB) {
deletion_time_threshold_s = 15;
}
/* Spare pool memory >= 256MB. */
else if (allocations_in_pool_ >= MEMORY_SIZE_256MB) {
deletion_time_threshold_s = 60;
}
if (time_passed > deletion_time_threshold_s) {
/* Delete allocation. */
delete handle.buffer;
pool_iterator = pool_allocations->erase(pool_iterator);
allocations_in_pool_ -= handle.buffer_size;
#if MTL_DEBUG_MEMORY_STATISTICS == 1
total_allocation_bytes_ -= handle.buffer_size;
buffers_in_pool_--;
#endif
continue;
}
pool_iterator++;
}
}
#if MTL_DEBUG_MEMORY_STATISTICS == 1
printf("--- Allocation Stats ---\n");
printf(" Num buffers processed in pool (this frame): %u\n", num_buffers_added);
@ -383,10 +445,10 @@ void MTLBufferPool::insert_buffer_into_pool(MTLResourceOptions options, gpu::MTL
std::multiset<MTLBufferHandle, CompareMTLBuffer> *pool = buffer_pools_.lookup(options);
pool->insert(MTLBufferHandle(buffer));
allocations_in_pool_ += buffer->get_size();
#if MTL_DEBUG_MEMORY_STATISTICS == 1
/* Debug statistics. */
allocations_in_pool_ += buffer->get_size();
buffers_in_pool_++;
#endif
}

View File

@ -249,7 +249,7 @@ struct MSLTextureResource {
MSLTextureSamplerAccess access;
/* Whether resource is a texture sampler or an image. */
bool is_texture_sampler;
/* Index in shader bind table [[texture(N)]].*/
/* Index in shader bind table `[[texture(N)]]`. */
uint slot;
/* Explicit bind index provided by ShaderCreateInfo. */
uint location;

View File

@ -602,7 +602,9 @@ inline MTLTextureUsage mtl_usage_from_gpu(eGPUTextureUsage usage)
if (usage == GPU_TEXTURE_USAGE_GENERAL) {
return MTLTextureUsageUnknown;
}
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
/* Host read implies general read support, as the compute-based host read routine requires
* reading of texture data. */
if (usage & GPU_TEXTURE_USAGE_SHADER_READ || usage & GPU_TEXTURE_USAGE_HOST_READ) {
mtl_usage = mtl_usage | MTLTextureUsageShaderRead;
}
if (usage & GPU_TEXTURE_USAGE_SHADER_WRITE) {

View File

@ -19,7 +19,7 @@ namespace blender::gpu {
void VKBatch::draw_setup()
{
/* Currently the pipeline is rebuild on each draw command. Clearing the dirty flag for
* consistency with the internals of GPU module. */
* consistency with the internals of GPU module. */
flag &= ~GPU_BATCH_DIRTY;
/* Finalize graphics pipeline */

View File

@ -42,7 +42,7 @@ void VKCommandBuffer::init(const VkDevice vk_device,
/* When a the last GHOST context is destroyed the device is deallocate. A moment later the GPU
* context is destroyed. The first step is to activate it. Activating would retrieve the device
* from GHOST which in that case is a VK_NULL_HANDLE.*/
* from GHOST which in that case is a #VK_NULL_HANDLE. */
if (vk_device == VK_NULL_HANDLE) {
return;
}

View File

@ -6,6 +6,8 @@
* \ingroup gpu
*/
#include "BLI_utildefines.h"
#include "vk_common.hh"
namespace blender::gpu {
@ -750,4 +752,109 @@ VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test)
return VK_CULL_MODE_NONE;
}
const char *to_string(VkObjectType type)
{
switch (type) {
case VK_OBJECT_TYPE_UNKNOWN:
return STRINGIFY_ARG(VK_OBJECT_TYPE_UNKNOWN);
case VK_OBJECT_TYPE_INSTANCE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_INSTANCE);
case VK_OBJECT_TYPE_PHYSICAL_DEVICE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_PHYSICAL_DEVICE);
case VK_OBJECT_TYPE_DEVICE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEVICE);
case VK_OBJECT_TYPE_QUEUE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_QUEUE);
case VK_OBJECT_TYPE_SEMAPHORE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_SEMAPHORE);
case VK_OBJECT_TYPE_COMMAND_BUFFER:
return STRINGIFY_ARG(VK_OBJECT_TYPE_COMMAND_BUFFER);
case VK_OBJECT_TYPE_FENCE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_FENCE);
case VK_OBJECT_TYPE_DEVICE_MEMORY:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEVICE_MEMORY);
case VK_OBJECT_TYPE_BUFFER:
return STRINGIFY_ARG(VK_OBJECT_TYPE_BUFFER);
case VK_OBJECT_TYPE_IMAGE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_IMAGE);
case VK_OBJECT_TYPE_EVENT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_EVENT);
case VK_OBJECT_TYPE_QUERY_POOL:
return STRINGIFY_ARG(VK_OBJECT_TYPE_QUERY_POOL);
case VK_OBJECT_TYPE_BUFFER_VIEW:
return STRINGIFY_ARG(VK_OBJECT_TYPE_BUFFER_VIEW);
case VK_OBJECT_TYPE_IMAGE_VIEW:
return STRINGIFY_ARG(VK_OBJECT_TYPE_IMAGE_VIEW);
case VK_OBJECT_TYPE_SHADER_MODULE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_SHADER_MODULE);
case VK_OBJECT_TYPE_PIPELINE_CACHE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_PIPELINE_CACHE);
case VK_OBJECT_TYPE_PIPELINE_LAYOUT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_PIPELINE_LAYOUT);
case VK_OBJECT_TYPE_RENDER_PASS:
return STRINGIFY_ARG(VK_OBJECT_TYPE_RENDER_PASS);
case VK_OBJECT_TYPE_PIPELINE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_PIPELINE);
case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT);
case VK_OBJECT_TYPE_SAMPLER:
return STRINGIFY_ARG(VK_OBJECT_TYPE_SAMPLER);
case VK_OBJECT_TYPE_DESCRIPTOR_POOL:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_POOL);
case VK_OBJECT_TYPE_DESCRIPTOR_SET:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_SET);
case VK_OBJECT_TYPE_FRAMEBUFFER:
return STRINGIFY_ARG(VK_OBJECT_TYPE_FRAMEBUFFER);
case VK_OBJECT_TYPE_COMMAND_POOL:
return STRINGIFY_ARG(VK_OBJECT_TYPE_COMMAND_POOL);
case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:
return STRINGIFY_ARG(VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION);
case VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE);
case VK_OBJECT_TYPE_SURFACE_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_SURFACE_KHR);
case VK_OBJECT_TYPE_SWAPCHAIN_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_SWAPCHAIN_KHR);
case VK_OBJECT_TYPE_DISPLAY_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DISPLAY_KHR);
case VK_OBJECT_TYPE_DISPLAY_MODE_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DISPLAY_MODE_KHR);
case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT);
#ifdef VK_ENABLE_BETA_EXTENSIONS
case VK_OBJECT_TYPE_VIDEO_SESSION_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_VIDEO_SESSION_KHR);
#endif
#ifdef VK_ENABLE_BETA_EXTENSIONS
case VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR);
#endif
case VK_OBJECT_TYPE_CU_MODULE_NVX:
return STRINGIFY_ARG(VK_OBJECT_TYPE_CU_MODULE_NVX);
case VK_OBJECT_TYPE_CU_FUNCTION_NVX:
return STRINGIFY_ARG(VK_OBJECT_TYPE_CU_FUNCTION_NVX);
case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT);
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR);
case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_VALIDATION_CACHE_EXT);
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV:
return STRINGIFY_ARG(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV);
case VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL:
return STRINGIFY_ARG(VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL);
case VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR:
return STRINGIFY_ARG(VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR);
case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV:
return STRINGIFY_ARG(VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV);
case VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT:
return STRINGIFY_ARG(VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT);
case VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA:
return STRINGIFY_ARG(VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA);
default:
BLI_assert_unreachable();
}
return "NotFound";
};
} // namespace blender::gpu

View File

@ -35,6 +35,7 @@ VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const voi
VkIndexType to_vk_index_type(const GPUIndexBufType index_type);
VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type);
VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test);
const char *to_string(VkObjectType type);
template<typename T> VkObjectType to_vk_object_type(T /*vk_obj*/)
{

View File

@ -1,208 +1,445 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include "BKE_global.h"
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_debug.hh"
namespace blender::gpu {
void VKContext::debug_group_begin(const char *name, int)
{
const VKDevice &device = VKBackend::get().device_get();
debug::push_marker(device, name);
}
void VKContext::debug_group_end()
{
const VKDevice &device = VKBackend::get().device_get();
debug::pop_marker(device);
}
bool VKContext::debug_capture_begin()
{
return VKBackend::get().debug_capture_begin();
}
bool VKBackend::debug_capture_begin()
{
#ifdef WITH_RENDERDOC
return renderdoc_api_.start_frame_capture(device_get().instance_get(), nullptr);
#else
return false;
#endif
}
void VKContext::debug_capture_end()
{
VKBackend::get().debug_capture_end();
}
void VKBackend::debug_capture_end()
{
#ifdef WITH_RENDERDOC
renderdoc_api_.end_frame_capture(device_get().instance_get(), nullptr);
#endif
}
void *VKContext::debug_capture_scope_create(const char * /*name*/)
{
return nullptr;
}
bool VKContext::debug_capture_scope_begin(void * /*scope*/)
{
return false;
}
void VKContext::debug_capture_scope_end(void * /*scope*/) {}
} // namespace blender::gpu
namespace blender::gpu::debug {
void VKDebuggingTools::init(VkInstance vk_instance)
{
PFN_vkGetInstanceProcAddr instance_proc_addr = vkGetInstanceProcAddr;
enabled = false;
vkCmdBeginDebugUtilsLabelEXT_r = (PFN_vkCmdBeginDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkCmdBeginDebugUtilsLabelEXT");
vkCmdEndDebugUtilsLabelEXT_r = (PFN_vkCmdEndDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkCmdEndDebugUtilsLabelEXT");
vkCmdInsertDebugUtilsLabelEXT_r = (PFN_vkCmdInsertDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkCmdInsertDebugUtilsLabelEXT");
vkCreateDebugUtilsMessengerEXT_r = (PFN_vkCreateDebugUtilsMessengerEXT)instance_proc_addr(
vk_instance, "vkCreateDebugUtilsMessengerEXT");
vkDestroyDebugUtilsMessengerEXT_r = (PFN_vkDestroyDebugUtilsMessengerEXT)instance_proc_addr(
vk_instance, "vkDestroyDebugUtilsMessengerEXT");
vkQueueBeginDebugUtilsLabelEXT_r = (PFN_vkQueueBeginDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkQueueBeginDebugUtilsLabelEXT");
vkQueueEndDebugUtilsLabelEXT_r = (PFN_vkQueueEndDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkQueueEndDebugUtilsLabelEXT");
vkQueueInsertDebugUtilsLabelEXT_r = (PFN_vkQueueInsertDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkQueueInsertDebugUtilsLabelEXT");
vkSetDebugUtilsObjectNameEXT_r = (PFN_vkSetDebugUtilsObjectNameEXT)instance_proc_addr(
vk_instance, "vkSetDebugUtilsObjectNameEXT");
vkSetDebugUtilsObjectTagEXT_r = (PFN_vkSetDebugUtilsObjectTagEXT)instance_proc_addr(
vk_instance, "vkSetDebugUtilsObjectTagEXT");
vkSubmitDebugUtilsMessageEXT_r = (PFN_vkSubmitDebugUtilsMessageEXT)instance_proc_addr(
vk_instance, "vkSubmitDebugUtilsMessageEXT");
if (vkCmdBeginDebugUtilsLabelEXT_r) {
enabled = true;
}
}
void VKDebuggingTools::deinit()
{
vkCmdBeginDebugUtilsLabelEXT_r = nullptr;
vkCmdEndDebugUtilsLabelEXT_r = nullptr;
vkCmdInsertDebugUtilsLabelEXT_r = nullptr;
vkCreateDebugUtilsMessengerEXT_r = nullptr;
vkDestroyDebugUtilsMessengerEXT_r = nullptr;
vkQueueBeginDebugUtilsLabelEXT_r = nullptr;
vkQueueEndDebugUtilsLabelEXT_r = nullptr;
vkQueueInsertDebugUtilsLabelEXT_r = nullptr;
vkSetDebugUtilsObjectNameEXT_r = nullptr;
vkSetDebugUtilsObjectTagEXT_r = nullptr;
vkSubmitDebugUtilsMessageEXT_r = nullptr;
enabled = false;
}
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
const VKDevice &device = VKBackend::get().device_get();
VkDebugUtilsObjectNameInfoEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
info.objectType = vk_object_type;
info.objectHandle = object_handle;
info.pObjectName = name;
debugging_tools.vkSetDebugUtilsObjectNameEXT_r(device.device_get(), &info);
}
}
}
void push_marker(VkCommandBuffer vk_command_buffer, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkCmdBeginDebugUtilsLabelEXT_r(vk_command_buffer, &info);
}
}
}
void set_marker(VkCommandBuffer vk_command_buffer, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkCmdInsertDebugUtilsLabelEXT_r(vk_command_buffer, &info);
}
}
}
void pop_marker(VkCommandBuffer vk_command_buffer)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
debugging_tools.vkCmdEndDebugUtilsLabelEXT_r(vk_command_buffer);
}
}
}
void push_marker(const VKDevice &device, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkQueueBeginDebugUtilsLabelEXT_r(device.queue_get(), &info);
}
}
}
void set_marker(const VKDevice &device, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkQueueInsertDebugUtilsLabelEXT_r(device.queue_get(), &info);
}
}
}
void pop_marker(const VKDevice &device)
{
if (G.debug & G_DEBUG_GPU) {
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
debugging_tools.vkQueueEndDebugUtilsLabelEXT_r(device.queue_get());
}
}
}
} // namespace blender::gpu::debug
/* SPDX-FileCopyrightText: 2023 Blender Foundation.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include "BKE_global.h"
#include "BLI_dynstr.h"
#include "CLG_log.h"
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_debug.hh"
static CLG_LogRef LOG = {"gpu.debug.vulkan"};
namespace blender::gpu {
void VKContext::debug_group_begin(const char *name, int)
{
const VKDevice &device = VKBackend::get().device_get();
debug::push_marker(device, name);
}
void VKContext::debug_group_end()
{
const VKDevice &device = VKBackend::get().device_get();
debug::pop_marker(device);
}
bool VKContext::debug_capture_begin()
{
return VKBackend::get().debug_capture_begin();
}
bool VKBackend::debug_capture_begin()
{
#ifdef WITH_RENDERDOC
return renderdoc_api_.start_frame_capture(device_get().instance_get(), nullptr);
#else
return false;
#endif
}
void VKContext::debug_capture_end()
{
VKBackend::get().debug_capture_end();
}
void VKBackend::debug_capture_end()
{
#ifdef WITH_RENDERDOC
renderdoc_api_.end_frame_capture(device_get().instance_get(), nullptr);
#endif
}
void *VKContext::debug_capture_scope_create(const char * /*name*/)
{
return nullptr;
}
bool VKContext::debug_capture_scope_begin(void * /*scope*/)
{
return false;
}
void VKContext::debug_capture_scope_end(void * /*scope*/) {}
} // namespace blender::gpu
namespace blender::gpu::debug {
void VKDebuggingTools::init(VkInstance vk_instance)
{
PFN_vkGetInstanceProcAddr instance_proc_addr = vkGetInstanceProcAddr;
enabled = false;
vk_debug_utils_messenger = nullptr;
vkCmdBeginDebugUtilsLabelEXT_r = (PFN_vkCmdBeginDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkCmdBeginDebugUtilsLabelEXT");
vkCmdEndDebugUtilsLabelEXT_r = (PFN_vkCmdEndDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkCmdEndDebugUtilsLabelEXT");
vkCmdInsertDebugUtilsLabelEXT_r = (PFN_vkCmdInsertDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkCmdInsertDebugUtilsLabelEXT");
vkCreateDebugUtilsMessengerEXT_r = (PFN_vkCreateDebugUtilsMessengerEXT)instance_proc_addr(
vk_instance, "vkCreateDebugUtilsMessengerEXT");
vkDestroyDebugUtilsMessengerEXT_r = (PFN_vkDestroyDebugUtilsMessengerEXT)instance_proc_addr(
vk_instance, "vkDestroyDebugUtilsMessengerEXT");
vkQueueBeginDebugUtilsLabelEXT_r = (PFN_vkQueueBeginDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkQueueBeginDebugUtilsLabelEXT");
vkQueueEndDebugUtilsLabelEXT_r = (PFN_vkQueueEndDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkQueueEndDebugUtilsLabelEXT");
vkQueueInsertDebugUtilsLabelEXT_r = (PFN_vkQueueInsertDebugUtilsLabelEXT)instance_proc_addr(
vk_instance, "vkQueueInsertDebugUtilsLabelEXT");
vkSetDebugUtilsObjectNameEXT_r = (PFN_vkSetDebugUtilsObjectNameEXT)instance_proc_addr(
vk_instance, "vkSetDebugUtilsObjectNameEXT");
vkSetDebugUtilsObjectTagEXT_r = (PFN_vkSetDebugUtilsObjectTagEXT)instance_proc_addr(
vk_instance, "vkSetDebugUtilsObjectTagEXT");
vkSubmitDebugUtilsMessageEXT_r = (PFN_vkSubmitDebugUtilsMessageEXT)instance_proc_addr(
vk_instance, "vkSubmitDebugUtilsMessageEXT");
if (vkCmdBeginDebugUtilsLabelEXT_r) {
enabled = true;
init_messenger(vk_instance);
}
}
void VKDebuggingTools::deinit(VkInstance vk_instance)
{
if (enabled) {
destroy_messenger(vk_instance);
}
vkCmdBeginDebugUtilsLabelEXT_r = nullptr;
vkCmdEndDebugUtilsLabelEXT_r = nullptr;
vkCmdInsertDebugUtilsLabelEXT_r = nullptr;
vkCreateDebugUtilsMessengerEXT_r = nullptr;
vkDestroyDebugUtilsMessengerEXT_r = nullptr;
vkQueueBeginDebugUtilsLabelEXT_r = nullptr;
vkQueueEndDebugUtilsLabelEXT_r = nullptr;
vkQueueInsertDebugUtilsLabelEXT_r = nullptr;
vkSetDebugUtilsObjectNameEXT_r = nullptr;
vkSetDebugUtilsObjectTagEXT_r = nullptr;
vkSubmitDebugUtilsMessageEXT_r = nullptr;
enabled = false;
}
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
const VKDevice &device = VKBackend::get().device_get();
VkDebugUtilsObjectNameInfoEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
info.objectType = vk_object_type;
info.objectHandle = object_handle;
info.pObjectName = name;
debugging_tools.vkSetDebugUtilsObjectNameEXT_r(device.device_get(), &info);
}
}
}
void push_marker(VkCommandBuffer vk_command_buffer, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkCmdBeginDebugUtilsLabelEXT_r(vk_command_buffer, &info);
}
}
}
void set_marker(VkCommandBuffer vk_command_buffer, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkCmdInsertDebugUtilsLabelEXT_r(vk_command_buffer, &info);
}
}
}
void pop_marker(VkCommandBuffer vk_command_buffer)
{
if (G.debug & G_DEBUG_GPU) {
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
debugging_tools.vkCmdEndDebugUtilsLabelEXT_r(vk_command_buffer);
}
}
}
void push_marker(const VKDevice &device, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkQueueBeginDebugUtilsLabelEXT_r(device.queue_get(), &info);
}
}
}
void set_marker(const VKDevice &device, const char *name)
{
if (G.debug & G_DEBUG_GPU) {
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
VkDebugUtilsLabelEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
info.pLabelName = name;
debugging_tools.vkQueueInsertDebugUtilsLabelEXT_r(device.queue_get(), &info);
}
}
}
void pop_marker(const VKDevice &device)
{
if (G.debug & G_DEBUG_GPU) {
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
debugging_tools.vkQueueEndDebugUtilsLabelEXT_r(device.queue_get());
}
}
}
} // namespace blender::gpu::debug
namespace blender::gpu::debug {
VKDebuggingTools::~VKDebuggingTools()
{
BLI_assert(vk_debug_utils_messenger == nullptr);
};
void VKDebuggingTools::print_vulkan_version()
{
uint32_t instanceVersion = VK_API_VERSION_1_0;
vkEnumerateInstanceVersion(&instanceVersion);
uint32_t major = VK_VERSION_MAJOR(instanceVersion);
uint32_t minor = VK_VERSION_MINOR(instanceVersion);
uint32_t patch = VK_VERSION_PATCH(instanceVersion);
printf("Vulkan Version:%u.%u.%u\n", major, minor, patch);
}
void VKDebuggingTools::print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data)
{
std::stringstream ss;
if (callback_data->objectCount > 0) {
ss << std::endl;
ss << callback_data->objectCount << " Object[s] related \n";
for (uint32_t object = 0; object < callback_data->objectCount; ++object) {
ss << "ObjectType[" << to_string(callback_data->pObjects[object].objectType) << "],";
ss << "Handle[0x" << std::hex
<< static_cast<uintptr_t>(callback_data->pObjects[object].objectHandle) << "]";
if (callback_data->pObjects[object].pObjectName) {
ss << ",Name[" << callback_data->pObjects[object].pObjectName << "]";
}
ss << std::endl;
}
}
if (callback_data->cmdBufLabelCount > 0) {
ss << std::endl;
ss << callback_data->cmdBufLabelCount << " Command Buffer Label[s] " << std::endl;
for (uint32_t label = 0; label < callback_data->cmdBufLabelCount; ++label) {
if (callback_data->pCmdBufLabels[label].pLabelName) {
ss << "CmdBufLabelName : " << callback_data->pCmdBufLabels[label].pLabelName << std::endl;
}
}
}
if (callback_data->queueLabelCount > 0) {
ss << std::endl;
ss << callback_data->queueLabelCount << " Queue Label[s]\n";
for (uint32_t label = 0; label < callback_data->queueLabelCount; ++label) {
if (callback_data->pQueueLabels[label].pLabelName) {
ss << "QueueLabelName : " << callback_data->pQueueLabels[label].pLabelName << std::endl;
}
}
}
printf("%s\n", ss.str().c_str());
fflush(stdout);
}
VKAPI_ATTR VkBool32 VKAPI_CALL
messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT /* message_type*/,
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
void *user_data);
VKAPI_ATTR VkBool32 VKAPI_CALL
messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT /* message_type*/,
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
void *user_data)
{
VKDebuggingTools &debugging_tools = *reinterpret_cast<VKDebuggingTools *>(user_data);
if (debugging_tools.is_ignore(callback_data->messageIdNumber)) {
return VK_FALSE;
}
bool use_color = CLG_color_support_get(&LOG);
UNUSED_VARS(use_color);
bool enabled = false;
if ((message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) ||
(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT))
{
if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= CLG_SEVERITY_INFO)) {
const char *format = "{0x%x}% s\n %s ";
CLG_logf(LOG.type,
CLG_SEVERITY_INFO,
"",
"",
format,
callback_data->messageIdNumber,
callback_data->pMessageIdName,
callback_data->pMessage);
enabled = true;
}
}
else {
CLG_Severity clog_severity;
switch (message_severity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
clog_severity = CLG_SEVERITY_WARN;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
clog_severity = CLG_SEVERITY_ERROR;
break;
default:
BLI_assert_unreachable();
}
enabled = true;
if (clog_severity == CLG_SEVERITY_ERROR) {
const char *format = " %s {0x%x}\n %s ";
CLG_logf(LOG.type,
clog_severity,
"",
"",
format,
callback_data->pMessageIdName,
callback_data->messageIdNumber,
callback_data->pMessage);
}
else if (LOG.type->level >= CLG_SEVERITY_WARN) {
const char *format = " %s {0x%x}\n %s ";
CLG_logf(LOG.type,
clog_severity,
"",
"",
format,
callback_data->pMessageIdName,
callback_data->messageIdNumber,
callback_data->pMessage);
}
}
if ((enabled) && ((callback_data->objectCount > 0) || (callback_data->cmdBufLabelCount > 0) ||
(callback_data->queueLabelCount > 0)))
{
debugging_tools.print_labels(callback_data);
}
return VK_TRUE;
};
VkResult VKDebuggingTools::init_messenger(VkInstance vk_instance)
{
print_vulkan_version();
vk_message_id_number_ignored.clear();
BLI_assert(enabled);
VkDebugUtilsMessengerCreateInfoEXT create_info;
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.messageSeverity = message_severity;
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
create_info.pfnUserCallback = messenger_callback;
create_info.pUserData = this;
VkResult res = vkCreateDebugUtilsMessengerEXT_r(
vk_instance, &create_info, nullptr, &vk_debug_utils_messenger);
BLI_assert(res == VK_SUCCESS);
return res;
}
void VKDebuggingTools::destroy_messenger(VkInstance vk_instance)
{
if (vk_debug_utils_messenger == nullptr) {
return;
}
BLI_assert(enabled);
vkDestroyDebugUtilsMessengerEXT_r(vk_instance, vk_debug_utils_messenger, nullptr);
vk_message_id_number_ignored.clear();
vk_debug_utils_messenger = nullptr;
return;
}
bool VKDebuggingTools::is_ignore(int32_t id_number)
{
bool found = false;
{
std::scoped_lock lock(ignore_mutex);
found = vk_message_id_number_ignored.contains(id_number);
}
return found;
}
void VKDebuggingTools::add_group(int32_t id_number)
{
std::scoped_lock lock(ignore_mutex);
vk_message_id_number_ignored.add(id_number);
};
void VKDebuggingTools::remove_group(int32_t id_number)
{
std::scoped_lock lock(ignore_mutex);
vk_message_id_number_ignored.remove(id_number);
};
void raise_message(int32_t id_number,
VkDebugUtilsMessageSeverityFlagBitsEXT vk_severity_flag_bits,
const char *format,
...)
{
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
DynStr *ds = nullptr;
va_list arg;
char *info = nullptr;
va_start(arg, format);
ds = BLI_dynstr_new();
BLI_dynstr_vappendf(ds, format, arg);
info = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
va_end(arg);
static VkDebugUtilsMessengerCallbackDataEXT vk_call_back_data;
vk_call_back_data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
vk_call_back_data.pNext = VK_NULL_HANDLE;
vk_call_back_data.messageIdNumber = id_number;
vk_call_back_data.pMessageIdName = "VulkanMessenger";
vk_call_back_data.objectCount = 0;
vk_call_back_data.flags = 0;
vk_call_back_data.pObjects = VK_NULL_HANDLE;
vk_call_back_data.pMessage = info;
debugging_tools.vkSubmitDebugUtilsMessageEXT_r(device.instance_get(),
vk_severity_flag_bits,
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT,
&vk_call_back_data);
MEM_freeN((void *)info);
}
}
}; // namespace blender::gpu::debug

View File

@ -1,62 +1,89 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#pragma once
#include "BKE_global.h"
#include "BLI_string.h"
#include "vk_common.hh"
#include <typeindex>
namespace blender::gpu {
class VKContext;
class VKDevice;
namespace debug {
struct VKDebuggingTools {
bool enabled = false;
/* Function pointer definitions. */
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT_r = nullptr;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT_r = nullptr;
PFN_vkSubmitDebugUtilsMessageEXT vkSubmitDebugUtilsMessageEXT_r = nullptr;
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT_r = nullptr;
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT_r = nullptr;
PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT_r = nullptr;
PFN_vkQueueBeginDebugUtilsLabelEXT vkQueueBeginDebugUtilsLabelEXT_r = nullptr;
PFN_vkQueueEndDebugUtilsLabelEXT vkQueueEndDebugUtilsLabelEXT_r = nullptr;
PFN_vkQueueInsertDebugUtilsLabelEXT vkQueueInsertDebugUtilsLabelEXT_r = nullptr;
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT_r = nullptr;
PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT_r = nullptr;
void init(VkInstance vk_instance);
void deinit();
};
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name);
template<typename T> void object_label(T vk_object_type, const char *name)
{
if (!(G.debug & G_DEBUG_GPU)) {
return;
}
const size_t label_size = 64;
char label[label_size];
memset(label, 0, label_size);
static int stats = 0;
SNPRINTF(label, "%s_%d", name, stats++);
object_label(to_vk_object_type(vk_object_type), (uint64_t)vk_object_type, (const char *)label);
};
void push_marker(VkCommandBuffer vk_command_buffer, const char *name);
void set_marker(VkCommandBuffer vk_command_buffer, const char *name);
void pop_marker(VkCommandBuffer vk_command_buffer);
void push_marker(const VKDevice &device, const char *name);
void set_marker(const VKDevice &device, const char *name);
void pop_marker(const VKDevice &device);
} // namespace debug
} // namespace blender::gpu
/* SPDX-FileCopyrightText: 2023 Blender Foundation. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#pragma once
#include "BKE_global.h"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "vk_common.hh"
#include <mutex>
#include <typeindex>
namespace blender::gpu {
class VKContext;
class VKDevice;
namespace debug {
class VKDebuggingTools {
public:
bool enabled = false;
VkDebugUtilsMessageSeverityFlagsEXT message_severity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
/* Function pointer definitions. */
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT_r = nullptr;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT_r = nullptr;
PFN_vkSubmitDebugUtilsMessageEXT vkSubmitDebugUtilsMessageEXT_r = nullptr;
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT_r = nullptr;
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT_r = nullptr;
PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT_r = nullptr;
PFN_vkQueueBeginDebugUtilsLabelEXT vkQueueBeginDebugUtilsLabelEXT_r = nullptr;
PFN_vkQueueEndDebugUtilsLabelEXT vkQueueEndDebugUtilsLabelEXT_r = nullptr;
PFN_vkQueueInsertDebugUtilsLabelEXT vkQueueInsertDebugUtilsLabelEXT_r = nullptr;
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT_r = nullptr;
PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT_r = nullptr;
VKDebuggingTools() = default;
~VKDebuggingTools();
void init(VkInstance vk_instance);
void deinit(VkInstance vk_instance);
bool is_ignore(int32_t id_number);
VkResult init_messenger(VkInstance vk_instance);
void destroy_messenger(VkInstance vk_instance);
void print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data);
private:
VkDebugUtilsMessengerEXT vk_debug_utils_messenger = nullptr;
Set<int32_t> vk_message_id_number_ignored;
std::mutex ignore_mutex;
void print_vulkan_version();
void add_group(int32_t id_number);
void remove_group(int32_t id_number);
};
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name);
template<typename T> void object_label(T vk_object_type, const char *name)
{
if (!(G.debug & G_DEBUG_GPU)) {
return;
}
const size_t label_size = 64;
char label[label_size];
memset(label, 0, label_size);
static int stats = 0;
SNPRINTF(label, "%s_%d", name, stats++);
object_label(to_vk_object_type(vk_object_type), (uint64_t)vk_object_type, (const char *)label);
};
void push_marker(VkCommandBuffer vk_command_buffer, const char *name);
void set_marker(VkCommandBuffer vk_command_buffer, const char *name);
void pop_marker(VkCommandBuffer vk_command_buffer);
void push_marker(const VKDevice &device, const char *name);
void set_marker(const VKDevice &device, const char *name);
void pop_marker(const VKDevice &device);
/* how to use : debug::raise_message(0xB41ca2,VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,"This
* is a raise message. %llx", (uintptr_t)vk_object); */
void raise_message(int32_t id_number,
VkDebugUtilsMessageSeverityFlagBitsEXT vk_severity_flag_bits,
const char *fmt,
...);
} // namespace debug
} // namespace blender::gpu

View File

@ -18,7 +18,7 @@ void VKDevice::deinit()
{
vmaDestroyAllocator(mem_allocator_);
mem_allocator_ = VK_NULL_HANDLE;
debugging_tools_.deinit();
debugging_tools_.deinit(vk_instance_);
vk_instance_ = VK_NULL_HANDLE;
vk_physical_device_ = VK_NULL_HANDLE;

View File

@ -441,7 +441,7 @@ void VKFrameBuffer::render_pass_create()
size_set(size[0], size[1]);
}
else {
/* A framebuffer should at least be 1 by 1.*/
/* A frame-buffer should at least be 1 by 1. */
this->size_set(1, 1);
}
viewport_reset();

View File

@ -113,9 +113,9 @@ class VKFrameBuffer : public FrameBuffer {
}
/**
* Is this framebuffer immutable?
* Is this frame-buffer immutable?
*
* Framebuffers that are owned by GHOST are immutable and
* Frame-buffers that are owned by GHOST are immutable and
* don't have any attachments assigned. It should be assumed that there is a single color texture
* in slot 0.
*/

View File

@ -79,9 +79,8 @@ void VKPipelineStateManager::finalize_color_blend_state(const VKFrameBuffer &fra
{
color_blend_attachments.clear();
if (framebuffer.is_immutable()) {
/* Immutable framebuffers are owned by GHOST and don't have any attachments assigned. In this
* case we assume that there is a single color texture assigned.
*/
/* Immutable frame-buffers are owned by GHOST and don't have any attachments assigned. In this
* case we assume that there is a single color texture assigned. */
color_blend_attachments.append(color_blend_attachment_template);
}
else {
@ -96,7 +95,7 @@ void VKPipelineStateManager::finalize_color_blend_state(const VKFrameBuffer &fra
else {
/* Test to detect if all color textures are sequential attached from the first slot. We
* assume at this moment that this is the case. Otherwise we need to rewire how attachments
* and bindings work.*/
* and bindings work. */
is_sequential = false;
}
}

View File

@ -20,7 +20,7 @@ class VKUniformBuffer;
class VKVertexBuffer;
class VKStateManager : public StateManager {
/* Dummy sampler for now.*/
/* Dummy sampler for now. */
VKSampler sampler_;
uint texture_unpack_row_length_ = 0;
@ -48,7 +48,7 @@ class VKStateManager : public StateManager {
void issue_barrier(eGPUBarrier barrier_bits) override;
/** Apply resources to the bindings of the active shader.*/
/** Apply resources to the bindings of the active shader. */
void apply_bindings();
void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) override;

View File

@ -55,7 +55,7 @@ class VKVertexBuffer : public VertBuf {
void allocate();
void *convert() const;
/* VKTexture requires access to `buffer_` to convert a vertex buffer to a texture.*/
/* VKTexture requires access to `buffer_` to convert a vertex buffer to a texture. */
friend class VKTexture;
};

View File

@ -119,10 +119,12 @@ static bool export_params_valid(const USDExportParams &params)
return valid;
}
/* Create the root Xform primitive, if the Root Prim path has been set
/**
* Create the root Xform primitive, if the Root Prim path has been set
* in the export options. In the future, this function can be extended
* to author transforms and additional schema data (e.g., model Kind)
* on the root prim. */
* on the root prim.
*/
static void ensure_root_prim(pxr::UsdStageRefPtr stage, const USDExportParams &params)
{
if (params.root_prim_path[0] == '\0') {

View File

@ -34,7 +34,7 @@ struct NodePlacementContext {
/* Map a USD shader prim path to the Blender node converted
* from that shader. This map is updated during shader
* conversion and is used to avoid creating duplicate nodes
* for a given shader. */
* for a given shader. */
ShaderToNodeMap node_cache;
NodePlacementContext(float in_origx,

View File

@ -275,7 +275,7 @@ static void populate_curve_props_for_nurbs(const bke::CurvesGeometry &geometry,
const bool is_cyclic)
{
/* Order and range, when representing a batched NurbsCurve should be authored one value per
* curve.*/
* curve. */
const int num_curves = geometry.curve_num;
orders.resize(num_curves);

View File

@ -711,6 +711,15 @@ enum {
* was kept around (because e.g. detected as user-edited).
*/
LIB_LIB_OVERRIDE_RESYNC_LEFTOVER = 1 << 13,
/**
* This `id` was explicitly copied as part of a clipboard copy operation.
* When reading the clipboard back, this can be used to check which ID's are
* intended to be part of the clipboard, compared with ID's that were indirectly referenced.
*
* While the flag is typically cleared, a saved file may have this set for some data-blocks,
* so it must be treated as dirty.
*/
LIB_CLIPBOARD_MARK = 1 << 14,
};
/**

View File

@ -201,7 +201,6 @@ typedef enum uiPanelDataExpansion {
UI_SUBPANEL_DATA_EXPAND_13 = (1 << 13),
UI_SUBPANEL_DATA_EXPAND_14 = (1 << 14),
UI_SUBPANEL_DATA_EXPAND_15 = (1 << 15),
UI_SUBPANEL_DATA_EXPAND_16 = (1 << 16),
} uiPanelDataExpansion;
/**

View File

@ -5,207 +5,18 @@
#include "UI_interface.h"
#include "UI_resources.h"
#include "BLI_array.hh"
#include "BLI_array_utils.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_instances.hh"
#include "BKE_mesh.hh"
#include "BKE_pointcloud.h"
#include "GEO_mesh_copy_selection.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_delete_geometry_cc {
template<typename T>
static void copy_data_based_on_map(const Span<T> src,
const Span<int> index_map,
MutableSpan<T> dst)
{
threading::parallel_for(index_map.index_range(), 1024, [&](const IndexRange range) {
for (const int i_src : range) {
const int i_dst = index_map[i_src];
if (i_dst != -1) {
dst[i_dst] = src[i_src];
}
}
});
}
static void copy_attributes_based_on_map(const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip,
const Span<int> index_map)
{
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (meta_data.domain != domain) {
return true;
}
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}
if (skip.contains(id.name())) {
return true;
}
const GVArraySpan src = *src_attributes.lookup(id);
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, meta_data.domain, meta_data.data_type);
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
copy_data_based_on_map(src.typed<T>(), index_map, dst.span.typed<T>());
});
dst.finish();
return true;
});
}
static void copy_face_corner_attributes(const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip,
const int selected_loops_num,
const Span<int> selected_poly_indices,
const Mesh &mesh_in)
{
const OffsetIndices polys = mesh_in.polys();
Vector<int64_t> indices;
indices.reserve(selected_loops_num);
for (const int src_poly_index : selected_poly_indices) {
for (const int corner : polys[src_poly_index]) {
indices.append_unchecked(corner);
}
}
IndexMaskMemory memory;
bke::gather_attributes(src_attributes,
ATTR_DOMAIN_CORNER,
propagation_info,
skip,
IndexMask::from_indices<int64_t>(indices, memory),
dst_attributes);
}
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map)
{
BLI_assert(src_mesh.totedge == edge_map.size());
const Span<int2> src_edges = src_mesh.edges();
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange range) {
for (const int i_src : range) {
const int i_dst = edge_map[i_src];
if (ELEM(i_dst, -1, -2)) {
continue;
}
dst_edges[i_dst] = src_edges[i_src];
}
});
}
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
Mesh &dst_mesh,
Span<int> vertex_map,
Span<int> edge_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
const Span<int2> src_edges = src_mesh.edges();
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange range) {
for (const int i_src : range) {
const int i_dst = edge_map[i_src];
if (i_dst == -1) {
continue;
}
dst_edges[i_dst][0] = vertex_map[src_edges[i_src][0]];
dst_edges[i_dst][1] = vertex_map[src_edges[i_src][1]];
}
});
}
/* Faces and edges changed but vertices are the same. */
static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Mesh &dst_mesh,
Span<int> edge_map,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> src_corner_edges = src_mesh.corner_edges();
MutableSpan<int> dst_poly_offsets = dst_mesh.poly_offsets_for_write();
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
MutableSpan<int> dst_corner_edges = dst_mesh.corner_edges_for_write();
threading::parallel_for(masked_poly_indices.index_range(), 512, [&](const IndexRange range) {
for (const int i_dst : range) {
const int i_src = masked_poly_indices[i_dst];
const IndexRange poly_src = src_polys[i_src];
const Span<int> src_poly_verts = src_corner_verts.slice(poly_src);
const Span<int> src_poly_edges = src_corner_edges.slice(poly_src);
dst_poly_offsets[i_dst] = new_loop_starts[i_dst];
MutableSpan<int> dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst],
poly_src.size());
MutableSpan<int> dst_poly_edges = dst_corner_edges.slice(dst_poly_offsets[i_dst],
poly_src.size());
dst_poly_verts.copy_from(src_poly_verts);
for (const int i : IndexRange(poly_src.size())) {
dst_poly_edges[i] = edge_map[src_poly_edges[i]];
}
}
});
}
static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Mesh &dst_mesh,
Span<int> vertex_map,
Span<int> edge_map,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> src_corner_edges = src_mesh.corner_edges();
MutableSpan<int> dst_poly_offsets = dst_mesh.poly_offsets_for_write();
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
MutableSpan<int> dst_corner_edges = dst_mesh.corner_edges_for_write();
threading::parallel_for(masked_poly_indices.index_range(), 512, [&](const IndexRange range) {
for (const int i_dst : range) {
const int i_src = masked_poly_indices[i_dst];
const IndexRange poly_src = src_polys[i_src];
const Span<int> src_poly_verts = src_corner_verts.slice(poly_src);
const Span<int> src_poly_edges = src_corner_edges.slice(poly_src);
dst_poly_offsets[i_dst] = new_loop_starts[i_dst];
MutableSpan<int> dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst],
poly_src.size());
MutableSpan<int> dst_poly_edges = dst_corner_edges.slice(dst_poly_offsets[i_dst],
poly_src.size());
for (const int i : IndexRange(poly_src.size())) {
dst_poly_verts[i] = vertex_map[src_poly_verts[i]];
}
for (const int i : IndexRange(poly_src.size())) {
dst_poly_edges[i] = edge_map[src_poly_edges[i]];
}
}
});
}
/** \return std::nullopt if the geometry should remain unchanged. */
static std::optional<Curves *> separate_curves_selection(
const Curves &src_curves_id,
@ -291,729 +102,24 @@ static void delete_selected_instances(GeometrySet &geometry_set,
instances.remove(selection, propagation_info);
}
static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
MutableSpan<int> r_vertex_map,
int *r_selected_verts_num)
{
BLI_assert(vertex_selection.size() == r_vertex_map.size());
int selected_verts_num = 0;
for (const int i : r_vertex_map.index_range()) {
if (vertex_selection[i]) {
r_vertex_map[i] = selected_verts_num;
selected_verts_num++;
}
else {
r_vertex_map[i] = -1;
}
}
*r_selected_verts_num = selected_verts_num;
}
static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
const Span<bool> vertex_selection,
MutableSpan<int> r_edge_map,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == r_edge_map.size());
const Span<int2> edges = mesh.edges();
int selected_edges_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
const int2 &edge = edges[i];
/* Only add the edge if both vertices will be in the new mesh. */
if (vertex_selection[edge[0]] && vertex_selection[edge[1]]) {
r_edge_map[i] = selected_edges_num;
selected_edges_num++;
}
else {
r_edge_map[i] = -1;
}
}
*r_selected_edges_num = selected_edges_num;
}
static void compute_selected_polys_from_vertex_selection(const Mesh &mesh,
const Span<bool> vertex_selection,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totvert == vertex_selection.size());
const OffsetIndices polys = mesh.polys();
const Span<int> corner_verts = mesh.corner_verts();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
for (const int i : polys.index_range()) {
const IndexRange poly_src = polys[i];
bool all_verts_in_selection = true;
for (const int vert : corner_verts.slice(poly_src)) {
if (!vertex_selection[vert]) {
all_verts_in_selection = false;
break;
}
}
if (all_verts_in_selection) {
r_selected_poly_indices.append_unchecked(i);
r_loop_starts.append_unchecked(selected_loops_num);
selected_loops_num += poly_src.size();
}
}
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
}
/**
* Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the
* edge are kept along with the edge.
*/
static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh,
const Span<bool> edge_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
int *r_selected_verts_num,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == edge_selection.size());
const Span<int2> edges = mesh.edges();
int selected_edges_num = 0;
int selected_verts_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
const int2 &edge = edges[i];
if (edge_selection[i]) {
r_edge_map[i] = selected_edges_num;
selected_edges_num++;
if (r_vertex_map[edge[0]] == -1) {
r_vertex_map[edge[0]] = selected_verts_num;
selected_verts_num++;
}
if (r_vertex_map[edge[1]] == -1) {
r_vertex_map[edge[1]] = selected_verts_num;
selected_verts_num++;
}
}
else {
r_edge_map[i] = -1;
}
}
*r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
}
/**
* Checks for every edge if it is in `edge_selection`.
*/
static void compute_selected_edges_from_edge_selection(const Mesh &mesh,
const Span<bool> edge_selection,
MutableSpan<int> r_edge_map,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == edge_selection.size());
int selected_edges_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
if (edge_selection[i]) {
r_edge_map[i] = selected_edges_num;
selected_edges_num++;
}
else {
r_edge_map[i] = -1;
}
}
*r_selected_edges_num = selected_edges_num;
}
/**
* Checks for every polygon if all the edges are in `edge_selection`. If they are, then that
* polygon is kept.
*/
static void compute_selected_polys_from_edge_selection(const Mesh &mesh,
const Span<bool> edge_selection,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
const OffsetIndices polys = mesh.polys();
const Span<int> corner_edges = mesh.corner_edges();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
for (const int i : polys.index_range()) {
const IndexRange poly_src = polys[i];
bool all_edges_in_selection = true;
for (const int edge : corner_edges.slice(poly_src)) {
if (!edge_selection[edge]) {
all_edges_in_selection = false;
break;
}
}
if (all_edges_in_selection) {
r_selected_poly_indices.append_unchecked(i);
r_loop_starts.append_unchecked(selected_loops_num);
selected_loops_num += poly_src.size();
}
}
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
}
/**
* Checks for every edge and polygon if all its vertices are in `vertex_selection`.
*/
static void compute_selected_mesh_data_from_vertex_selection_edge_face(
static std::optional<Mesh *> separate_mesh_selection(
const Mesh &mesh,
const Span<bool> vertex_selection,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
const Field<bool> &selection,
const eAttrDomain selection_domain,
const GeometryNodeDeleteGeometryMode mode,
const AnonymousAttributePropagationInfo &propagation_info)
{
threading::parallel_invoke(
mesh.totedge > 1000,
[&]() {
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
},
[&]() {
compute_selected_polys_from_vertex_selection(mesh,
vertex_selection,
r_selected_poly_indices,
r_loop_starts,
r_selected_polys_num,
r_selected_loops_num);
});
}
/**
* Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all
* vertices of that polygon or edge are in the selection.
*/
static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
const Span<bool> vertex_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
threading::parallel_invoke(
mesh.totedge > 1000,
[&]() {
compute_selected_verts_from_vertex_selection(
vertex_selection, r_vertex_map, r_selected_verts_num);
},
[&]() {
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
},
[&]() {
compute_selected_polys_from_vertex_selection(mesh,
vertex_selection,
r_selected_poly_indices,
r_loop_starts,
r_selected_polys_num,
r_selected_loops_num);
});
}
/**
* Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in
* the selection.
*/
static void compute_selected_mesh_data_from_edge_selection_edge_face(
const Mesh &mesh,
const Span<bool> edge_selection,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
threading::parallel_invoke(
mesh.totedge > 1000,
[&]() {
compute_selected_edges_from_edge_selection(
mesh, edge_selection, r_edge_map, r_selected_edges_num);
},
[&]() {
compute_selected_polys_from_edge_selection(mesh,
edge_selection,
r_selected_poly_indices,
r_loop_starts,
r_selected_polys_num,
r_selected_loops_num);
});
}
/**
* Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
* that edge are kept as well. The polys are kept if all edges are in the selection.
*/
static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
const Span<bool> edge_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
threading::parallel_invoke(
mesh.totedge > 1000,
[&]() {
r_vertex_map.fill(-1);
compute_selected_verts_and_edges_from_edge_selection(mesh,
edge_selection,
r_vertex_map,
r_edge_map,
r_selected_verts_num,
r_selected_edges_num);
},
[&]() {
compute_selected_polys_from_edge_selection(mesh,
edge_selection,
r_selected_poly_indices,
r_loop_starts,
r_selected_polys_num,
r_selected_loops_num);
});
}
/**
* Checks for every polygon if it is in `poly_selection`.
*/
static void compute_selected_polys_from_poly_selection(const Mesh &mesh,
const Span<bool> poly_selection,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
const OffsetIndices polys = mesh.polys();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
for (const int i : polys.index_range()) {
const IndexRange poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
r_loop_starts.append_unchecked(selected_loops_num);
selected_loops_num += poly_src.size();
}
}
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
}
/**
* Checks for every polygon if it is in `poly_selection`. If it is, the edges
* belonging to that polygon are kept as well.
*/
static void compute_selected_mesh_data_from_poly_selection_edge_face(
const Mesh &mesh,
const Span<bool> poly_selection,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
const OffsetIndices polys = mesh.polys();
const Span<int> corner_edges = mesh.corner_edges();
r_edge_map.fill(-1);
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
int selected_edges_num = 0;
for (const int i : polys.index_range()) {
const IndexRange poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
r_loop_starts.append_unchecked(selected_loops_num);
selected_loops_num += poly_src.size();
/* Add the vertices and the edges. */
for (const int edge : corner_edges.slice(poly_src)) {
/* Check first if it has not yet been added. */
if (r_edge_map[edge] == -1) {
r_edge_map[edge] = selected_edges_num;
selected_edges_num++;
}
}
}
}
*r_selected_edges_num = selected_edges_num;
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
}
/**
* Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices
* belonging to that polygon are kept as well.
*/
static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
const Span<bool> poly_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
const OffsetIndices polys = mesh.polys();
const Span<int> corner_verts = mesh.corner_verts();
const Span<int> corner_edges = mesh.corner_edges();
r_vertex_map.fill(-1);
r_edge_map.fill(-1);
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
int selected_verts_num = 0;
int selected_edges_num = 0;
for (const int i : polys.index_range()) {
const IndexRange poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
r_loop_starts.append_unchecked(selected_loops_num);
selected_loops_num += poly_src.size();
/* Add the vertices and the edges. */
for (const int corner : poly_src) {
const int vert = corner_verts[corner];
const int edge = corner_edges[corner];
/* Check first if it has not yet been added. */
if (r_vertex_map[vert] == -1) {
r_vertex_map[vert] = selected_verts_num;
selected_verts_num++;
}
if (r_edge_map[edge] == -1) {
r_edge_map[edge] = selected_edges_num;
selected_edges_num++;
}
}
}
}
*r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
}
/**
* Keep the parts of the mesh that are in the selection.
*/
static void do_mesh_separation(GeometrySet &geometry_set,
const Mesh &mesh_in,
const Span<bool> selection,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* Needed in all cases. */
Vector<int> selected_poly_indices;
Vector<int> new_loop_starts;
int selected_polys_num = 0;
int selected_loops_num = 0;
IndexMaskMemory memory;
Mesh *mesh_out;
switch (mode) {
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: {
Array<int> vertex_map(mesh_in.totvert);
int selected_verts_num = 0;
Array<int> edge_map(mesh_in.totedge);
int selected_edges_num = 0;
switch (domain) {
case ATTR_DOMAIN_POINT:
compute_selected_mesh_data_from_vertex_selection(mesh_in,
selection,
vertex_map,
edge_map,
selected_poly_indices,
new_loop_starts,
&selected_verts_num,
&selected_edges_num,
&selected_polys_num,
&selected_loops_num);
break;
case ATTR_DOMAIN_EDGE:
compute_selected_mesh_data_from_edge_selection(mesh_in,
selection,
vertex_map,
edge_map,
selected_poly_indices,
new_loop_starts,
&selected_verts_num,
&selected_edges_num,
&selected_polys_num,
&selected_loops_num);
break;
case ATTR_DOMAIN_FACE:
compute_selected_mesh_data_from_poly_selection(mesh_in,
selection,
vertex_map,
edge_map,
selected_poly_indices,
new_loop_starts,
&selected_verts_num,
&selected_edges_num,
&selected_polys_num,
&selected_loops_num);
break;
default:
BLI_assert_unreachable();
break;
}
mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in,
selected_verts_num,
selected_edges_num,
selected_polys_num,
selected_loops_num);
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map);
copy_masked_polys_to_new_mesh(
mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
copy_attributes_based_on_map(mesh_in.attributes(),
mesh_out->attributes_for_write(),
ATTR_DOMAIN_POINT,
propagation_info,
{},
vertex_map);
copy_attributes_based_on_map(mesh_in.attributes(),
mesh_out->attributes_for_write(),
ATTR_DOMAIN_EDGE,
propagation_info,
{".edge_verts"},
edge_map);
bke::gather_attributes(mesh_in.attributes(),
ATTR_DOMAIN_FACE,
propagation_info,
{},
IndexMask::from_indices(selected_poly_indices.as_span(), memory),
mesh_out->attributes_for_write());
copy_face_corner_attributes(mesh_in.attributes(),
mesh_out->attributes_for_write(),
propagation_info,
{".corner_vert", ".corner_edge"},
selected_loops_num,
selected_poly_indices,
mesh_in);
break;
}
case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: {
Array<int> edge_map(mesh_in.totedge);
int selected_edges_num = 0;
switch (domain) {
case ATTR_DOMAIN_POINT:
compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in,
selection,
edge_map,
selected_poly_indices,
new_loop_starts,
&selected_edges_num,
&selected_polys_num,
&selected_loops_num);
break;
case ATTR_DOMAIN_EDGE:
compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in,
selection,
edge_map,
selected_poly_indices,
new_loop_starts,
&selected_edges_num,
&selected_polys_num,
&selected_loops_num);
break;
case ATTR_DOMAIN_FACE:
compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in,
selection,
edge_map,
selected_poly_indices,
new_loop_starts,
&selected_edges_num,
&selected_polys_num,
&selected_loops_num);
break;
default:
BLI_assert_unreachable();
break;
}
mesh_out = BKE_mesh_new_nomain_from_template(
&mesh_in, mesh_in.totvert, selected_edges_num, selected_polys_num, selected_loops_num);
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map);
copy_masked_polys_to_new_mesh(
mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts);
bke::copy_attributes(mesh_in.attributes(),
ATTR_DOMAIN_POINT,
propagation_info,
{},
mesh_out->attributes_for_write());
copy_attributes_based_on_map(mesh_in.attributes(),
mesh_out->attributes_for_write(),
ATTR_DOMAIN_EDGE,
propagation_info,
{".edge_verts"},
edge_map);
bke::gather_attributes(mesh_in.attributes(),
ATTR_DOMAIN_FACE,
propagation_info,
{},
IndexMask::from_indices(selected_poly_indices.as_span(), memory),
mesh_out->attributes_for_write());
copy_face_corner_attributes(mesh_in.attributes(),
mesh_out->attributes_for_write(),
propagation_info,
{".corner_vert", ".corner_edge"},
selected_loops_num,
selected_poly_indices,
mesh_in);
/* Positions are not changed by the operation, so the bounds are the same. */
mesh_out->runtime->bounds_cache = mesh_in.runtime->bounds_cache;
break;
}
case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: {
switch (domain) {
case ATTR_DOMAIN_POINT:
compute_selected_polys_from_vertex_selection(mesh_in,
selection,
selected_poly_indices,
new_loop_starts,
&selected_polys_num,
&selected_loops_num);
break;
case ATTR_DOMAIN_EDGE:
compute_selected_polys_from_edge_selection(mesh_in,
selection,
selected_poly_indices,
new_loop_starts,
&selected_polys_num,
&selected_loops_num);
break;
case ATTR_DOMAIN_FACE:
compute_selected_polys_from_poly_selection(mesh_in,
selection,
selected_poly_indices,
new_loop_starts,
&selected_polys_num,
&selected_loops_num);
break;
default:
BLI_assert_unreachable();
break;
}
mesh_out = BKE_mesh_new_nomain_from_template(
&mesh_in, mesh_in.totvert, mesh_in.totedge, selected_polys_num, selected_loops_num);
mesh_out->poly_offsets_for_write().drop_back(1).copy_from(new_loop_starts);
bke::copy_attributes(mesh_in.attributes(),
ATTR_DOMAIN_POINT,
propagation_info,
{},
mesh_out->attributes_for_write());
bke::copy_attributes(mesh_in.attributes(),
ATTR_DOMAIN_EDGE,
propagation_info,
{},
mesh_out->attributes_for_write());
bke::gather_attributes(mesh_in.attributes(),
ATTR_DOMAIN_FACE,
propagation_info,
{},
IndexMask::from_indices(selected_poly_indices.as_span(), memory),
mesh_out->attributes_for_write());
copy_face_corner_attributes(mesh_in.attributes(),
mesh_out->attributes_for_write(),
propagation_info,
{},
selected_loops_num,
selected_poly_indices,
mesh_in);
/* Positions are not changed by the operation, so the bounds are the same. */
mesh_out->runtime->bounds_cache = mesh_in.runtime->bounds_cache;
break;
}
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL:
return geometry::mesh_copy_selection(mesh, selection, selection_domain, propagation_info);
case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE:
return geometry::mesh_copy_selection_keep_verts(
mesh, selection, selection_domain, propagation_info);
case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE:
return geometry::mesh_copy_selection_keep_edges(
mesh, selection, selection_domain, propagation_info);
}
geometry_set.replace_mesh(mesh_out);
}
static void separate_mesh_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const eAttrDomain selection_domain,
const GeometryNodeDeleteGeometryMode mode,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Mesh &src_mesh = *geometry_set.get_mesh_for_read();
const bke::MeshFieldContext field_context{src_mesh, selection_domain};
fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)};
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
/* Check if there is anything to delete. */
if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) {
return;
}
const VArraySpan<bool> selection_span{selection};
do_mesh_separation(
geometry_set, src_mesh, selection_span, selection_domain, mode, propagation_info);
return nullptr;
}
} // namespace blender::nodes::node_geo_delete_geometry_cc
@ -1040,9 +146,13 @@ void separate_geometry(GeometrySet &geometry_set,
some_valid_domain = true;
}
}
if (geometry_set.has_mesh()) {
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) {
file_ns::separate_mesh_selection(geometry_set, selection, domain, mode, propagation_info);
std::optional<Mesh *> dst_mesh = file_ns::separate_mesh_selection(
*mesh, selection, domain, mode, propagation_info);
if (dst_mesh) {
geometry_set.replace_mesh(*dst_mesh);
}
some_valid_domain = true;
}
}

View File

@ -133,17 +133,33 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item)
return get_simulation_item_cpp_type(eNodeSocketDatatype(item.socket_type));
}
static void remove_materials(Material ***materials, short *materials_num)
{
MEM_SAFE_FREE(*materials);
*materials_num = 0;
}
/**
* Removess parts of the geometry that can't be stored in the simulation state:
* - Anonymous attributes can't be stored because it is not known which of them will or will not be
* used in the future.
* - Materials can't be stored directly, because they are linked ID data blocks that can't be
* restored from baked data currently.
*/
static void cleanup_geometry_for_simulation_state(GeometrySet &main_geometry)
{
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
mesh->attributes_for_write().remove_anonymous();
remove_materials(&mesh->mat, &mesh->totcol);
}
if (Curves *curves = geometry.get_curves_for_write()) {
curves->geometry.wrap().attributes_for_write().remove_anonymous();
remove_materials(&curves->mat, &curves->totcol);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
pointcloud->attributes_for_write().remove_anonymous();
remove_materials(&pointcloud->mat, &pointcloud->totcol);
}
if (bke::Instances *instances = geometry.get_instances_for_write()) {
instances->attributes_for_write().remove_anonymous();

View File

@ -528,7 +528,7 @@ void RE_RenderBuffer_assign_shared(RenderBuffer *lhs, const RenderBuffer *rhs);
void RE_RenderBuffer_data_free(RenderBuffer *render_buffer);
/* Implementation of above, but for byte buffer. */
/* TODO(sergey): Once everything is C++ we can remove the duplicated API. */
/* TODO(sergey): Once everything is C++ we can remove the duplicated API. */
RenderByteBuffer RE_RenderByteBuffer_new(uint8_t *data);
void RE_RenderByteBuffer_assign_data(RenderByteBuffer *render_buffer, uint8_t *data);
void RE_RenderByteBuffer_assign_shared(RenderByteBuffer *lhs, const RenderByteBuffer *rhs);

Some files were not shown because too many files have changed in this diff Show More