UI: Asset Shelf (Experimental Feature) #104831

Closed
Julian Eisel wants to merge 399 commits from asset-shelf into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
77 changed files with 2223 additions and 418 deletions
Showing only changes of commit 0b32863368 - Show all commits

View File

@ -1673,7 +1673,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
driver_version = "470"
col.label(text=iface_("Requires NVIDIA GPU with compute capability %s") % compute_capability,
icon='BLANK1', translate=False)
col.label(text="and NVIDIA driver version %s or newer" % driver_version,
col.label(text=iface_("and NVIDIA driver version %s or newer") % driver_version,
icon='BLANK1', translate=False)
elif device_type == 'HIP':
if True:
@ -1719,7 +1719,8 @@ class CyclesPreferences(bpy.types.AddonPreferences):
.replace('(TM)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(tm)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(R)', unicodedata.lookup('REGISTERED SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN')),
translate=False
)
def draw_impl(self, layout, context):

View File

@ -35,7 +35,7 @@ struct BVHReferenceCompare {
/* Compare two references.
*
* Returns value is similar to return value of strcmp().
* Returns value is similar to return value of `strcmp()`.
*/
__forceinline int compare(const BVHReference &ra, const BVHReference &rb) const
{

View File

@ -494,7 +494,7 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
float3 H;
float cos_NH, cos_HI;
float3 local_H, local_I, X, Y; /* Nneeded for anisotropic microfacets later. */
float3 local_H, local_I, X, Y; /* Needed for anisotropic microfacets later. */
if (m_singular) {
H = N;
cos_NH = 1.0f;

View File

@ -979,7 +979,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
for (int ci = 0; ci < sd_mnee->num_closure; ci++) {
ccl_private ShaderClosure *bsdf = &sd_mnee->closure[ci];
if (CLOSURE_IS_REFRACTIVE(bsdf->type)) {
/* Note that Glass closures are treates as refractive further below. */
/* Note that Glass closures are treated as refractive further below. */
found_refractive_microfacet_bsdf = true;
ccl_private MicrofacetBsdf *microfacet_bsdf = (ccl_private MicrofacetBsdf *)bsdf;

View File

@ -177,20 +177,53 @@ typedef enum {
typedef enum {
GHOST_kEventUnknown = 0,
GHOST_kEventCursorMove, /* Mouse move event. */
GHOST_kEventButtonDown, /* Mouse button event. */
GHOST_kEventButtonUp, /* Mouse button event. */
GHOST_kEventWheel, /* Mouse wheel event. */
GHOST_kEventTrackpad, /* Trackpad event. */
/** Mouse move event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventCursorData.
*/
GHOST_kEventCursorMove,
/** Mouse button down event. */
GHOST_kEventButtonDown,
/** Mouse button up event. */
GHOST_kEventButtonUp,
/**
* Mouse wheel event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventWheelData.
*/
GHOST_kEventWheel,
/**
* Trackpad event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventTrackpadData.
*/
GHOST_kEventTrackpad,
#ifdef WITH_INPUT_NDOF
GHOST_kEventNDOFMotion, /* N degree of freedom device motion event. */
GHOST_kEventNDOFButton, /* N degree of freedom device button event. */
/**
* N degree of freedom device motion event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventNDOFMotionData.
*/
GHOST_kEventNDOFMotion,
/**
* N degree of freedom device button event.
*
* \note #GHOST_GetEventData returns #GHOST_TEventNDOFButtonData.
*/
GHOST_kEventNDOFButton,
#endif
/**
* Keyboard up/down events.
*
* Includes repeat events, check #GHOST_TEventKeyData::is_repeat
* if detecting repeat events is needed.
*
* \note #GHOST_GetEventData returns #GHOST_TEventKeyData.
*/
GHOST_kEventKeyDown,
GHOST_kEventKeyUp,
// GHOST_kEventKeyAuto,
GHOST_kEventQuitRequest,

View File

@ -20,11 +20,13 @@ enum TransformType {
TRANSFORM_SRGB_TO_LINEAR,
TRANSFORM_SCALE,
TRANSFORM_EXPONENT,
TRANSFORM_NONE,
TRANSFORM_UNKNOWN,
};
#define COLORSPACE_LINEAR ((OCIO_ConstColorSpaceRcPtr *)1)
#define COLORSPACE_SRGB ((OCIO_ConstColorSpaceRcPtr *)2)
#define COLORSPACE_DATA ((OCIO_ConstColorSpaceRcPtr *)3)
typedef struct OCIO_PackedImageDescription {
float *data;
@ -165,6 +167,8 @@ OCIO_ConstColorSpaceRcPtr *FallbackImpl::configGetColorSpace(OCIO_ConstConfigRcP
return COLORSPACE_LINEAR;
else if (strcmp(name, "sRGB") == 0)
return COLORSPACE_SRGB;
else if (strcmp(name, "data") == 0)
return COLORSPACE_DATA;
return NULL;
}
@ -179,6 +183,9 @@ int FallbackImpl::configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, con
else if (cs == COLORSPACE_SRGB) {
return 1;
}
else if (cs == COLORSPACE_DATA) {
return 2;
}
return -1;
}
@ -314,7 +321,10 @@ OCIO_ConstProcessorRcPtr *FallbackImpl::configGetProcessorWithNames(OCIO_ConstCo
OCIO_ConstColorSpaceRcPtr *cs_src = configGetColorSpace(config, srcName);
OCIO_ConstColorSpaceRcPtr *cs_dst = configGetColorSpace(config, dstName);
FallbackTransform transform;
if (cs_src == COLORSPACE_LINEAR && cs_dst == COLORSPACE_SRGB) {
if (cs_src == COLORSPACE_DATA || cs_dst == COLORSPACE_DATA) {
transform.type = TRANSFORM_NONE;
}
else if (cs_src == COLORSPACE_LINEAR && cs_dst == COLORSPACE_SRGB) {
transform.type = TRANSFORM_LINEAR_TO_SRGB;
}
else if (cs_src == COLORSPACE_SRGB && cs_dst == COLORSPACE_LINEAR) {
@ -433,6 +443,9 @@ const char *FallbackImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs)
else if (cs == COLORSPACE_SRGB) {
return "sRGB";
}
else if (cs == COLORSPACE_DATA) {
return "data";
}
return NULL;
}

View File

@ -501,9 +501,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
Recursively get strings, needed in case we have "Blah" + "Blah", passed as an argument in that case it won't
evaluate to a string. However, break on some kind of stopper nodes, like e.g. Subscript.
"""
# New in py 3.8: all constants are of type 'ast.Constant'.
# 'ast.Str' will have to be removed when we officially switch to this version.
if type(node) in {ast.Str, getattr(ast, "Constant", None)}:
if type(node) == ast.Constant:
eval_str = ast.literal_eval(node)
if eval_str and type(eval_str) == str:
yield (is_split, eval_str, (node,))
@ -692,6 +690,15 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
else:
continue
# Skip function if it's marked as not translatable.
do_translate = True
for kw in node.keywords:
if kw.arg == "translate" and not kw.value.value:
do_translate = False
break
if not do_translate:
continue
func_args = func_translate_args.get(func_id, {})
# First try to get i18n contexts, for every possible msgid id.

View File

@ -13,8 +13,8 @@ from bpy.props import (
class SCENE_OT_freestyle_fill_range_by_selection(Operator):
"""Fill the Range Min/Max entries by the min/max distance between selected mesh objects and the source object """
"""(either a user-specified object or the active camera)"""
"""Fill the Range Min/Max entries by the min/max distance between selected mesh objects and the source object """ \
"""(either a user-specified object or the active camera)"""
bl_idname = "scene.freestyle_fill_range_by_selection"
bl_label = "Fill Range by Selection"
bl_options = {'INTERNAL'}

View File

@ -21,6 +21,8 @@ struct FCurve;
struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
struct Scene;
struct ViewLayer;
/* ************** F-Curve Drivers ***************** */
@ -59,6 +61,32 @@ void fcurve_free_driver(struct FCurve *fcu);
*/
struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver);
/**
* Get property from which the specific property can be found from.
*
* This depends on the type of `dvar`:
*
* - For the Single Property the `r_prop` is a pointer to an ID, which is used to resolve the
* target rna_path.
*
* - For Transform Channel, Rotational Difference, Distance the `r_prop` is a pointer to an
* object from which transformation is read.
*
* - For Context Property the `r_prop` points to a resolved data corresponding to the
* dtar->context_property accessed from the given evaluated context. This could either be an ID
* property for Active Scene, or a data property for Active View Layer.
*
* If the target property can not be resolved false is returned.
*/
typedef struct DriverTargetContext {
struct Scene *scene;
struct ViewLayer *view_layer;
} DriverTargetContext;
bool driver_get_target_property(const DriverTargetContext *driver_target_context,
struct DriverVar *dvar,
struct DriverTarget *dtar,
struct PointerRNA *r_prop);
/**
* Copy driver variables from src_vars list to dst_vars list.
*/
@ -102,11 +130,15 @@ struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver);
/**
* Evaluate a Driver Variable to get a value that contributes to the final.
*/
float driver_get_variable_value(struct ChannelDriver *driver, struct DriverVar *dvar);
float driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar);
/**
* Same as 'dtar_get_prop_val'. but get the RNA property.
*/
bool driver_get_variable_property(struct ChannelDriver *driver,
bool driver_get_variable_property(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar,
struct PointerRNA *r_ptr,
struct PropertyRNA **r_prop,

View File

@ -271,9 +271,20 @@ typedef struct bNodeType {
/** Check and update if internal ID data has changed. */
void (*group_update_func)(struct bNodeTree *ntree, struct bNode *node);
/** Initialize a new node instance of this type after creation. */
/**
* Initialize a new node instance of this type after creation.
*
* \note Assignments to `node->id` must not increment the user of the ID.
* This is handled by the caller of this callback.
*/
void (*initfunc)(struct bNodeTree *ntree, struct bNode *node);
/** Free the node instance. */
/**
* Free the node instance.
*
* \note Access to `node->id` must be avoided in this function as this is called
* while freeing #Main, the state of this ID is undefined.
* Higher level logic to remove the node handles the user-count.
*/
void (*freefunc)(struct bNode *node);
/** Make a copy of the node instance. */
void (*copyfunc)(struct bNodeTree *dest_ntree,

View File

@ -409,6 +409,9 @@ void BKE_curve_init(Curve *cu, const short curve_type)
cu->resolv = 4;
}
cu->bevel_profile = nullptr;
/* Initialize the offset to 1.0, to compensate for it being set to -1.0
in the property getter. */
cu->offset = 1.0f;
}
Curve *BKE_curve_add(Main *bmain, const char *name, int type)

View File

@ -10,6 +10,7 @@
#include "DNA_anim_types.h"
#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_alloca.h"
#include "BLI_expr_pylike_eval.h"
@ -31,11 +32,14 @@
#include "RNA_access.h"
#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "atomic_ops.h"
#include "CLG_log.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
@ -53,7 +57,9 @@ static CLG_LogRef LOG = {"bke.fcurve"};
/* TypeInfo for Driver Variables (dvti) */
typedef struct DriverVarTypeInfo {
/* Evaluation callback. */
float (*get_value)(ChannelDriver *driver, DriverVar *dvar);
float (*get_value)(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar);
/* Allocation of target slots. */
int num_targets; /* Number of target slots required. */
@ -73,27 +79,89 @@ typedef struct DriverVarTypeInfo {
/** \name Driver Target Utilities
* \{ */
static DriverTargetContext driver_target_context_from_animation_context(
const AnimationEvalContext *anim_eval_context)
{
DriverTargetContext driver_target_context;
driver_target_context.scene = DEG_get_evaluated_scene(anim_eval_context->depsgraph);
driver_target_context.view_layer = DEG_get_evaluated_view_layer(anim_eval_context->depsgraph);
return driver_target_context;
}
/* Specialized implementation of driver_get_target_property() used for the
* DVAR_TYPE_CONTEXT_PROP variable type. */
static bool driver_get_target_context_property(const DriverTargetContext *driver_target_context,
DriverTarget *dtar,
PointerRNA *r_property_ptr)
{
switch (dtar->context_property) {
case DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE:
RNA_id_pointer_create(&driver_target_context->scene->id, r_property_ptr);
return true;
case DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER: {
RNA_pointer_create(&driver_target_context->scene->id,
&RNA_ViewLayer,
driver_target_context->view_layer,
r_property_ptr);
return true;
}
}
BLI_assert_unreachable();
/* Reset to a NULL RNA pointer.
* This allows to more gracefully handle issues with unsupported configuration (forward
* compatibility. for example). */
/* TODO(sergey): Replace with utility null-RNA-pointer creation once that is available. */
r_property_ptr->data = NULL;
r_property_ptr->type = NULL;
r_property_ptr->owner_id = NULL;
return false;
}
bool driver_get_target_property(const DriverTargetContext *driver_target_context,
DriverVar *dvar,
DriverTarget *dtar,
PointerRNA *r_prop)
{
if (dvar->type == DVAR_TYPE_CONTEXT_PROP) {
return driver_get_target_context_property(driver_target_context, dtar, r_prop);
}
if (dtar->id == NULL) {
return false;
}
RNA_id_pointer_create(dtar->id, r_prop);
return true;
}
/**
* Helper function to obtain a value using RNA from the specified source
* (for evaluating drivers).
*/
static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
static float dtar_get_prop_val(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar,
DriverTarget *dtar)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
ID *id;
int index = -1;
float value = 0.0f;
/* Sanity check. */
if (ELEM(NULL, driver, dtar)) {
if (driver == NULL) {
return 0.0f;
}
id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
/* Get property to resolve the target from.
* Naming is a bit confusing, but this is what is exposed as "Prop" or "Context Property" in
* interface. */
const DriverTargetContext driver_target_context = driver_target_context_from_animation_context(
anim_eval_context);
PointerRNA property_ptr;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &property_ptr)) {
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
}
@ -103,16 +171,18 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
/* Get RNA-pointer for the ID-block given in target. */
RNA_id_pointer_create(id, &id_ptr);
/* Get property to read from, and get value as appropriate. */
if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
PointerRNA value_ptr;
PropertyRNA *value_prop;
int index = -1;
float value = 0.0f;
if (!RNA_path_resolve_property_full(
&property_ptr, dtar->rna_path, &value_ptr, &value_prop, &index)) {
/* Path couldn't be resolved. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: cannot resolve target for %s -> %s",
id->name,
property_ptr.owner_id->name,
dtar->rna_path);
}
@ -121,14 +191,14 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
if (RNA_property_array_check(prop)) {
if (RNA_property_array_check(value_prop)) {
/* Array. */
if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) {
if (index < 0 || index >= RNA_property_array_length(&value_ptr, value_prop)) {
/* Out of bounds. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)",
id->name,
property_ptr.owner_id->name,
dtar->rna_path,
index);
}
@ -138,15 +208,15 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
switch (RNA_property_type(prop)) {
switch (RNA_property_type(value_prop)) {
case PROP_BOOLEAN:
value = (float)RNA_property_boolean_get_index(&ptr, prop, index);
value = (float)RNA_property_boolean_get_index(&value_ptr, value_prop, index);
break;
case PROP_INT:
value = (float)RNA_property_int_get_index(&ptr, prop, index);
value = (float)RNA_property_int_get_index(&value_ptr, value_prop, index);
break;
case PROP_FLOAT:
value = RNA_property_float_get_index(&ptr, prop, index);
value = RNA_property_float_get_index(&value_ptr, value_prop, index);
break;
default:
break;
@ -154,18 +224,18 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
}
else {
/* Not an array. */
switch (RNA_property_type(prop)) {
switch (RNA_property_type(value_prop)) {
case PROP_BOOLEAN:
value = (float)RNA_property_boolean_get(&ptr, prop);
value = (float)RNA_property_boolean_get(&value_ptr, value_prop);
break;
case PROP_INT:
value = (float)RNA_property_int_get(&ptr, prop);
value = (float)RNA_property_int_get(&value_ptr, value_prop);
break;
case PROP_FLOAT:
value = RNA_property_float_get(&ptr, prop);
value = RNA_property_float_get(&value_ptr, value_prop);
break;
case PROP_ENUM:
value = (float)RNA_property_enum_get(&ptr, prop);
value = (float)RNA_property_enum_get(&value_ptr, value_prop);
break;
default:
break;
@ -177,16 +247,16 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return value;
}
bool driver_get_variable_property(ChannelDriver *driver,
bool driver_get_variable_property(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar,
DriverTarget *dtar,
PointerRNA *r_ptr,
PropertyRNA **r_prop,
int *r_index)
{
PointerRNA id_ptr;
PointerRNA ptr;
PropertyRNA *prop;
ID *id;
int index = -1;
/* Sanity check. */
@ -194,10 +264,11 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
/* Get RNA-pointer for the data-block given in target. */
const DriverTargetContext driver_target_context = driver_target_context_from_animation_context(
anim_eval_context);
PointerRNA target_ptr;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &target_ptr)) {
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
}
@ -207,15 +278,12 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
/* Get RNA-pointer for the ID-block given in target. */
RNA_id_pointer_create(id, &id_ptr);
/* Get property to read from, and get value as appropriate. */
if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') {
ptr = PointerRNA_NULL;
prop = NULL; /* OK. */
}
else if (RNA_path_resolve_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
else if (RNA_path_resolve_full(&target_ptr, dtar->rna_path, &ptr, &prop, &index)) {
/* OK. */
}
else {
@ -223,7 +291,7 @@ bool driver_get_variable_property(ChannelDriver *driver,
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: cannot resolve target for %s -> %s",
id->name,
target_ptr.owner_id->name,
dtar->rna_path);
}
@ -276,14 +344,18 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
* \{ */
/* Evaluate 'single prop' driver variable. */
static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_singleProp(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar)
{
/* Just evaluate the first target slot. */
return dtar_get_prop_val(driver, &dvar->targets[0]);
return dtar_get_prop_val(anim_eval_context, driver, dvar, &dvar->targets[0]);
}
/* Evaluate 'rotation difference' driver variable. */
static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_rotDiff(const AnimationEvalContext *UNUSED(anim_eval_context),
ChannelDriver *driver,
DriverVar *dvar)
{
short valid_targets = driver_check_valid_targets(driver, dvar);
@ -344,7 +416,9 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
*
* TODO: this needs to take into account space conversions.
*/
static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_locDiff(const AnimationEvalContext *UNUSED(anim_eval_context),
ChannelDriver *driver,
DriverVar *dvar)
{
float loc1[3] = {0.0f, 0.0f, 0.0f};
float loc2[3] = {0.0f, 0.0f, 0.0f};
@ -446,7 +520,9 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/**
* Evaluate 'transform channel' driver variable.
*/
static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_transChan(const AnimationEvalContext *UNUSED(anim_eval_context),
ChannelDriver *driver,
DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
Object *ob = (Object *)dtar->id;
@ -578,6 +654,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
return mat[3][dtar->transChan];
}
/* Evaluate 'context prop' driver variable. */
static float dvar_eval_contextProp(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar)
{
/* Just evaluate the first target slot. */
return dtar_get_prop_val(anim_eval_context, driver, dvar, &dvar->targets[0]);
}
/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */
static void quaternion_to_angles(float quat[4], int channel)
{
@ -675,6 +760,12 @@ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
{"Object/Bone"}, /* UI names for targets */
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
END_DVAR_TYPEDEF,
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_CONTEXT_PROP) dvar_eval_contextProp, /* Eval callback. */
1, /* Number of targets used. */
{"Property"}, /* UI names for targets */
{0} /* Flags. */
END_DVAR_TYPEDEF,
};
/* Get driver variable typeinfo */
@ -972,7 +1063,8 @@ static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr)
return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME);
}
static bool driver_evaluate_simple_expr(ChannelDriver *driver,
static bool driver_evaluate_simple_expr(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
ExprPyLike_Parsed *expr,
float *result,
float time)
@ -985,7 +1077,7 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver,
vars[VAR_INDEX_FRAME] = time;
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
vars[i++] = driver_get_variable_value(driver, dvar);
vars[i++] = driver_get_variable_value(anim_eval_context, driver, dvar);
}
/* Evaluate expression. */
@ -1042,7 +1134,8 @@ static bool driver_compile_simple_expr(ChannelDriver *driver)
/* Try using the simple expression evaluator to compute the result of the driver.
* On success, stores the result and returns true; on failure result is set to 0. */
static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
static bool driver_try_evaluate_simple_expr(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
ChannelDriver *driver_orig,
float *result,
float time)
@ -1051,7 +1144,8 @@ static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
return driver_compile_simple_expr(driver_orig) &&
BLI_expr_pylike_is_valid(driver_orig->expr_simple) &&
driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time);
driver_evaluate_simple_expr(
anim_eval_context, driver, driver_orig->expr_simple, result, time);
}
bool BKE_driver_has_simple_expression(ChannelDriver *driver)
@ -1121,7 +1215,9 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver,
/** \name Driver Evaluation
* \{ */
float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
float driver_get_variable_value(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar)
{
const DriverVarTypeInfo *dvti;
@ -1136,7 +1232,7 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
dvti = get_dvar_typeinfo(dvar->type);
if (dvti && dvti->get_value) {
dvar->curval = dvti->get_value(driver, dvar);
dvar->curval = dvti->get_value(anim_eval_context, driver, dvar);
}
else {
dvar->curval = 0.0f;
@ -1145,7 +1241,8 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
return dvar->curval;
}
static void evaluate_driver_sum(ChannelDriver *driver)
static void evaluate_driver_sum(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver)
{
DriverVar *dvar;
@ -1153,7 +1250,7 @@ static void evaluate_driver_sum(ChannelDriver *driver)
if (BLI_listbase_is_single(&driver->variables)) {
/* Just one target, so just use that. */
dvar = driver->variables.first;
driver->curval = driver_get_variable_value(driver, dvar);
driver->curval = driver_get_variable_value(anim_eval_context, driver, dvar);
return;
}
@ -1163,7 +1260,7 @@ static void evaluate_driver_sum(ChannelDriver *driver)
/* Loop through targets, adding (hopefully we don't get any overflow!). */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
value += driver_get_variable_value(driver, dvar);
value += driver_get_variable_value(anim_eval_context, driver, dvar);
tot++;
}
@ -1176,7 +1273,8 @@ static void evaluate_driver_sum(ChannelDriver *driver)
}
}
static void evaluate_driver_min_max(ChannelDriver *driver)
static void evaluate_driver_min_max(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver)
{
DriverVar *dvar;
float value = 0.0f;
@ -1184,7 +1282,7 @@ static void evaluate_driver_min_max(ChannelDriver *driver)
/* Loop through the variables, getting the values and comparing them to existing ones. */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
/* Get value. */
float tmp_val = driver_get_variable_value(driver, dvar);
float tmp_val = driver_get_variable_value(anim_eval_context, driver, dvar);
/* Store this value if appropriate. */
if (dvar->prev) {
@ -1221,8 +1319,11 @@ static void evaluate_driver_python(PathResolvedRNA *anim_rna,
if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) {
driver->curval = 0.0f;
}
else if (!driver_try_evaluate_simple_expr(
driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) {
else if (!driver_try_evaluate_simple_expr(anim_eval_context,
driver,
driver_orig,
&driver->curval,
anim_eval_context->eval_time)) {
#ifdef WITH_PYTHON
/* This evaluates the expression using Python, and returns its result:
* - on errors it reports, then returns 0.0f. */
@ -1250,11 +1351,11 @@ float evaluate_driver(PathResolvedRNA *anim_rna,
switch (driver->type) {
case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */
case DRIVER_TYPE_SUM: /* Sum values of driver targets. */
evaluate_driver_sum(driver);
evaluate_driver_sum(anim_eval_context, driver);
break;
case DRIVER_TYPE_MIN: /* Smallest value. */
case DRIVER_TYPE_MAX: /* Largest value. */
evaluate_driver_min_max(driver);
evaluate_driver_min_max(anim_eval_context, driver);
break;
case DRIVER_TYPE_PYTHON: /* Expression. */
evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context);

View File

@ -2053,19 +2053,22 @@ bool nodeFindNodeTry(bNodeTree *ntree, bNodeSocket *sock, bNode **r_node, int *r
bNode *nodeFindRootParent(bNode *node)
{
if (node->parent) {
return nodeFindRootParent(node->parent);
bNode *parent_iter = node;
while (parent_iter->parent != nullptr) {
parent_iter = parent_iter->parent;
}
return node->type == NODE_FRAME ? node : nullptr;
if (parent_iter->type != NODE_FRAME) {
return nullptr;
}
return parent_iter;
}
bool nodeIsParentAndChild(const bNode *parent, const bNode *child)
{
if (parent == child) {
return true;
}
if (child->parent) {
return nodeIsParentAndChild(parent, child->parent);
for (const bNode *child_iter = child; child_iter; child_iter = child_iter->parent) {
if (child_iter == parent) {
return true;
}
}
return false;
}
@ -2551,26 +2554,26 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
void nodeToView(const bNode *node, const float x, const float y, float *rx, float *ry)
{
if (node->parent) {
nodeToView(node->parent, x + node->locx, y + node->locy, rx, ry);
}
else {
*rx = x + node->locx;
*ry = y + node->locy;
float mapping_x = 0.0f;
float mapping_y = 0.0f;
for (const bNode *node_iter = node; node_iter; node_iter = node_iter->parent) {
mapping_x += node_iter->locx;
mapping_y += node_iter->locy;
}
*rx = mapping_x + x;
*ry = mapping_y + y;
}
void nodeFromView(const bNode *node, const float x, const float y, float *rx, float *ry)
{
if (node->parent) {
nodeFromView(node->parent, x, y, rx, ry);
*rx -= node->locx;
*ry -= node->locy;
}
else {
*rx = x - node->locx;
*ry = y - node->locy;
float mapping_x = 0.0f;
float mapping_y = 0.0f;
for (const bNode *node_iter = node; node_iter; node_iter = node_iter->parent) {
mapping_x += node_iter->locx;
mapping_y += node_iter->locy;
}
*rx = -mapping_x + x;
*ry = -mapping_y + y;
}
void nodeAttachNode(bNodeTree *ntree, bNode *node, bNode *parent)

View File

@ -1517,10 +1517,9 @@ void BKE_tracking_marker_get_subframe_position(MovieTrackingTrack *track,
MovieTrackingMarker *marker_next = marker + 1;
if (marker_next->framenr == marker->framenr + 1) {
/* currently only do subframing inside tracked ranges, do not extrapolate tracked segments
/* Currently only do sub-framing inside tracked ranges, do not extrapolate tracked segments
* could be changed when / if mask parent would be interpolating position in-between
* tracked segments
*/
* tracked segments. */
float fac = (framenr - int(framenr)) / (marker_next->framenr - marker->framenr);

View File

@ -310,7 +310,7 @@ static bool tracking_check_marker_margin(const libmv_Marker *libmv_marker,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Autotrack context initialization.
/** \name Auto-Track Context Initialization
* \{ */
static bool autotrack_is_marker_usable(const MovieTrackingMarker *marker)

View File

@ -383,7 +383,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixe
rgb_frame = context->current_frame;
}
/* Copy the Blender pixels into the FFmpeg datastructure, taking care of endianness and flipping
/* Copy the Blender pixels into the FFMPEG data-structure, taking care of endianness and flipping
* the image vertically. */
int linesize = rgb_frame->linesize[0];
for (int y = 0; y < height; y++) {

View File

@ -6,6 +6,7 @@ set(INC
cached_resources
../../blenkernel
../../blenlib
../../blentranslation
../../gpu
../../imbuf
../../makesdna

View File

@ -1192,29 +1192,46 @@ void DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcurve, int driver_index
void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve)
{
build_driver_id_property(id, fcurve->rna_path);
PointerRNA id_ptr;
RNA_id_pointer_create(id, &id_ptr);
build_driver_id_property(id_ptr, fcurve->rna_path);
DriverTargetContext driver_target_context;
driver_target_context.scene = graph_->scene;
driver_target_context.view_layer = graph_->view_layer;
LISTBASE_FOREACH (DriverVar *, dvar, &fcurve->driver->variables) {
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
if (dtar->id == nullptr) {
PointerRNA target_prop;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &target_prop)) {
continue;
}
build_id(dtar->id);
build_driver_id_property(dtar->id, dtar->rna_path);
/* Property is always expected to be resolved to a non-null RNA property, which is always
* relative to some ID. */
BLI_assert(target_prop.owner_id);
ID *target_id = target_prop.owner_id;
build_id(target_id);
build_driver_id_property(target_prop, dtar->rna_path);
}
DRIVER_TARGETS_LOOPER_END;
}
}
void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path)
void DepsgraphNodeBuilder::build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop)
{
if (id == nullptr || rna_path == nullptr) {
if (rna_path_from_target_prop == nullptr || rna_path_from_target_prop[0] == '\0') {
return;
}
PointerRNA id_ptr, ptr;
PointerRNA ptr;
PropertyRNA *prop;
int index;
RNA_id_pointer_create(id, &id_ptr);
if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) {
if (!RNA_path_resolve_full(&target_prop, rna_path_from_target_prop, &ptr, &prop, &index)) {
return;
}
if (prop == nullptr) {

View File

@ -49,6 +49,7 @@ struct bNodeSocket;
struct bNodeTree;
struct bPoseChannel;
struct bSound;
struct PointerRNA;
namespace blender::deg {
@ -207,6 +208,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
*/
virtual void build_animation_images(ID *id);
virtual void build_action(bAction *action);
/**
* Build graph node(s) for Driver
* \param id: ID-Block that driver is attached to
@ -214,8 +216,22 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
* \param driver_index: Index in animation data drivers list
*/
virtual void build_driver(ID *id, FCurve *fcurve, int driver_index);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_id_property(ID *id, const char *rna_path);
/* Build operations of a property value from which is read by a driver target.
*
* The driver target points to a data-block (or a sub-data-block like View Layer).
* This data-block is presented in the interface as a "Prop" and its resolved RNA pointer is
* passed here as `target_prop`.
*
* The tricky part (and a bit confusing naming) is that the driver target accesses a property of
* the `target_prop` to get its value. The property which is read to give an actual target value
* is denoted by its RNA path relative to the `target_prop`. In the interface it is called "Path"
* and here it is called `rna_path_from_target_prop`. */
virtual void build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop);
virtual void build_parameters(ID *id);
virtual void build_dimensions(Object *object);
virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);

View File

@ -1743,16 +1743,30 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
fcu->rna_path ? fcu->rna_path : "",
fcu->array_index);
const char *rna_path = fcu->rna_path ? fcu->rna_path : "";
const RNAPathKey self_key(id, rna_path, RNAPointerSource::ENTRY);
DriverTargetContext driver_target_context;
driver_target_context.scene = graph_->scene;
driver_target_context.view_layer = graph_->view_layer;
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
/* Only used targets. */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
ID *target_id = dtar->id;
if (target_id == nullptr) {
PointerRNA target_prop;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &target_prop)) {
continue;
}
/* Property is always expected to be resolved to a non-null RNA property, which is always
* relative to some ID. */
BLI_assert(target_prop.owner_id);
ID *target_id = target_prop.owner_id;
build_id(target_id);
build_driver_id_property(target_id, dtar->rna_path);
build_driver_id_property(target_prop, dtar->rna_path);
Object *object = nullptr;
if (GS(target_id->name) == ID_OB) {
object = (Object *)target_id;
@ -1849,16 +1863,17 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
}
}
void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_path)
void DepsgraphRelationBuilder::build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop)
{
if (id == nullptr || rna_path == nullptr) {
if (rna_path_from_target_prop == nullptr || rna_path_from_target_prop[0] == '\0') {
return;
}
PointerRNA id_ptr, ptr;
PointerRNA ptr;
PropertyRNA *prop;
int index;
RNA_id_pointer_create(id, &id_ptr);
if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) {
if (!RNA_path_resolve_full(&target_prop, rna_path_from_target_prop, &ptr, &prop, &index)) {
return;
}
if (prop == nullptr) {

View File

@ -171,7 +171,20 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_driver(ID *id, FCurve *fcurve);
virtual void build_driver_data(ID *id, FCurve *fcurve);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_id_property(ID *id, const char *rna_path);
/* Build operations of a property value from which is read by a driver target.
*
* The driver target points to a data-block (or a sub-data-block like View Layer).
* This data-block is presented in the interface as a "Prop" and its resolved RNA pointer is
* passed here as `target_prop`.
*
* The tricky part (and a bit confusing naming) is that the driver target accesses a property of
* the `target_prop` to get its value. The property which is read to give an actual target value
* is denoted by its RNA path relative to the `target_prop`. In the interface it is called "Path"
* and here it is called `rna_path_from_target_prop`. */
virtual void build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop);
virtual void build_parameters(ID *id);
virtual void build_dimensions(Object *object);
virtual void build_world(World *world);

View File

@ -241,7 +241,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
}
/**
* Reset for each "redraw". When rendering using ogl render,
* Reset for each "redraw". When rendering using OpenGL render,
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
*/
if (DRW_state_is_opengl_render()) {

View File

@ -106,7 +106,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
/* Complementary Depth Pass */
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK;
if (show_retopology) {
/* Do not cull backfaces for retopology depth pass.
/* Do not cull back-faces for retopology depth pass.
* This prevents edit overlays from appearing behind any faces.
* Doing so reduces visual clutter. */
state &= ~DRW_STATE_CULL_BACK;
@ -162,9 +162,9 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
&pd->edit_mesh_faces_cage_grp[i];
state = state_common;
if (show_retopology) {
/* Cull backfaces for retopology face pass.
* This makes it so backfaces are not drawn.
* Doing so lets us distinguish backfaces from frontfaces. */
/* Cull back-faces for retopology face pass.
* This makes it so back-faces are not drawn.
* Doing so lets us distinguish back-faces from front-faces. */
state |= DRW_STATE_CULL_BACK;
}
DRW_PASS_CREATE(*edit_face_ps, state | pd->clipping_state);

View File

@ -577,7 +577,7 @@ static void write_render_z_output(struct RenderLayer *layer,
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert ogl depth [0..1] to view Z [near..far] */
/* Convert GPU depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(nullptr)) {
for (float &z : MutableSpan(rp->rect, pix_num)) {
if (z == 1.0f) {

View File

@ -120,7 +120,7 @@ static void workbench_render_result_z(struct RenderLayer *rl,
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert ogl depth [0..1] to view Z [near..far] */
/* Convert GPU depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(NULL)) {
for (int i = 0; i < pix_num; i++) {
if (rp->rect[i] == 1.0f) {

View File

@ -902,6 +902,29 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar
uiItemR(sub, &dtar_ptr, "transform_space", 0, IFACE_("Space"), ICON_NONE);
}
/* Settings for 'Context Property' driver variable type. */
static void graph_panel_driverVar__contextProp(uiLayout *layout, ID *id, DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
/* Initialize RNA pointer to the target. */
PointerRNA dtar_ptr;
RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr);
/* Target Property. */
{
uiLayout *row = uiLayoutRow(layout, false);
uiItemR(row, &dtar_ptr, "context_property", 0, NULL, ICON_NONE);
}
/* Target Path */
{
uiLayout *col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID));
uiTemplatePathBuilder(col, &dtar_ptr, "data_path", NULL, IFACE_("Path"));
}
}
/* ----------------------------------------------------------------- */
/* property driven by the driver - duplicates Active FCurve, but useful for clarity */
@ -1213,6 +1236,9 @@ static void graph_draw_driver_settings_panel(uiLayout *layout,
case DVAR_TYPE_TRANSFORM_CHAN: /* transform channel */
graph_panel_driverVar__transChan(box, id, dvar);
break;
case DVAR_TYPE_CONTEXT_PROP: /* context property */
graph_panel_driverVar__contextProp(box, id, dvar);
break;
}
/* 3) value of variable */

View File

@ -181,6 +181,23 @@ void ED_node_tag_update_id(ID *id)
namespace blender::ed::space_node {
static const char *node_socket_get_translation_context(const bNodeSocket &socket)
{
/* The node is not explicitly defined. */
if (socket.runtime->declaration == nullptr) {
return nullptr;
}
blender::StringRefNull translation_context = socket.runtime->declaration->translation_context;
/* Default context. */
if (translation_context.is_empty()) {
return nullptr;
}
return translation_context.data();
}
static void node_socket_add_tooltip_in_node_editor(const bNodeTree &ntree,
const bNodeSocket &sock,
uiLayout &layout);
@ -380,8 +397,14 @@ static void node_update_basis(const bContext &C,
/* Align output buttons to the right. */
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = nodeSocketLabel(socket);
socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
const char *socket_translation_context = node_socket_get_translation_context(*socket);
socket->typeinfo->draw((bContext *)&C,
row,
&sockptr,
&nodeptr,
CTX_IFACE_(socket_translation_context, socket_label));
node_socket_add_tooltip_in_node_editor(ntree, *socket, *row);
@ -514,7 +537,12 @@ static void node_update_basis(const bContext &C,
uiLayout *row = uiLayoutRow(layout, true);
const char *socket_label = nodeSocketLabel(socket);
socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
const char *socket_translation_context = node_socket_get_translation_context(*socket);
socket->typeinfo->draw((bContext *)&C,
row,
&sockptr,
&nodeptr,
CTX_IFACE_(socket_translation_context, socket_label));
node_socket_add_tooltip_in_node_editor(ntree, *socket, *row);

View File

@ -51,6 +51,7 @@ set(SRC
transform_convert_sequencer.c
transform_convert_sequencer_image.c
transform_convert_tracking.c
transform_convert_tracking_curves.c
transform_draw_cursors.c
transform_generics.c
transform_gizmo_2d.c

View File

@ -624,7 +624,9 @@ static bool transform_modal_item_poll(const wmOperator *op, int value)
return false;
}
if (value == TFM_MODAL_TRANSLATE && t->mode == TFM_TRANSLATION) {
return false;
/* The tracking transform in MovieClip has an alternate translate that modifies the offset
* of the tracks. */
return t->data_type == &TransConvertType_Tracking;
}
if (value == TFM_MODAL_ROTATE && t->mode == TFM_ROTATION) {
return false;
@ -987,16 +989,16 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->redraw |= TREDRAW_HARD;
handled = true;
}
else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
restoreTransObjects(t);
t->flag ^= T_ALT_TRANSFORM;
t->redraw |= TREDRAW_HARD;
handled = true;
}
}
else {
if (t->mode == TFM_TRANSLATION) {
if (t->data_type == &TransConvertType_Tracking) {
restoreTransObjects(t);
t->flag ^= T_ALT_TRANSFORM;
t->redraw |= TREDRAW_HARD;
handled = true;
}
break;
}
restoreTransObjects(t);

View File

@ -932,6 +932,9 @@ static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj
}
if (t->spacetype == SPACE_CLIP) {
if (t->options & CTX_MOVIECLIP) {
if (t->region->regiontype == RGN_TYPE_PREVIEW) {
return &TransConvertType_TrackingCurves;
}
return &TransConvertType_Tracking;
}
if (t->options & CTX_MASK) {

View File

@ -276,6 +276,10 @@ extern TransConvertTypeInfo TransConvertType_SequencerImage;
extern TransConvertTypeInfo TransConvertType_Tracking;
/* transform_convert_tracking_curves.c */
extern TransConvertTypeInfo TransConvertType_TrackingCurves;
#ifdef __cplusplus
}
#endif

View File

@ -9,26 +9,24 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_movieclip.h"
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
#include "BKE_tracking.h"
#include "ED_clip.h"
#include "WM_api.h"
#include "WM_types.h"
#include "transform.h"
#include "transform_convert.h"
typedef struct TransDataTracking {
int mode, flag;
int mode;
int flag;
/* tracks transformation from main window */
int area;
@ -41,18 +39,13 @@ typedef struct TransDataTracking {
int framenr;
MovieTrackingMarker *markers;
/* marker transformation from curves editor */
float *prev_pos, scale;
short coord;
MovieTrackingTrack *track;
MovieTrackingPlaneTrack *plane_track;
} TransDataTracking;
enum transDataTracking_Mode {
transDataTracking_ModeTracks = 0,
transDataTracking_ModeCurves = 1,
transDataTracking_ModePlaneTracks = 2,
transDataTracking_ModePlaneTracks = 1,
};
/* -------------------------------------------------------------------- */
@ -379,139 +372,8 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t)
}
}
static void markerToTransCurveDataInit(TransData *td,
TransData2D *td2d,
TransDataTracking *tdt,
MovieTrackingTrack *track,
MovieTrackingMarker *marker,
MovieTrackingMarker *prev_marker,
short coord,
float size)
{
float frames_delta = (marker->framenr - prev_marker->framenr);
tdt->flag = marker->flag;
marker->flag &= ~MARKER_TRACKED;
tdt->mode = transDataTracking_ModeCurves;
tdt->coord = coord;
tdt->scale = 1.0f / size * frames_delta;
tdt->prev_pos = prev_marker->pos;
tdt->track = track;
/* calculate values depending on marker's speed */
td2d->loc[0] = marker->framenr;
td2d->loc[1] = (marker->pos[coord] - prev_marker->pos[coord]) * size / frames_delta;
td2d->loc[2] = 0.0f;
td2d->loc2d = marker->pos; /* current location */
td->flag = 0;
td->loc = td2d->loc;
copy_v3_v3(td->center, td->loc);
copy_v3_v3(td->iloc, td->loc);
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
td->ext = NULL;
td->val = NULL;
td->flag |= TD_SELECTED;
td->dist = 0.0;
unit_m3(td->mtx);
unit_m3(td->smtx);
}
static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
{
TransData *td;
TransData2D *td2d;
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
TransDataTracking *tdt;
int width, height;
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* count */
tc->data_len = 0;
if ((sc->flag & SC_SHOW_GRAPH_TRACKS_MOTION) == 0) {
return;
}
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
for (int i = 1; i < track->markersnr; i++) {
const MovieTrackingMarker *marker = &track->markers[i];
const MovieTrackingMarker *prev_marker = &track->markers[i - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & MARKER_GRAPH_SEL_X) {
tc->data_len += 1;
}
if (marker->flag & MARKER_GRAPH_SEL_Y) {
tc->data_len += 1;
}
}
}
}
if (tc->data_len == 0) {
return;
}
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData");
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D),
"TransTracking TransData2D");
tc->custom.type.data = tdt = MEM_callocN(tc->data_len * sizeof(TransDataTracking),
"TransTracking TransDataTracking");
tc->custom.type.free_cb = transDataTrackingFree;
/* create actual data */
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
for (int i = 1; i < track->markersnr; i++) {
MovieTrackingMarker *marker = &track->markers[i];
MovieTrackingMarker *prev_marker = &track->markers[i - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & MARKER_GRAPH_SEL_X) {
markerToTransCurveDataInit(
td, td2d, tdt, track, marker, &track->markers[i - 1], 0, width);
td += 1;
td2d += 1;
tdt += 1;
}
if (marker->flag & MARKER_GRAPH_SEL_Y) {
markerToTransCurveDataInit(
td, td2d, tdt, track, marker, &track->markers[i - 1], 1, height);
td += 1;
td2d += 1;
tdt += 1;
}
}
}
}
}
static void createTransTrackingData(bContext *C, TransInfo *t)
{
ARegion *region = CTX_wm_region(C);
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
int width, height;
@ -530,13 +392,7 @@ static void createTransTrackingData(bContext *C, TransInfo *t)
return;
}
if (region->regiontype == RGN_TYPE_PREVIEW) {
/* transformation was called from graph editor */
createTransTrackingCurvesData(C, t);
}
else {
createTransTrackingTracksData(C, t);
}
createTransTrackingTracksData(C, t);
}
/** \} */
@ -574,24 +430,6 @@ static void cancelTransTracking(TransInfo *t)
i += 2;
}
}
else if (tdt->mode == transDataTracking_ModeCurves) {
MovieTrackingTrack *track = tdt->track;
MovieTrackingMarker *marker, *prev_marker;
int a;
for (a = 1; a < track->markersnr; a++) {
marker = &track->markers[a];
prev_marker = &track->markers[a - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & (MARKER_GRAPH_SEL_X | MARKER_GRAPH_SEL_Y)) {
marker->flag = tdt->flag;
}
}
}
else if (tdt->mode == transDataTracking_ModePlaneTracks) {
MovieTrackingPlaneTrack *plane_track = tdt->plane_track;
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track,
@ -675,9 +513,6 @@ static void flushTransTracking(TransInfo *t)
}
}
}
else if (tdt->mode == transDataTracking_ModeCurves) {
td2d->loc2d[tdt->coord] = tdt->prev_pos[tdt->coord] + td2d->loc[1] * tdt->scale;
}
else if (tdt->mode == transDataTracking_ModePlaneTracks) {
td2d->loc2d[0] = td2d->loc[0] / t->aspect[0];
td2d->loc2d[1] = td2d->loc[1] / t->aspect[1];

View File

@ -0,0 +1,301 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include "DNA_space_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_movieclip.h"
#include "BKE_node_tree_update.h"
#include "BKE_tracking.h"
#include "ED_clip.h"
#include "WM_api.h"
#include "transform.h"
#include "transform_convert.h"
typedef struct TransDataTrackingCurves {
int flag;
/* marker transformation from curves editor */
float *prev_pos;
float scale;
short coord;
MovieTrackingTrack *track;
} TransDataTrackingCurves;
/* -------------------------------------------------------------------- */
/** \name Clip Editor Motion Tracking Transform Creation
* \{ */
static void markerToTransCurveDataInit(TransData *td,
TransData2D *td2d,
TransDataTrackingCurves *tdt,
MovieTrackingTrack *track,
MovieTrackingMarker *marker,
MovieTrackingMarker *prev_marker,
short coord,
float size)
{
float frames_delta = (marker->framenr - prev_marker->framenr);
tdt->flag = marker->flag;
marker->flag &= ~MARKER_TRACKED;
tdt->coord = coord;
tdt->scale = 1.0f / size * frames_delta;
tdt->prev_pos = prev_marker->pos;
tdt->track = track;
/* calculate values depending on marker's speed */
td2d->loc[0] = marker->framenr;
td2d->loc[1] = (marker->pos[coord] - prev_marker->pos[coord]) * size / frames_delta;
td2d->loc[2] = 0.0f;
td2d->loc2d = marker->pos; /* current location */
td->flag = 0;
td->loc = td2d->loc;
copy_v3_v3(td->center, td->loc);
copy_v3_v3(td->iloc, td->loc);
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
td->ext = NULL;
td->val = NULL;
td->flag |= TD_SELECTED;
td->dist = 0.0;
unit_m3(td->mtx);
unit_m3(td->smtx);
}
static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
{
TransData *td;
TransData2D *td2d;
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
TransDataTrackingCurves *tdt;
int width, height;
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* count */
tc->data_len = 0;
if ((sc->flag & SC_SHOW_GRAPH_TRACKS_MOTION) == 0) {
return;
}
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
for (int i = 1; i < track->markersnr; i++) {
const MovieTrackingMarker *marker = &track->markers[i];
const MovieTrackingMarker *prev_marker = &track->markers[i - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & MARKER_GRAPH_SEL_X) {
tc->data_len += 1;
}
if (marker->flag & MARKER_GRAPH_SEL_Y) {
tc->data_len += 1;
}
}
}
}
if (tc->data_len == 0) {
return;
}
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData");
td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D),
"TransTracking TransData2D");
tc->custom.type.data = tdt = MEM_callocN(tc->data_len * sizeof(TransDataTrackingCurves),
"TransTracking TransDataTracking");
tc->custom.type.free_cb = NULL;
/* create actual data */
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
for (int i = 1; i < track->markersnr; i++) {
MovieTrackingMarker *marker = &track->markers[i];
MovieTrackingMarker *prev_marker = &track->markers[i - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & MARKER_GRAPH_SEL_X) {
markerToTransCurveDataInit(
td, td2d, tdt, track, marker, &track->markers[i - 1], 0, width);
td += 1;
td2d += 1;
tdt += 1;
}
if (marker->flag & MARKER_GRAPH_SEL_Y) {
markerToTransCurveDataInit(
td, td2d, tdt, track, marker, &track->markers[i - 1], 1, height);
td += 1;
td2d += 1;
tdt += 1;
}
}
}
}
}
static void createTransTrackingCurves(bContext *C, TransInfo *t)
{
ARegion *region = CTX_wm_region(C);
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
int width, height;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
tc->data_len = 0;
if (!clip) {
return;
}
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
if (width == 0 || height == 0) {
return;
}
/* transformation was called from graph editor */
BLI_assert(region->regiontype == RGN_TYPE_PREVIEW);
createTransTrackingCurvesData(C, t);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name recalc Motion Tracking TransData
* \{ */
static void cancelTransTrackingCurves(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
TransDataTrackingCurves *tdt_array = tc->custom.type.data;
int i = 0;
while (i < tc->data_len) {
TransDataTrackingCurves *tdt = &tdt_array[i];
{
MovieTrackingTrack *track = tdt->track;
MovieTrackingMarker *marker, *prev_marker;
int a;
for (a = 1; a < track->markersnr; a++) {
marker = &track->markers[a];
prev_marker = &track->markers[a - 1];
if ((marker->flag & MARKER_DISABLED) || (prev_marker->flag & MARKER_DISABLED)) {
continue;
}
if (marker->flag & (MARKER_GRAPH_SEL_X | MARKER_GRAPH_SEL_Y)) {
marker->flag = tdt->flag;
}
}
}
i++;
}
}
static void flushTransTrackingCurves(TransInfo *t)
{
TransData *td;
TransData2D *td2d;
TransDataTrackingCurves *tdt;
int td_index;
if (t->state == TRANS_CANCEL) {
cancelTransTrackingCurves(t);
}
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* flush to 2d vector from internally used 3d vector */
for (td_index = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data;
td_index < tc->data_len;
td_index++, td2d++, td++, tdt++) {
{
td2d->loc2d[tdt->coord] = tdt->prev_pos[tdt->coord] + td2d->loc[1] * tdt->scale;
}
}
}
static void recalcData_tracking_curves(TransInfo *t)
{
SpaceClip *sc = t->area->spacedata.first;
if (ED_space_clip_check_show_trackedit(sc)) {
MovieClip *clip = ED_space_clip_get_clip(sc);
flushTransTrackingCurves(t);
DEG_id_tag_update(&clip->id, 0);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Special After Transform Tracking
* \{ */
static void special_aftertrans_update__movieclip_for_curves(bContext *C, TransInfo *t)
{
SpaceClip *sc = t->area->spacedata.first;
MovieClip *clip = ED_space_clip_get_clip(sc);
if (t->scene->nodetree != NULL) {
/* Tracks can be used for stabilization nodes,
* flush update for such nodes.
*/
if (t->context != NULL) {
Main *bmain = CTX_data_main(C);
BKE_ntree_update_tag_id_changed(bmain, &clip->id);
BKE_ntree_update_main(bmain, NULL);
WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
}
}
}
/** \} */
TransConvertTypeInfo TransConvertType_TrackingCurves = {
/*flags*/ (T_POINTS | T_2D_EDIT),
/*createTransData*/ createTransTrackingCurves,
/*recalcData*/ recalcData_tracking_curves,
/*special_aftertrans_update*/ special_aftertrans_update__movieclip_for_curves,
};

View File

@ -594,7 +594,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
Object *ob,
BMEditMesh *em,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
int *r_count_failed = nullptr)
{
/* pointers to modifier data for unwrap control */
SubsurfModifierData *smd_real;
@ -666,8 +666,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
nullptr;
}
/* Prepare and feed faces to the solver */
for (const int i : subsurf_polys.index_range()) {
/* Prepare and feed faces to the solver. */
for (const int64_t i : subsurf_polys.index_range()) {
const MPoly &poly = subsurf_polys[i];
ParamKey key, vkeys[4];
bool pin[4], select[4];
@ -717,8 +717,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
blender::geometry::uv_parametrizer_face_add(handle, key, 4, vkeys, co, uv, pin, select);
}
/* these are calculated from original mesh too */
for (const int i : subsurf_edges.index_range()) {
/* These are calculated from original mesh too. */
for (const int64_t i : subsurf_edges.index_range()) {
if ((edgeMap[i] != nullptr) && BM_elem_flag_test(edgeMap[i], BM_ELEM_SEAM)) {
const MEdge *edge = &subsurf_edges[i];
ParamKey vkeys[2];
@ -728,11 +728,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
}
}
blender::geometry::uv_parametrizer_construct_end(handle,
options->fill_holes,
options->topology_from_uvs,
result_info ? &result_info->count_failed :
nullptr);
blender::geometry::uv_parametrizer_construct_end(
handle, options->fill_holes, options->topology_from_uvs, r_count_failed);
/* cleanup */
MEM_freeN(faceMap);
@ -1804,7 +1801,8 @@ static void uvedit_unwrap(const Scene *scene,
ParamHandle *handle;
if (use_subsurf) {
handle = construct_param_handle_subsurfed(scene, obedit, em, options, result_info);
handle = construct_param_handle_subsurfed(
scene, obedit, em, options, result_info ? &result_info->count_failed : nullptr);
}
else {
handle = construct_param_handle(

View File

@ -28,12 +28,12 @@ static float pack_islands_scale_margin(const Span<PackIsland *> &island_vector,
for (const int64_t index : island_vector.index_range()) {
PackIsland *island = island_vector[index];
BoxPack *box = &box_array[index];
box->index = (int)index;
box->index = int(index);
box->w = BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin;
box->h = BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin;
}
float max_u, max_v;
BLI_box_pack_2d(box_array, (int)island_vector.size(), &max_u, &max_v);
BLI_box_pack_2d(box_array, int(island_vector.size()), &max_u, &max_v);
return max_ff(max_u, max_v);
}

View File

@ -201,9 +201,11 @@ set(VULKAN_SRC
vulkan/vk_fence.cc
vulkan/vk_framebuffer.cc
vulkan/vk_index_buffer.cc
vulkan/vk_memory_layout.cc
vulkan/vk_memory.cc
vulkan/vk_pipeline.cc
vulkan/vk_pixel_buffer.cc
vulkan/vk_push_constants.cc
vulkan/vk_query.cc
vulkan/vk_shader.cc
vulkan/vk_shader_interface.cc
@ -226,9 +228,11 @@ set(VULKAN_SRC
vulkan/vk_fence.hh
vulkan/vk_framebuffer.hh
vulkan/vk_index_buffer.hh
vulkan/vk_memory_layout.hh
vulkan/vk_memory.hh
vulkan/vk_pipeline.hh
vulkan/vk_pixel_buffer.hh
vulkan/vk_push_constants.hh
vulkan/vk_query.hh
vulkan/vk_shader.hh
vulkan/vk_shader_interface.hh
@ -532,6 +536,7 @@ set(GLSL_SRC_TEST
tests/shaders/gpu_compute_ssbo_test.glsl
tests/shaders/gpu_compute_vbo_test.glsl
tests/shaders/gpu_compute_dummy_test.glsl
tests/shaders/gpu_push_constants_test.glsl
)
set(MTL_BACKEND_GLSL_SRC
@ -804,12 +809,19 @@ if(WITH_GTESTS)
tests/gpu_testing.cc
tests/gpu_index_buffer_test.cc
tests/gpu_push_constants_test.cc
tests/gpu_shader_builtin_test.cc
tests/gpu_shader_test.cc
tests/gpu_storage_buffer_test.cc
tests/gpu_testing.hh
)
if(WITH_VULKAN_BACKEND)
list(APPEND TEST_SRC
tests/memory_layout_test.cc
)
endif()
set(TEST_INC
)
set(TEST_LIB

View File

@ -313,15 +313,15 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *framebuffer, char err_out[256])
/** \} */
/* -------------------------------------------------------------------- */
/** \name Empty framebuffer
/** \name Empty frame-buffer
*
* An empty framebuffer is a framebuffer with no attachments. This allow to rasterize geometry
* An empty frame-buffer is a frame-buffer with no attachments. This allow to rasterize geometry
* without creating any dummy attachments and write some computation results using other means
* (SSBOs, Images).
* \{ */
/**
* Default size is used if the framebuffer contains no attachments.
* Default size is used if the frame-buffer contains no attachments.
* It needs to be re-specified each time an attachment is added.
*/
void GPU_framebuffer_default_size(GPUFrameBuffer *framebuffer, int width, int height);
@ -349,7 +349,7 @@ void GPU_framebuffer_viewport_set(
void GPU_framebuffer_viewport_get(GPUFrameBuffer *framebuffer, int r_viewport[4]);
/**
* Reset a framebuffer viewport bounds to its attachment(s) size.
* Reset a frame-buffer viewport bounds to its attachment(s) size.
* \note Viewport and scissor size is stored per frame-buffer.
*/
void GPU_framebuffer_viewport_reset(GPUFrameBuffer *framebuffer);
@ -361,7 +361,7 @@ void GPU_framebuffer_viewport_reset(GPUFrameBuffer *framebuffer);
* \{ */
/**
* Clear the framebuffer attachments.
* Clear the frame-buffer attachments.
* \a buffers controls the types of attachments to clear. Setting GPU_COLOR_BIT will clear *all*
* the color attachment.
* Each attachment gets cleared to the value of its type:
@ -438,19 +438,19 @@ void GPU_framebuffer_clear_color_depth_stencil(GPUFrameBuffer *fb,
void GPU_framebuffer_multi_clear(GPUFrameBuffer *framebuffer, const float (*clear_colors)[4]);
/**
* Clear all color attachment textures of the active framebuffer with the given red, green, blue,
* Clear all color attachment textures of the active frame-buffer with the given red, green, blue,
* alpha values.
* \note `GPU_write_mask`, and stencil test do not affect this command.
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
* DEPRECATED: Use `GPU_framebuffer_clear_color` with explicit framebuffer.
* DEPRECATED: Use `GPU_framebuffer_clear_color` with explicit frame-buffer.
*/
void GPU_clear_color(float red, float green, float blue, float alpha);
/**
* Clear the depth attachment texture of the active framebuffer with the given depth value.
* Clear the depth attachment texture of the active frame-buffer with the given depth value.
* \note `GPU_write_mask`, and stencil test do not affect this command.
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
* DEPRECATED: Use `GPU_framebuffer_clear_color` with explicit framebuffer.
* DEPRECATED: Use `GPU_framebuffer_clear_color` with explicit frame-buffer.
*/
void GPU_clear_depth(float depth);
@ -467,7 +467,7 @@ const char *GPU_framebuffer_get_name(GPUFrameBuffer *framebuffer);
/* -------------------------------------------------------------------- */
/** \name Python API & meta-data
*
* These are not intrinsic properties of a framebuffer but they are stored inside the
* These are not intrinsic properties of a frame-buffer but they are stored inside the
* gpu::FrameBuffer structure for tracking purpose.
* \{ */
@ -481,8 +481,8 @@ void GPU_framebuffer_py_reference_set(GPUFrameBuffer *framebuffer, void **py_ref
#endif
/**
* Keep a stack of bound framebuffer to allow scoped binding of framebuffer in python.
* This is also used by #GPUOffScreen to save/restore the current framebuffers.
* Keep a stack of bound frame-buffer to allow scoped binding of frame-buffer in python.
* This is also used by #GPUOffScreen to save/restore the current frame-buffers.
* \note This isn't thread safe.
*/
/* TODO(fclem): This has nothing to do with the GPU module and should be move to the pyGPU module.
@ -547,7 +547,7 @@ void GPU_frontbuffer_read_pixels(
* The attachments types are chosen by \a blit_buffers .
* Only one color buffer can by copied at a time and its index is chosen by \a read_slot and \a
* write_slot.
* The source and destination framebuffers dimensions have to match.
* The source and destination frame-buffers dimensions have to match.
* DEPRECATED: Prefer using `GPU_texture_copy()`.
*/
void GPU_framebuffer_blit(GPUFrameBuffer *fb_read,

View File

@ -417,7 +417,7 @@ void GPU_framebuffer_clear_color(GPUFrameBuffer *fb, const float clear_col[4])
void GPU_framebuffer_clear_depth(GPUFrameBuffer *fb, float clear_depth)
{
GPU_framebuffer_clear(fb, GPU_DEPTH_BIT, NULL, clear_depth, 0x00);
GPU_framebuffer_clear(fb, GPU_DEPTH_BIT, nullptr, clear_depth, 0x00);
}
void GPU_framebuffer_clear_color_depth(GPUFrameBuffer *fb,
@ -429,12 +429,12 @@ void GPU_framebuffer_clear_color_depth(GPUFrameBuffer *fb,
void GPU_framebuffer_clear_stencil(GPUFrameBuffer *fb, uint clear_stencil)
{
GPU_framebuffer_clear(fb, GPU_STENCIL_BIT, NULL, 0.0f, clear_stencil);
GPU_framebuffer_clear(fb, GPU_STENCIL_BIT, nullptr, 0.0f, clear_stencil);
}
void GPU_framebuffer_clear_depth_stencil(GPUFrameBuffer *fb, float clear_depth, uint clear_stencil)
{
GPU_framebuffer_clear(fb, GPU_DEPTH_BIT | GPU_STENCIL_BIT, NULL, clear_depth, clear_stencil);
GPU_framebuffer_clear(fb, GPU_DEPTH_BIT | GPU_STENCIL_BIT, nullptr, clear_depth, clear_stencil);
}
void GPU_framebuffer_clear_color_depth_stencil(GPUFrameBuffer *fb,

View File

@ -55,6 +55,36 @@ GPU_SHADER_CREATE_INFO(gpu_compute_ssbo_binding_test)
.compute_source("gpu_compute_dummy_test.glsl")
.do_static_compilation(true);
/* Push constants*/
GPU_SHADER_CREATE_INFO(gpu_push_constants_base_test)
.local_group_size(1)
.storage_buf(0, Qualifier::WRITE, "float", "data_out[]")
.compute_source("gpu_push_constants_test.glsl");
GPU_SHADER_CREATE_INFO(gpu_push_constants_test)
.additional_info("gpu_push_constants_base_test")
.push_constant(Type::FLOAT, "float_in")
.push_constant(Type::VEC2, "vec2_in")
.push_constant(Type::VEC3, "vec3_in")
.push_constant(Type::VEC4, "vec4_in")
.do_static_compilation(true);
/* Push constants size test. */
GPU_SHADER_CREATE_INFO(gpu_push_constants_128bytes_test)
.additional_info("gpu_push_constants_test")
.push_constant(Type::FLOAT, "filler", 20)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(gpu_push_constants_256bytes_test)
.additional_info("gpu_push_constants_128bytes_test")
.push_constant(Type::FLOAT, "filler2", 32)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(gpu_push_constants_512bytes_test)
.additional_info("gpu_push_constants_256bytes_test")
.push_constant(Type::FLOAT, "filler3", 64)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_shadow_test)
.fragment_source("eevee_shadow_test.glsl")
.additional_info("gpu_shader_test")

View File

@ -0,0 +1,210 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "testing/testing.h"
#include "GPU_capabilities.h"
#include "GPU_compute.h"
#include "GPU_shader.h"
#include "GPU_storage_buffer.h"
#include "BLI_math_vector.hh"
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "gpu_testing.hh"
namespace blender::gpu::tests {
struct CallData {
GPUStorageBuf *ssbo = nullptr;
Vector<float> data;
float float_in;
float2 vec2_in;
float3 vec3_in;
float4 vec4_in;
void init_ssbo(size_t num_floats)
{
if (ssbo == nullptr) {
ssbo = GPU_storagebuf_create_ex(
num_floats * sizeof(float), nullptr, GPU_USAGE_DEVICE_ONLY, __func__);
data.resize(num_floats);
}
}
~CallData()
{
if (ssbo != nullptr) {
GPU_storagebuf_free(ssbo);
ssbo = nullptr;
}
}
void generate_test_data(const float vector_mul, const float scalar_mul)
{
float_in = vector_mul;
vec2_in = float2(vector_mul * 2.0, vector_mul * 2.0 + scalar_mul);
vec3_in = float3(
vector_mul * 3.0, vector_mul * 3.0 + scalar_mul, vector_mul * 3.0 + scalar_mul * 2.0);
vec4_in = float4(vector_mul * 4.0,
vector_mul * 4.0 + scalar_mul,
vector_mul * 4.0 + scalar_mul * 2.0,
vector_mul * 4.0 + scalar_mul * 3.0);
}
void read_back()
{
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
GPU_storagebuf_read(ssbo, data.data());
}
void validate()
{
/* Check the results. */
EXPECT_EQ(data[0], float_in);
EXPECT_EQ(data[1], vec2_in.x);
EXPECT_EQ(data[2], vec2_in.y);
EXPECT_EQ(data[3], vec3_in.x);
EXPECT_EQ(data[4], vec3_in.y);
EXPECT_EQ(data[5], vec3_in.z);
EXPECT_EQ(data[6], vec4_in.x);
EXPECT_EQ(data[7], vec4_in.y);
EXPECT_EQ(data[8], vec4_in.z);
EXPECT_EQ(data[9], vec4_in.w);
}
};
struct Shader {
GPUShader *shader = nullptr;
Vector<CallData> call_datas;
~Shader()
{
if (shader != nullptr) {
GPU_shader_unbind();
GPU_shader_free(shader);
}
}
void init_shader(const char *info_name)
{
if (shader == nullptr) {
shader = GPU_shader_create_from_info_name(info_name);
EXPECT_NE(shader, nullptr);
GPU_shader_bind(shader);
}
}
CallData &new_call()
{
CallData call_data;
call_datas.append(call_data);
return call_datas.last();
}
void bind(CallData &call_data)
{
GPU_storagebuf_bind(call_data.ssbo, GPU_shader_get_ssbo_binding(shader, "data_out"));
}
void update_push_constants(const CallData &call_data)
{
GPU_shader_uniform_1f(shader, "float_in", call_data.float_in);
GPU_shader_uniform_2fv(shader, "vec2_in", call_data.vec2_in);
GPU_shader_uniform_3fv(shader, "vec3_in", call_data.vec3_in);
GPU_shader_uniform_4fv(shader, "vec4_in", call_data.vec4_in);
}
void dispatch()
{
GPU_compute_dispatch(shader, 1, 1, 1);
}
};
/** Test the given info when doing a single call. */
static void do_push_constants_test(const char *info_name, const int num_calls_simultaneously = 1)
{
if (!GPU_compute_shader_support() && !GPU_shader_storage_buffer_objects_support()) {
/* We can't test as a the platform does not support compute shaders. */
std::cout << "Skipping test: platform not supported";
return;
}
static constexpr uint SIZE = 16;
Shader shader;
shader.init_shader(info_name);
for (const int call_index : IndexRange(num_calls_simultaneously)) {
CallData &call_data = shader.new_call();
call_data.generate_test_data(call_index * 10.0, call_index * 1.0);
call_data.init_ssbo(SIZE);
shader.bind(call_data);
shader.update_push_constants(call_data);
shader.dispatch();
}
/* All calls will be "simultaneously" in flight. First read-back will wait until the dispatches
* have finished execution. */
for (const int call_index : IndexRange(num_calls_simultaneously)) {
CallData &call_data = shader.call_datas[call_index];
call_data.read_back();
call_data.validate();
}
}
/* Test case with single call as sanity check, before we make it more interesting. */
static void test_push_constants()
{
do_push_constants_test("gpu_push_constants_test");
}
GPU_TEST(push_constants)
static void test_push_constants_128bytes()
{
do_push_constants_test("gpu_push_constants_128bytes_test");
}
GPU_TEST(push_constants_128bytes)
static void test_push_constants_256bytes()
{
do_push_constants_test("gpu_push_constants_256bytes_test");
}
GPU_TEST(push_constants_256bytes)
static void test_push_constants_512bytes()
{
do_push_constants_test("gpu_push_constants_512bytes_test");
}
GPU_TEST(push_constants_512bytes)
#if 0
/* Schedule multiple simultaneously. */
/* These test have been disabled for now as this will to be solved in a separate PR.
* - `DescriptorSets` may not be altered, when they are in the command queue or being executed.
*/
static void test_push_constants_multiple()
{
do_push_constants_test("gpu_push_constants_test", 10);
}
GPU_TEST(push_constants_multiple)
static void test_push_constants_multiple_128bytes()
{
do_push_constants_test("gpu_push_constants_128bytes_test", 10);
}
GPU_TEST(push_constants_multiple_128bytes)
static void test_push_constants_multiple_256bytes()
{
do_push_constants_test("gpu_push_constants_256bytes_test", 10);
}
GPU_TEST(push_constants_multiple_256bytes)
static void test_push_constants_multiple_512bytes()
{
do_push_constants_test("gpu_push_constants_512bytes_test", 10);
}
GPU_TEST(push_constants_multiple_512bytes)
#endif
} // namespace blender::gpu::tests

View File

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "testing/testing.h"
#include "../vulkan/vk_memory_layout.hh"
namespace blender::gpu {
template<typename Layout>
static void def_attr(const shader::Type type,
const int array_size,
const uint32_t expected_alignment,
const uint32_t expected_reserve,
uint32_t *r_offset)
{
align<Layout>(type, array_size, r_offset);
EXPECT_EQ(*r_offset, expected_alignment);
reserve<Layout>(type, array_size, r_offset);
EXPECT_EQ(*r_offset, expected_reserve);
}
TEST(std140, fl)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 0, 0, 4, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 16);
}
TEST(std140, _2fl)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 0, 0, 4, &offset);
def_attr<Std140>(shader::Type::FLOAT, 0, 4, 8, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 16);
}
TEST(std140, _3fl)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 0, 0, 4, &offset);
def_attr<Std140>(shader::Type::FLOAT, 0, 4, 8, &offset);
def_attr<Std140>(shader::Type::FLOAT, 0, 8, 12, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 16);
}
TEST(std140, _4fl)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 0, 0, 4, &offset);
def_attr<Std140>(shader::Type::FLOAT, 0, 4, 8, &offset);
def_attr<Std140>(shader::Type::FLOAT, 0, 8, 12, &offset);
def_attr<Std140>(shader::Type::FLOAT, 0, 12, 16, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 16);
}
TEST(std140, fl2)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 2, 0, 32, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 32);
}
TEST(std140, fl_fl2)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 0, 0, 4, &offset);
def_attr<Std140>(shader::Type::FLOAT, 2, 16, 48, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 48);
}
TEST(std140, fl_vec2)
{
uint32_t offset = 0;
def_attr<Std140>(shader::Type::FLOAT, 0, 0, 4, &offset);
def_attr<Std140>(shader::Type::VEC2, 0, 8, 16, &offset);
align_end_of_struct<Std140>(&offset);
EXPECT_EQ(offset, 16);
}
} // namespace blender::gpu

View File

@ -0,0 +1,16 @@
void main()
{
data_out[0] = float_in;
data_out[1] = vec2_in.x;
data_out[2] = vec2_in.y;
data_out[3] = vec3_in.x;
data_out[4] = vec3_in.y;
data_out[5] = vec3_in.z;
data_out[6] = vec4_in.x;
data_out[7] = vec4_in.y;
data_out[8] = vec4_in.z;
data_out[9] = vec4_in.w;
}

View File

@ -67,6 +67,9 @@ void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_
VKCommandBuffer &command_buffer = context.command_buffer_get();
VKPipeline &pipeline = shader->pipeline_get();
VKDescriptorSet &descriptor_set = pipeline.descriptor_set_get();
VKPushConstants &push_constants = pipeline.push_constants_get();
push_constants.update(context);
descriptor_set.update(context.device_get());
command_buffer.bind(
descriptor_set, shader->vk_pipeline_layout_get(), VK_PIPELINE_BIND_POINT_COMPUTE);

View File

@ -9,6 +9,7 @@
#include "vk_buffer.hh"
#include "vk_context.hh"
#include "vk_memory.hh"
#include "vk_pipeline.hh"
#include "vk_texture.hh"
#include "BLI_assert.h"
@ -71,6 +72,20 @@ void VKCommandBuffer::bind(const VKDescriptorSet &descriptor_set,
vk_command_buffer_, bind_point, vk_pipeline_layout, 0, 1, &vk_descriptor_set, 0, 0);
}
void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
const VkPipelineLayout vk_pipeline_layout,
const VkShaderStageFlags vk_shader_stages)
{
BLI_assert(push_constants.layout_get().storage_type_get() ==
VKPushConstants::StorageType::PUSH_CONSTANTS);
vkCmdPushConstants(vk_command_buffer_,
vk_pipeline_layout,
vk_shader_stages,
push_constants.offset(),
push_constants.layout_get().size_in_bytes(),
push_constants.data());
}
void VKCommandBuffer::copy(VKBuffer &dst_buffer,
VKTexture &src_texture,
Span<VkBufferImageCopy> regions)

View File

@ -8,11 +8,15 @@
#pragma once
#include "vk_common.hh"
#include "vk_pipeline.hh"
#include "BLI_utility_mixins.hh"
namespace blender::gpu {
class VKBuffer;
class VKTexture;
class VKPushConstants;
class VKPipeline;
class VKDescriptorSet;
/** Command buffer to keep track of the life-time of a command buffer. */
class VKCommandBuffer : NonCopyable, NonMovable {
@ -33,6 +37,14 @@ class VKCommandBuffer : NonCopyable, NonMovable {
void bind(const VKDescriptorSet &descriptor_set,
const VkPipelineLayout vk_pipeline_layout,
VkPipelineBindPoint bind_point);
/**
* Add a push constant command to the command buffer.
*
* Only valid when the storage type of push_constants is StorageType::PUSH_CONSTANTS.
*/
void push_constants(const VKPushConstants &push_constants,
const VkPipelineLayout vk_pipeline_layout,
const VkShaderStageFlags vk_shader_stages);
void dispatch(int groups_x_len, int groups_y_len, int groups_z_len);
/** Copy the contents of a texture MIP level to the dst buffer. */
void copy(VKBuffer &dst_buffer, VKTexture &src_texture, Span<VkBufferImageCopy> regions);

View File

@ -15,6 +15,15 @@
#include "BLI_assert.h"
namespace blender::gpu {
VKDescriptorSet::VKDescriptorSet(VKDescriptorSet &&other)
: vk_descriptor_pool_(other.vk_descriptor_pool_),
vk_descriptor_set_(other.vk_descriptor_set_),
bindings_(std::move(other.bindings_))
{
other.mark_freed();
}
VKDescriptorSet::~VKDescriptorSet()
{
if (vk_descriptor_set_ != VK_NULL_HANDLE) {

View File

@ -111,6 +111,7 @@ class VKDescriptorSet : NonCopyable {
: vk_descriptor_pool_(vk_descriptor_pool), vk_descriptor_set_(vk_descriptor_set)
{
}
VKDescriptorSet(VKDescriptorSet &&other);
virtual ~VKDescriptorSet();
VKDescriptorSet &operator=(VKDescriptorSet &&other)

View File

@ -0,0 +1,172 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. All rights reserved. */
/** \file
* \ingroup gpu
*/
#include "vk_memory_layout.hh"
namespace blender::gpu {
uint32_t Std430::component_mem_size(const shader::Type /*type*/)
{
return 4;
}
uint32_t Std430::element_alignment(const shader::Type type, const bool is_array)
{
if (is_array) {
return 16;
}
switch (type) {
case shader::Type::FLOAT:
case shader::Type::UINT:
case shader::Type::INT:
case shader::Type::BOOL:
return 4;
case shader::Type::VEC2:
case shader::Type::UVEC2:
case shader::Type::IVEC2:
return 8;
case shader::Type::VEC3:
case shader::Type::UVEC3:
case shader::Type::IVEC3:
case shader::Type::VEC4:
case shader::Type::UVEC4:
case shader::Type::IVEC4:
case shader::Type::MAT3:
case shader::Type::MAT4:
return 16;
default:
BLI_assert_msg(false, "Type not supported in dynamic structs.");
}
return 0;
}
uint32_t Std430::element_components_len(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
case shader::Type::UINT:
case shader::Type::INT:
case shader::Type::BOOL:
return 1;
case shader::Type::VEC2:
case shader::Type::UVEC2:
case shader::Type::IVEC2:
return 2;
case shader::Type::VEC3:
case shader::Type::UVEC3:
case shader::Type::IVEC3:
case shader::Type::VEC4:
case shader::Type::UVEC4:
case shader::Type::IVEC4:
return 4;
case shader::Type::MAT3:
return 12;
case shader::Type::MAT4:
return 16;
default:
BLI_assert_msg(false, "Type not supported in dynamic structs.");
}
return 0;
}
uint32_t Std430::array_components_len(const shader::Type type)
{
return Std430::element_components_len(type);
}
uint32_t Std140::component_mem_size(const shader::Type /*type*/)
{
return 4;
}
uint32_t Std140::element_alignment(const shader::Type type, const bool is_array)
{
if (is_array) {
return 16;
}
switch (type) {
case shader::Type::FLOAT:
case shader::Type::UINT:
case shader::Type::INT:
case shader::Type::BOOL:
return 4;
case shader::Type::VEC2:
case shader::Type::UVEC2:
case shader::Type::IVEC2:
return 8;
case shader::Type::VEC3:
case shader::Type::UVEC3:
case shader::Type::IVEC3:
case shader::Type::VEC4:
case shader::Type::UVEC4:
case shader::Type::IVEC4:
case shader::Type::MAT3:
case shader::Type::MAT4:
return 16;
default:
BLI_assert_msg(false, "Type not supported in dynamic structs.");
}
return 0;
}
uint32_t Std140::element_components_len(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
case shader::Type::UINT:
case shader::Type::INT:
case shader::Type::BOOL:
return 1;
case shader::Type::VEC2:
case shader::Type::UVEC2:
case shader::Type::IVEC2:
return 2;
case shader::Type::VEC3:
case shader::Type::UVEC3:
case shader::Type::IVEC3:
case shader::Type::VEC4:
case shader::Type::UVEC4:
case shader::Type::IVEC4:
return 4;
case shader::Type::MAT3:
return 12;
case shader::Type::MAT4:
return 16;
default:
BLI_assert_msg(false, "Type not supported in dynamic structs.");
}
return 0;
}
uint32_t Std140::array_components_len(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
case shader::Type::UINT:
case shader::Type::INT:
case shader::Type::BOOL:
case shader::Type::VEC2:
case shader::Type::UVEC2:
case shader::Type::IVEC2:
case shader::Type::VEC3:
case shader::Type::UVEC3:
case shader::Type::IVEC3:
case shader::Type::VEC4:
case shader::Type::UVEC4:
case shader::Type::IVEC4:
return 4;
case shader::Type::MAT3:
return 12;
case shader::Type::MAT4:
return 16;
default:
BLI_assert_msg(false, "Type not supported in dynamic structs.");
}
return 0;
}
} // namespace blender::gpu

View File

@ -0,0 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. All rights reserved. */
/** \file
* \ingroup gpu
*/
#pragma once
#include "gpu_shader_create_info.hh"
namespace blender::gpu {
/**
* Information about alignment/components and memory size for types when using std140 layout.
*/
struct Std140 {
/** Get the memory size in bytes of a single component using by the given type.*/
static uint32_t component_mem_size(const shader::Type type);
/** Get to alignment of the given type in bytes.*/
static uint32_t element_alignment(const shader::Type type, bool is_array);
/** Get the number of components that should be allocated for the given type.*/
static uint32_t element_components_len(const shader::Type type);
/** Get the number of components of the given type when used in an array.*/
static uint32_t array_components_len(const shader::Type type);
};
/**
* Information about alignment/components and memory size for types when using std430 layout.
*/
struct Std430 {
/** Get the memory size in bytes of a single component using by the given type.*/
static uint32_t component_mem_size(const shader::Type type);
/** Get to alignment of the given type in bytes.*/
static uint32_t element_alignment(const shader::Type type, bool is_array);
/** Get the number of components that should be allocated for the given type.*/
static uint32_t element_components_len(const shader::Type type);
/** Get the number of components of the given type when used in an array.*/
static uint32_t array_components_len(const shader::Type type);
};
template<typename LayoutT> static uint32_t element_stride(const shader::Type type)
{
return LayoutT::element_components_len(type) * LayoutT::component_mem_size(type);
}
template<typename LayoutT> static uint32_t array_stride(const shader::Type type)
{
return LayoutT::array_components_len(type) * LayoutT::component_mem_size(type);
}
/**
* Move the r_offset to the next alignment where the given type+array_size can be
* reserved.
*
* 'type': The type that needs to be aligned.
* 'array_size': The array_size that needs to be aligned. (0=no array).
* 'r_offset': After the call it will point to the byte where the reservation
* can happen.
*/
template<typename LayoutT>
static void align(const shader::Type &type, const int32_t array_size, uint32_t *r_offset)
{
uint32_t alignment = LayoutT::element_alignment(type, array_size != 0);
uint32_t alignment_mask = alignment - 1;
uint32_t offset = *r_offset;
if ((offset & alignment_mask) != 0) {
offset &= ~alignment_mask;
offset += alignment;
*r_offset = offset;
}
}
/**
* Reserve space for the given type and array size.
*
* This function doesn't handle alignment this needs to be done up front by calling
* 'align<Layout>' function. Caller is responsible for this.
*
* 'type': The type that needs to be reserved.
* 'array_size': The array_size that needs to be reserved. (0=no array).
* 'r_offset': When calling needs to be pointing to the aligned location where to
* reserve space. After the call it will point to the byte just after reserved
* space.
*/
template<typename LayoutT>
static void reserve(const shader::Type type, int32_t array_size, uint32_t *r_offset)
{
uint32_t size = array_size == 0 ? element_stride<LayoutT>(type) :
array_stride<LayoutT>(type) * array_size;
*r_offset += size;
}
/**
* Update 'r_offset' to be aligned to the end of the struct.
*
* Call this function when all attributes have been added to make sure that the struct size is
* correct.
*/
template<typename LayoutT> static void align_end_of_struct(uint32_t *r_offset)
{
align<LayoutT>(shader::Type::VEC4, 0, r_offset);
}
} // namespace blender::gpu

View File

@ -11,10 +11,13 @@
namespace blender::gpu {
VKPipeline::VKPipeline(VkPipeline vk_pipeline, VKDescriptorSet &&vk_descriptor_set)
: vk_pipeline_(vk_pipeline)
VKPipeline::VKPipeline(VkPipeline vk_pipeline,
VKDescriptorSet &&descriptor_set,
VKPushConstants &&push_constants)
: vk_pipeline_(vk_pipeline),
descriptor_set_(std::move(descriptor_set)),
push_constants_(std::move(push_constants))
{
descriptor_set_ = std::move(vk_descriptor_set);
}
VKPipeline::~VKPipeline()
@ -26,10 +29,12 @@ VKPipeline::~VKPipeline()
}
}
VKPipeline VKPipeline::create_compute_pipeline(VKContext &context,
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layout)
VKPipeline VKPipeline::create_compute_pipeline(
VKContext &context,
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layout,
const VKPushConstants::Layout &push_constants_layout)
{
VK_ALLOCATION_CALLBACKS
VkDevice vk_device = context.device_get();
@ -44,15 +49,16 @@ VKPipeline VKPipeline::create_compute_pipeline(VKContext &context,
pipeline_info.layout = pipeline_layout;
pipeline_info.stage.pName = "main";
VkPipeline pipeline;
VkPipeline vk_pipeline;
if (vkCreateComputePipelines(
vk_device, nullptr, 1, &pipeline_info, vk_allocation_callbacks, &pipeline) !=
vk_device, nullptr, 1, &pipeline_info, vk_allocation_callbacks, &vk_pipeline) !=
VK_SUCCESS) {
return VKPipeline();
}
VKDescriptorSet descriptor_set = context.descriptor_pools_get().allocate(descriptor_set_layout);
return VKPipeline(pipeline, std::move(descriptor_set));
VKPushConstants push_constants(&push_constants_layout);
return VKPipeline(vk_pipeline, std::move(descriptor_set), std::move(push_constants));
}
VkPipeline VKPipeline::vk_handle() const

View File

@ -7,42 +7,55 @@
#pragma once
#include <optional>
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "vk_common.hh"
#include "vk_descriptor_set.hh"
#include "vk_push_constants.hh"
namespace blender::gpu {
class VKContext;
class VKPipeline : NonCopyable {
VKDescriptorSet descriptor_set_;
VkPipeline vk_pipeline_ = VK_NULL_HANDLE;
VKDescriptorSet descriptor_set_;
VKPushConstants push_constants_;
public:
VKPipeline() = default;
virtual ~VKPipeline();
VKPipeline(VkPipeline vk_pipeline, VKDescriptorSet &&vk_descriptor_set);
VKPipeline(VkPipeline vk_pipeline,
VKDescriptorSet &&vk_descriptor_set,
VKPushConstants &&push_constants);
VKPipeline &operator=(VKPipeline &&other)
{
vk_pipeline_ = other.vk_pipeline_;
other.vk_pipeline_ = VK_NULL_HANDLE;
descriptor_set_ = std::move(other.descriptor_set_);
push_constants_ = std::move(other.push_constants_);
return *this;
}
static VKPipeline create_compute_pipeline(VKContext &context,
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layouts);
VkPipelineLayout &pipeline_layouts,
const VKPushConstants::Layout &push_constants_layout);
VKDescriptorSet &descriptor_set_get()
{
return descriptor_set_;
}
VKPushConstants &push_constants_get()
{
return push_constants_;
}
VkPipeline vk_handle() const;
bool is_valid() const;
};

View File

@ -0,0 +1,190 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. All rights reserved. */
/** \file
* \ingroup gpu
*/
#include "vk_push_constants.hh"
#include "vk_backend.hh"
#include "vk_memory_layout.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_storage_buffer.hh"
#include "vk_uniform_buffer.hh"
namespace blender::gpu {
template<typename LayoutT>
static VKPushConstants::Layout::PushConstant init_constant(
const shader::ShaderCreateInfo::PushConst &push_constant,
const ShaderInput &shader_input,
uint32_t *r_offset)
{
align<LayoutT>(push_constant.type, push_constant.array_size, r_offset);
VKPushConstants::Layout::PushConstant layout;
layout.location = shader_input.location;
layout.type = push_constant.type;
layout.array_size = push_constant.array_size;
layout.offset = *r_offset;
reserve<LayoutT>(push_constant.type, push_constant.array_size, r_offset);
return layout;
}
template<typename LayoutT>
uint32_t struct_size(Span<shader::ShaderCreateInfo::PushConst> push_constants)
{
uint32_t offset = 0;
for (const shader::ShaderCreateInfo::PushConst &push_constant : push_constants) {
align<LayoutT>(push_constant.type, push_constant.array_size, &offset);
reserve<LayoutT>(push_constant.type, push_constant.array_size, &offset);
}
align_end_of_struct<LayoutT>(&offset);
return offset;
}
VKPushConstants::StorageType VKPushConstants::Layout::determine_storage_type(
const shader::ShaderCreateInfo &info, const VkPhysicalDeviceLimits &vk_physical_device_limits)
{
if (info.push_constants_.is_empty()) {
return StorageType::NONE;
}
uint32_t size = struct_size<Std430>(info.push_constants_);
return size <= vk_physical_device_limits.maxPushConstantsSize ? STORAGE_TYPE_DEFAULT :
STORAGE_TYPE_FALLBACK;
}
template<typename LayoutT>
void init_struct(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
Vector<VKPushConstants::Layout::PushConstant> &r_struct,
uint32_t *r_offset)
{
for (const shader::ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
const ShaderInput *shader_input = interface.uniform_get(push_constant.name.c_str());
r_struct.append(init_constant<LayoutT>(push_constant, *shader_input, r_offset));
}
align_end_of_struct<Std140>(r_offset);
}
void VKPushConstants::Layout::init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
const StorageType storage_type,
const VKDescriptorSet::Location location)
{
BLI_assert(push_constants.is_empty());
storage_type_ = storage_type;
size_in_bytes_ = 0;
if (storage_type == StorageType::UNIFORM_BUFFER) {
descriptor_set_location_ = location;
init_struct<Std140>(info, interface, push_constants, &size_in_bytes_);
}
else {
init_struct<Std430>(info, interface, push_constants, &size_in_bytes_);
}
}
const VKPushConstants::Layout::PushConstant *VKPushConstants::Layout::find(int32_t location) const
{
for (const PushConstant &push_constant : push_constants) {
if (push_constant.location == location) {
return &push_constant;
}
}
return nullptr;
}
VKPushConstants::VKPushConstants() = default;
VKPushConstants::VKPushConstants(const Layout *layout) : layout_(layout)
{
data_ = MEM_mallocN(layout->size_in_bytes(), __func__);
switch (layout_->storage_type_get()) {
case StorageType::UNIFORM_BUFFER:
uniform_buffer_ = new VKUniformBuffer(layout_->size_in_bytes(), __func__);
break;
case StorageType::PUSH_CONSTANTS:
case StorageType::NONE:
break;
}
}
VKPushConstants::VKPushConstants(VKPushConstants &&other) : layout_(other.layout_)
{
data_ = other.data_;
other.data_ = nullptr;
uniform_buffer_ = other.uniform_buffer_;
other.uniform_buffer_ = nullptr;
}
VKPushConstants::~VKPushConstants()
{
if (data_ != nullptr) {
MEM_freeN(data_);
data_ = nullptr;
}
delete uniform_buffer_;
uniform_buffer_ = nullptr;
}
VKPushConstants &VKPushConstants::operator=(VKPushConstants &&other)
{
layout_ = other.layout_;
data_ = other.data_;
other.data_ = nullptr;
uniform_buffer_ = other.uniform_buffer_;
other.uniform_buffer_ = nullptr;
return *this;
}
void VKPushConstants::update(VKContext &context)
{
VKShader *shader = static_cast<VKShader *>(context.shader);
VKCommandBuffer &command_buffer = context.command_buffer_get();
VKPipeline &pipeline = shader->pipeline_get();
BLI_assert_msg(&pipeline.push_constants_get() == this,
"Invalid state detected. Push constants doesn't belong to the active shader of "
"the given context.");
VKDescriptorSet &descriptor_set = pipeline.descriptor_set_get();
switch (layout_get().storage_type_get()) {
case VKPushConstants::StorageType::NONE:
break;
case VKPushConstants::StorageType::PUSH_CONSTANTS:
command_buffer.push_constants(*this, shader->vk_pipeline_layout_get(), VK_SHADER_STAGE_ALL);
break;
case VKPushConstants::StorageType::UNIFORM_BUFFER:
update_uniform_buffer();
descriptor_set.bind(uniform_buffer_get(), layout_get().descriptor_set_location_get());
break;
}
}
void VKPushConstants::update_uniform_buffer()
{
BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER);
BLI_assert(uniform_buffer_ != nullptr);
BLI_assert(data_ != nullptr);
uniform_buffer_->update(data_);
}
VKUniformBuffer &VKPushConstants::uniform_buffer_get()
{
BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER);
BLI_assert(uniform_buffer_ != nullptr);
return *uniform_buffer_;
}
} // namespace blender::gpu

View File

@ -0,0 +1,249 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. All rights reserved. */
/** \file
* \ingroup gpu
*
* Push constants is a way to quickly provide a small amount of uniform data to shaders. It should
* be much quicker than UBOs but a huge limitation is the size of data - spec requires 128 bytes to
* be available for a push constant range. Hardware vendors may support more, but compared to other
* means it is still very little (for example 256 bytes).
*
* Due to this size requirements we try to use push constants when it fits on the device. If it
* doesn't fit we fallback to use an uniform buffer.
*
* Shader developers are responsible to fine-tune the performance of the shader. One way to do this
* is to tailor what will be sent as a push constant to keep the push constants within the limits.
*/
#pragma once
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "gpu_shader_create_info.hh"
#include "vk_common.hh"
#include "vk_descriptor_set.hh"
namespace blender::gpu {
class VKShaderInterface;
class VKUniformBuffer;
class VKContext;
/**
* Container to store push constants in a buffer.
*
* Can handle buffers with different memory layouts (std140/std430)
* Which memory layout is used is based on the storage type.
*
* VKPushConstantsLayout only describes the buffer, an instance of this
* class can handle setting/modifying/duplicating push constants.
*
* It should also keep track of the submissions in order to reuse the allocated
* data.
*/
class VKPushConstants : NonCopyable {
public:
/** Different methods to store push constants.*/
enum class StorageType {
/** Push constants aren't in use.*/
NONE,
/** Store push constants as regular vulkan push constants.*/
PUSH_CONSTANTS,
/**
* Fallback when push constants doesn't meet the device requirements.
*/
UNIFORM_BUFFER,
};
/**
* Describe the layout of the push constants and the storage type that should be used.
*/
struct Layout {
static constexpr StorageType STORAGE_TYPE_DEFAULT = StorageType::PUSH_CONSTANTS;
static constexpr StorageType STORAGE_TYPE_FALLBACK = StorageType::UNIFORM_BUFFER;
struct PushConstant {
/* Used as lookup based on ShaderInput.*/
int32_t location;
/** Offset in the push constant data (in bytes). */
uint32_t offset;
shader::Type type;
int array_size;
};
private:
Vector<PushConstant> push_constants;
uint32_t size_in_bytes_ = 0;
StorageType storage_type_ = StorageType::NONE;
/**
* Binding index in the descriptor set when the push constants use an uniform buffer.
*/
VKDescriptorSet::Location descriptor_set_location_;
public:
/**
* Return the desired storage type that can fit the push constants of the given shader create
* info, matching the device limits.
*
* Returns:
* - StorageType::NONE: No push constants are needed.
* - StorageType::PUSH_CONSTANTS: Regular vulkan push constants can be used.
* - StorageType::UNIFORM_BUFFER: The push constants don't fit in the limits of the given
* device. A uniform buffer should be used as a fallback method.
*/
static StorageType determine_storage_type(
const shader::ShaderCreateInfo &info,
const VkPhysicalDeviceLimits &vk_physical_device_limits);
/**
* Initialize the push constants of the given shader create info with the
* binding location.
*
* interface: Uniform locations of the interface are used as lookup key.
* storage_type: The type of storage for push constants to use.
* location: When storage_type=StorageType::UNIFORM_BUFFER this contains
* the location in the descriptor set where the uniform buffer can be
* bound.
*/
void init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
StorageType storage_type,
VKDescriptorSet::Location location);
/**
* Return the storage type that is used.
*/
StorageType storage_type_get() const
{
return storage_type_;
}
/**
* Get the binding location for the uniform buffer.
*
* Only valid when storage_type=StorageType::UNIFORM_BUFFER.
*/
VKDescriptorSet::Location descriptor_set_location_get() const
{
return descriptor_set_location_;
}
/**
* Get the size needed to store the push constants.
*/
uint32_t size_in_bytes() const
{
return size_in_bytes_;
}
/**
* Find the push constant layout for the given location.
* Location = ShaderInput.location.
*/
const PushConstant *find(int32_t location) const;
};
private:
const Layout *layout_ = nullptr;
void *data_ = nullptr;
VKUniformBuffer *uniform_buffer_ = nullptr;
public:
VKPushConstants();
VKPushConstants(const Layout *layout);
VKPushConstants(VKPushConstants &&other);
virtual ~VKPushConstants();
VKPushConstants &operator=(VKPushConstants &&other);
size_t offset() const
{
return 0;
}
const Layout &layout_get() const
{
return *layout_;
}
/**
* Get the reference to the active data.
*
* Data can get inactive when push constants are modified, after being added to the command
* queue. We still keep track of the old data for reuse and make sure we don't overwrite data
* that is still not on the GPU.
*/
const void *data() const
{
return data_;
}
/**
* Modify a push constant.
*
* location: ShaderInput.location of the push constant to update.
* comp_len: number of components has the data type that is being updated.
* array_size: number of elements when an array to update. (0=no array)
* input_data: packed source data to use.
*/
template<typename T>
void push_constant_set(int32_t location,
int32_t comp_len,
int32_t array_size,
const T *input_data)
{
const Layout::PushConstant *push_constant_layout = layout_->find(location);
BLI_assert(push_constant_layout);
uint8_t *bytes = static_cast<uint8_t *>(data_);
T *dst = static_cast<T *>(static_cast<void *>(&bytes[push_constant_layout->offset]));
const bool is_tightly_std140_packed = (comp_len % 4) == 0;
if (layout_->storage_type_get() == StorageType::PUSH_CONSTANTS || array_size == 0 ||
is_tightly_std140_packed) {
BLI_assert_msg(push_constant_layout->offset + comp_len * array_size * sizeof(T) <=
layout_->size_in_bytes(),
"Tried to write outside the push constant allocated memory.");
memcpy(dst, input_data, comp_len * array_size * sizeof(T));
return;
}
/* Store elements in uniform buffer as array. In Std140 arrays have an element stride of 16
* bytes.*/
BLI_assert(sizeof(T) == 4);
const T *src = input_data;
for (const int i : IndexRange(array_size)) {
UNUSED_VARS(i);
memcpy(dst, src, comp_len * sizeof(T));
src += comp_len;
dst += 4;
}
}
/**
* Update the GPU resources with the latest push constants.
*/
void update(VKContext &context);
private:
/**
* When storage type = StorageType::UNIFORM_BUFFER use this method to update the uniform
* buffer.
*
* It must be called just before adding a draw/compute command to the command queue.
*/
void update_uniform_buffer();
/**
* Get a reference to the uniform buffer.
*
* Only valid when storage type = StorageType::UNIFORM_BUFFER.
*/
VKUniformBuffer &uniform_buffer_get();
};
} // namespace blender::gpu

View File

@ -676,7 +676,7 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
if (!finalize_descriptor_set_layouts(vk_device, *vk_interface, *info)) {
return false;
}
if (!finalize_pipeline_layout(vk_device, *info)) {
if (!finalize_pipeline_layout(vk_device, *vk_interface)) {
return false;
}
@ -695,7 +695,11 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
BLI_assert(fragment_module_ == VK_NULL_HANDLE);
BLI_assert(compute_module_ != VK_NULL_HANDLE);
compute_pipeline_ = VKPipeline::create_compute_pipeline(
*context_, compute_module_, layout_, pipeline_layout_);
*context_,
compute_module_,
layout_,
pipeline_layout_,
vk_interface->push_constants_layout_get());
result = compute_pipeline_.is_valid();
}
@ -739,17 +743,29 @@ bool VKShader::finalize_graphics_pipeline(VkDevice /*vk_device */)
}
bool VKShader::finalize_pipeline_layout(VkDevice vk_device,
const shader::ShaderCreateInfo & /*info*/)
const VKShaderInterface &shader_interface)
{
VK_ALLOCATION_CALLBACKS
const uint32_t layout_count = layout_ == VK_NULL_HANDLE ? 0 : 1;
VkPipelineLayoutCreateInfo pipeline_info = {};
VkPushConstantRange push_constant_range = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_info.flags = 0;
pipeline_info.setLayoutCount = layout_count;
pipeline_info.pSetLayouts = &layout_;
/* Setup push constants. */
const VKPushConstants::Layout &push_constants_layout =
shader_interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::PUSH_CONSTANTS) {
push_constant_range.offset = 0;
push_constant_range.size = push_constants_layout.size_in_bytes();
push_constant_range.stageFlags = VK_SHADER_STAGE_ALL;
pipeline_info.pushConstantRangeCount = 1;
pipeline_info.pPushConstantRanges = &push_constant_range;
}
if (vkCreatePipelineLayout(
vk_device, &pipeline_info, vk_allocation_callbacks, &pipeline_layout_) != VK_SUCCESS) {
return false;
@ -868,6 +884,21 @@ static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
return binding;
}
static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
const VKPushConstants::Layout &push_constants_layout)
{
BLI_assert(push_constants_layout.storage_type_get() ==
VKPushConstants::StorageType::UNIFORM_BUFFER);
VkDescriptorSetLayoutBinding binding = {};
binding.binding = push_constants_layout.descriptor_set_location_get();
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
binding.descriptorCount = 1;
binding.stageFlags = VK_SHADER_STAGE_ALL;
binding.pImmutableSamplers = nullptr;
return binding;
}
static void add_descriptor_set_layout_bindings(
const VKShaderInterface &interface,
const Vector<shader::ShaderCreateInfo::Resource> &resources,
@ -877,6 +908,12 @@ static void add_descriptor_set_layout_bindings(
const VKDescriptorSet::Location location = interface.descriptor_set_location(resource);
r_bindings.append(create_descriptor_set_layout_binding(location, resource));
}
/* Add push constants to the descriptor when push constants are stored in an uniform buffer.*/
const VKPushConstants::Layout &push_constants_layout = interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::UNIFORM_BUFFER) {
r_bindings.append(create_descriptor_set_layout_binding(push_constants_layout));
}
}
static VkDescriptorSetLayoutCreateInfo create_descriptor_set_layout(
@ -894,11 +931,19 @@ static VkDescriptorSetLayoutCreateInfo create_descriptor_set_layout(
return set_info;
}
static bool descriptor_sets_needed(const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info)
{
return !info.pass_resources_.is_empty() || !info.batch_resources_.is_empty() ||
shader_interface.push_constants_layout_get().storage_type_get() ==
VKPushConstants::StorageType::UNIFORM_BUFFER;
}
bool VKShader::finalize_descriptor_set_layouts(VkDevice vk_device,
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info)
{
if (info.pass_resources_.is_empty() && info.batch_resources_.is_empty()) {
if (!descriptor_sets_needed(shader_interface, info)) {
return true;
}
@ -958,17 +1003,14 @@ void VKShader::unbind()
}
}
void VKShader::uniform_float(int /*location*/,
int /*comp_len*/,
int /*array_size*/,
const float * /*data*/)
void VKShader::uniform_float(int location, int comp_len, int array_size, const float *data)
{
pipeline_get().push_constants_get().push_constant_set(location, comp_len, array_size, data);
}
void VKShader::uniform_int(int /*location*/,
int /*comp_len*/,
int /*array_size*/,
const int * /*data*/)
void VKShader::uniform_int(int location, int comp_len, int array_size, const int *data)
{
pipeline_get().push_constants_get().push_constant_set(location, comp_len, array_size, data);
}
std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) const
@ -993,9 +1035,19 @@ std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) co
print_resource_alias(ss, res);
}
if (!info.push_constants_.is_empty()) {
/* Push constants. */
const VKPushConstants::Layout &push_constants_layout = interface.push_constants_layout_get();
const VKPushConstants::StorageType push_constants_storage =
push_constants_layout.storage_type_get();
if (push_constants_storage != VKPushConstants::StorageType::NONE) {
ss << "\n/* Push Constants. */\n";
ss << "layout(push_constant) uniform constants\n";
if (push_constants_storage == VKPushConstants::StorageType::PUSH_CONSTANTS) {
ss << "layout(push_constant) uniform constants\n";
}
else if (push_constants_storage == VKPushConstants::StorageType::UNIFORM_BUFFER) {
ss << "layout(binding = " << push_constants_layout.descriptor_set_location_get()
<< ", std140) uniform constants\n";
}
ss << "{\n";
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
ss << " " << to_string(uniform.type) << " pc_" << uniform.name;

View File

@ -11,6 +11,7 @@
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_pipeline.hh"
#include "BLI_string_ref.hh"
@ -78,7 +79,7 @@ class VKShader : public Shader {
bool finalize_descriptor_set_layouts(VkDevice vk_device,
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info);
bool finalize_pipeline_layout(VkDevice vk_device, const shader::ShaderCreateInfo &info);
bool finalize_pipeline_layout(VkDevice vk_device, const VKShaderInterface &shader_interface);
bool finalize_graphics_pipeline(VkDevice vk_device);
bool is_graphics_shader() const

View File

@ -6,15 +6,19 @@
*/
#include "vk_shader_interface.hh"
#include "vk_context.hh"
namespace blender::gpu {
void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
{
static char PUSH_CONSTANTS_FALLBACK_NAME[] = "push_constants_fallback";
static size_t PUSH_CONSTANTS_FALLBACK_NAME_LEN = strlen(PUSH_CONSTANTS_FALLBACK_NAME);
using namespace blender::gpu::shader;
attr_len_ = 0;
uniform_len_ = 0;
uniform_len_ = info.push_constants_.size();
ssbo_len_ = 0;
ubo_len_ = 0;
image_offset_ = -1;
@ -40,6 +44,17 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
break;
}
}
/* Reserve 1 uniform buffer for push constants fallback. */
size_t names_size = info.interface_names_size_;
VKContext &context = *VKContext::get();
const VKPushConstants::StorageType push_constants_storage_type =
VKPushConstants::Layout::determine_storage_type(info, context.physical_device_limits_get());
if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
ubo_len_++;
names_size += PUSH_CONSTANTS_FALLBACK_NAME_LEN + 1;
}
/* Make sure that the image slots don't overlap with the sampler slots. */
image_offset_++;
@ -48,7 +63,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
MEM_calloc_arrayN(input_tot_len, sizeof(ShaderInput), __func__));
ShaderInput *input = inputs_;
name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer");
name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer");
uint32_t name_buffer_offset = 0;
/* Uniform blocks */
@ -59,6 +74,13 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
input++;
}
}
/* Add push constant when using uniform buffer as fallback. */
int32_t push_constants_fallback_location = -1;
if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
copy_input_name(input, PUSH_CONSTANTS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
input->location = input->binding = -1;
input++;
}
/* Images, Samplers and buffers. */
for (const ShaderCreateInfo::Resource &res : all_resources) {
@ -74,6 +96,15 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
}
}
/* Push constants. */
int32_t push_constant_location = 1024;
for (const ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
copy_input_name(input, push_constant.name, name_buffer_, name_buffer_offset);
input->location = push_constant_location++;
input->binding = -1;
input++;
}
/* Storage buffers */
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
@ -106,6 +137,17 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
const ShaderInput *input = shader_input_get(res);
descriptor_set_location_update(input, descriptor_set_location++);
}
/* Post initializing push constants.*/
/* Determine the binding location of push constants fallback buffer.*/
int32_t push_constant_descriptor_set_location = -1;
if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
push_constant_descriptor_set_location = descriptor_set_location++;
const ShaderInput *push_constant_input = ubo_get(PUSH_CONSTANTS_FALLBACK_NAME);
descriptor_set_location_update(push_constant_input, push_constants_fallback_location);
}
push_constants_layout_.init(
info, *this, push_constants_storage_type, push_constant_descriptor_set_location);
}
static int32_t shader_input_index(const ShaderInput *shader_inputs,

View File

@ -12,7 +12,9 @@
#include "gpu_shader_create_info.hh"
#include "gpu_shader_interface.hh"
#include "vk_descriptor_set.hh"
#include "BLI_array.hh"
#include "vk_push_constants.hh"
namespace blender::gpu {
class VKShaderInterface : public ShaderInterface {
@ -27,6 +29,8 @@ class VKShaderInterface : public ShaderInterface {
uint32_t image_offset_ = 0;
Array<VKDescriptorSet::Location> descriptor_set_locations_;
VKPushConstants::Layout push_constants_layout_;
public:
VKShaderInterface() = default;
@ -37,6 +41,12 @@ class VKShaderInterface : public ShaderInterface {
const VKDescriptorSet::Location descriptor_set_location(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
/** Get the Layout of the shader.*/
const VKPushConstants::Layout &push_constants_layout_get() const
{
return push_constants_layout_;
}
private:
/**
* Retrieve the shader input for the given resource.

View File

@ -23,10 +23,10 @@ namespace blender::io::ply {
std::unique_ptr<PlyData> import_ply_ascii(fstream &file, PlyHeader *header);
/**
* Loads the information from the PLY file in ASCII format to the PlyData datastructure.
* Loads the information from the PLY file in ASCII format to the #PlyData data-structure.
* \param file: The PLY file that was opened.
* \param header: The information in the PLY header.
* \return The PlyData datastructure that can be used for conversion to a Mesh.
* \return The #PlyData data-structure that can be used for conversion to a Mesh.
*/
PlyData load_ply_ascii(fstream &file, const PlyHeader *header);

View File

@ -17,15 +17,15 @@ namespace blender::io::ply {
* The function that gets called from the importer.
* \param file: The PLY file that was opened.
* \param header: The information in the PLY header.
* \return The PlyData datastructure that can be used for conversion to a Mesh.
* \return The #PlyData data-structure that can be used for conversion to a #Mesh.
*/
std::unique_ptr<PlyData> import_ply_binary(fstream &file, const PlyHeader *header);
/**
* Loads the information from the PLY file in binary format to the PlyData datastructure.
* Loads the information from the PLY file in binary format to the #PlyData data-structure.
* \param file: The PLY file that was opened.
* \param header: The information in the PLY header.
* \return The PlyData datastructure that can be used for conversion to a Mesh.
* \return The #PlyData data-structure that can be used for conversion to a Mesh.
*/
PlyData load_ply_binary(fstream &file, const PlyHeader *header);

View File

@ -12,7 +12,7 @@
namespace blender::io::ply {
/**
* Converts the PlyData datastructure to a mesh.
* Converts the #PlyData data-structure to a mesh.
* \param data: The PLY data.
* \return The mesh that can be used inside blender.
*/

View File

@ -3,6 +3,7 @@
#include "testing/testing.h"
#include "tests/blendfile_loading_base_test.h"
#include "BKE_appdir.h"
#include "BKE_blender_version.h"
#include "DEG_depsgraph.h"
@ -31,6 +32,26 @@ class PlyExportTest : public BlendfileLoadingBaseTest {
depsgraph_create(eval_mode);
return true;
}
protected:
void SetUp() override
{
BlendfileLoadingBaseTest::SetUp();
BKE_tempdir_init("");
}
void TearDown() override
{
BlendfileLoadingBaseTest::TearDown();
BKE_tempdir_session_purge();
}
std::string get_temp_ply_filename(const std::string &filename)
{
return std::string(BKE_tempdir_session()) + "/" + filename;
}
};
static std::unique_ptr<PlyData> load_cube(PLYExportParams &params)
@ -103,7 +124,7 @@ static std::vector<char> read_temp_file_in_vectorchar(const std::string &file_pa
TEST_F(PlyExportTest, WriteHeaderAscii)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = true;
_params.export_normals = false;
@ -141,7 +162,7 @@ TEST_F(PlyExportTest, WriteHeaderAscii)
TEST_F(PlyExportTest, WriteHeaderBinary)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = false;
_params.export_normals = false;
@ -179,7 +200,7 @@ TEST_F(PlyExportTest, WriteHeaderBinary)
TEST_F(PlyExportTest, WriteVerticesAscii)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = true;
_params.export_normals = false;
@ -211,7 +232,7 @@ TEST_F(PlyExportTest, WriteVerticesAscii)
TEST_F(PlyExportTest, WriteVerticesBinary)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = false;
_params.export_normals = false;
@ -253,7 +274,7 @@ TEST_F(PlyExportTest, WriteVerticesBinary)
TEST_F(PlyExportTest, WriteFacesAscii)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = true;
_params.export_normals = false;
@ -283,7 +304,7 @@ TEST_F(PlyExportTest, WriteFacesAscii)
TEST_F(PlyExportTest, WriteFacesBinary)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = false;
_params.export_normals = false;
@ -326,7 +347,7 @@ TEST_F(PlyExportTest, WriteFacesBinary)
TEST_F(PlyExportTest, WriteVertexNormalsAscii)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = true;
_params.export_normals = true;
@ -358,7 +379,7 @@ TEST_F(PlyExportTest, WriteVertexNormalsAscii)
TEST_F(PlyExportTest, WriteVertexNormalsBinary)
{
std::string filePath = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
_params.ascii_format = false;
_params.export_normals = true;

View File

@ -322,6 +322,13 @@ typedef struct DriverTarget {
short flag;
/** Type of ID-block that this target can use. */
int idtype;
/* Context-dependent property of a "Context Property" type target.
* The `rna_path` of this property is used as a target.
* This is a value of enumerator #eDriverTarget_ContextProperty. */
int context_property;
int _pad1;
} DriverTarget;
/** Driver Target flags. */
@ -386,6 +393,11 @@ typedef enum eDriverTarget_RotationMode {
DTAR_ROTMODE_EULER_MAX = DTAR_ROTMODE_EULER_ZYX,
} eDriverTarget_RotationMode;
typedef enum eDriverTarget_ContextProperty {
DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE = 0,
DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER = 1,
} eDriverTarget_ContextProperty;
/* --- */
/* maximum number of driver targets per variable */
@ -432,6 +444,8 @@ typedef enum eDriverVar_Types {
DVAR_TYPE_LOC_DIFF,
/** 'final' transform for object/bones */
DVAR_TYPE_TRANSFORM_CHAN,
/** Property within a current evaluation context */
DVAR_TYPE_CONTEXT_PROP,
/**
* Maximum number of variable types.

View File

@ -243,7 +243,7 @@ typedef struct bArmatureConstraint {
ListBase targets;
} bArmatureConstraint;
/* Single-target subobject constraints --------------------- */
/* Single-target sub-object constraints --------------------- */
/* Track To Constraint */
typedef struct bTrackToConstraint {

View File

@ -20,7 +20,7 @@
.pathlen = 100, \
.resolu = 12, \
.resolv = 12, \
.offset = 1.0, \
.offset = 0.0, \
.wordspace = 1.0, \
.spacing = 1.0f, \
.linedist = 1.0, \

View File

@ -330,7 +330,11 @@ typedef struct bNode {
int16_t custom1, custom2;
float custom3, custom4;
/** Optional link to libdata. */
/**
* Optional link to libdata.
*
* \see #bNodeType::initfunc & #bNodeType::freefunc for details on ID user-count.
*/
struct ID *id;
/** Custom data struct for node properties for storage in files. */

View File

@ -161,6 +161,20 @@ const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_driver_target_context_property_items[] = {
{DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE,
"ACTIVE_SCENE",
ICON_NONE,
"Active Scene",
"Currently evaluating scene"},
{DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER,
"ACTIVE_VIEW_LAYER",
ICON_NONE,
"Active View Layer",
"Currently evaluating view layer"},
{0, NULL, 0, NULL, NULL},
};
#ifdef RNA_RUNTIME
# include "WM_api.h"
@ -1879,6 +1893,14 @@ static void rna_def_drivertarget(BlenderRNA *brna)
RNA_def_property_enum_items(prop, prop_local_space_items);
RNA_def_property_ui_text(prop, "Transform Space", "Space in which transforms are used");
RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data");
prop = RNA_def_property(srna, "context_property", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "context_property");
RNA_def_property_enum_items(prop, rna_enum_driver_target_context_property_items);
RNA_def_property_enum_default(prop, DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE);
RNA_def_property_ui_text(
prop, "Context Property", "Type of a context-dependent data-block to access property from");
RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data");
}
static void rna_def_drivervar(BlenderRNA *brna)
@ -1907,6 +1929,11 @@ static void rna_def_drivervar(BlenderRNA *brna)
ICON_DRIVER_DISTANCE,
"Distance",
"Distance between two bones or objects"},
{DVAR_TYPE_CONTEXT_PROP,
"CONTEXT_PROP",
ICON_RNA,
"Context Property",
"Use the value from some RNA property within the current evaluation context"},
{0, NULL, 0, NULL, NULL},
};

View File

@ -16,14 +16,14 @@
*
* The main missing features in this code compared to the paper are:
*
* + No mesh evolution. The paper suggests iteratively subsurfing the
* - No mesh evolution. The paper suggests iteratively subdivision-surfacing the
* skin output and adapting the output to better conform with the
* spheres of influence surrounding each vertex.
*
* + No mesh fairing. The paper suggests re-aligning output edges to
* - No mesh fairing. The paper suggests re-aligning output edges to
* follow principal mesh curvatures.
*
* + No auxiliary balls. These would serve to influence mesh
* - No auxiliary balls. These would serve to influence mesh
* evolution, which as noted above is not implemented.
*
* The code also adds some features not present in the paper:

View File

@ -8,6 +8,8 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "BLT_translation.h"
#include "DNA_node_types.h"
struct bNode;
@ -145,6 +147,7 @@ class SocketDeclaration {
std::string name;
std::string identifier;
std::string description;
std::string translation_context;
/** Defined by whether the socket is part of the node's input or
* output socket declaration list. Included here for convenience. */
eNodeSocketInOut in_out;
@ -275,6 +278,12 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
Self &translation_context(std::string value = BLT_I18NCONTEXT_DEFAULT)
{
decl_->translation_context = std::move(value);
return *(Self *)this;
}
Self &no_muted_links(bool value = true)
{
decl_->no_mute_links = value;

View File

@ -33,7 +33,8 @@ namespace blender::nodes::node_composite_keyingscreen_cc {
static void cmp_node_keyingscreen_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Color>(N_("Screen"));
b.add_output<decl::Color>(CTX_N_(BLT_I18NCONTEXT_ID_SCREEN, "Screen"))
.translation_context(BLT_I18NCONTEXT_ID_SCREEN);
}
static void node_composit_init_keyingscreen(const bContext *C, PointerRNA *ptr)

View File

@ -688,7 +688,8 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
/* Support for any RNA data. */
#ifdef USE_RNA_AS_PYOBJECT
if (dvar->type == DVAR_TYPE_SINGLE_PROP) {
driver_arg = pyrna_driver_get_variable_value(driver, &dvar->targets[0]);
driver_arg = pyrna_driver_get_variable_value(
anim_eval_context, driver, dvar, &dvar->targets[0]);
if (driver_arg == NULL) {
driver_arg = PyFloat_FromDouble(0.0);
@ -714,7 +715,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
#endif
{
/* Try to get variable value. */
const float tval = driver_get_variable_value(driver, dvar);
const float tval = driver_get_variable_value(anim_eval_context, driver, dvar);
driver_arg = PyFloat_FromDouble((double)tval);
}

View File

@ -20,14 +20,17 @@
#include "bpy_rna_driver.h" /* own include */
PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar)
PyObject *pyrna_driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar)
{
PyObject *driver_arg = NULL;
PointerRNA ptr;
PropertyRNA *prop = NULL;
int index;
if (driver_get_variable_property(driver, dtar, &ptr, &prop, &index)) {
if (driver_get_variable_property(anim_eval_context, driver, dvar, dtar, &ptr, &prop, &index)) {
if (prop) {
if (index != -1) {
if (index < RNA_property_array_length(&ptr, prop) && index >= 0) {

View File

@ -17,7 +17,10 @@ extern "C" {
/**
* A version of #driver_get_variable_value which returns a #PyObject.
*/
PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar);
PyObject *pyrna_driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar);
PyObject *pyrna_driver_self_from_anim_rna(struct PathResolvedRNA *anim_rna);
bool pyrna_driver_is_equal_anim_rna(const struct PathResolvedRNA *anim_rna,

View File

@ -54,7 +54,7 @@ typedef struct RenderView {
float *rectf;
/* if this exists, result of composited layers */
float *rectz;
/* optional, 32 bits version of picture, used for sequencer, ogl render and image curves */
/* optional, 32 bits version of picture, used for sequencer, OpenGL render and image curves */
int *rect32;
} RenderView;
@ -105,7 +105,7 @@ typedef struct RenderResult {
/* The following rect32, rectf and rectz buffers are for temporary storage only,
* for RenderResult structs created in #RE_AcquireResultImage - which do not have RenderView */
/* optional, 32 bits version of picture, used for ogl render and image curves */
/* Optional, 32 bits version of picture, used for OpenGL render and image curves. */
int *rect32;
/* if this exists, a copy of one of layers, or result of composited layers */
float *rectf;

View File

@ -116,7 +116,7 @@ bool RE_engine_is_external(const Render *re)
bool RE_engine_is_opengl(RenderEngineType *render_type)
{
/* TODO: refine? Can we have ogl render engine without ogl render pipeline? */
/* TODO: refine? Can we have OpenGL render engine without OpenGL render pipeline? */
return (render_type->draw_engine != nullptr) &&
DRW_engine_render_support(render_type->draw_engine);
}

View File

@ -862,7 +862,7 @@ void RE_test_break_cb(Render *re, void *handle, bool (*f)(void *handle))
void RE_gl_context_create(Render *re)
{
/* Needs to be created in the main ogl thread. */
/* Needs to be created in the main OpenGL thread. */
re->gl_context = WM_opengl_context_create();
/* So we activate the window's one afterwards. */
wm_window_reset_drawable();
@ -870,7 +870,7 @@ void RE_gl_context_create(Render *re)
void RE_gl_context_destroy(Render *re)
{
/* Needs to be called from the thread which used the ogl context for rendering. */
/* Needs to be called from the thread which used the OpenGL context for rendering. */
if (re->gl_context) {
if (re->gpu_context) {
WM_opengl_context_activate(re->gl_context);

View File

@ -714,7 +714,18 @@ typedef struct wmEvent {
/** Custom data type, stylus, 6-DOF, see `wm_event_types.h`. */
short custom;
short customdata_free;
/** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */
/**
* The #wmEvent::type implies the following #wmEvent::custodata.
*
* - #EVT_ACTIONZONE_AREA / #EVT_ACTIONZONE_FULLSCREEN / #EVT_ACTIONZONE_FULLSCREEN:
* Uses #sActionzoneData.
* - #EVT_DROP: uses #ListBase of #wmDrag (also #wmEvent::custom == #EVT_DATA_DRAGDROP).
* Typically set to #wmWindowManger::drags.
* - #EVT_FILESELECT: uses #wmOperator.
* - #EVT_XR_ACTION: uses #wmXrActionData (also #wmEvent::custom == #EVT_DATA_XR).
* - #NDOF_MOTION: uses #wmNDOFMotionData (also #wmEvent::custom == #EVT_DATA_NDOF_MOTION).
* - #TIMER: uses #wmTimer (also #wmEvent::custom == #EVT_DATA_TIMER).
*/
void *customdata;
/* Previous State. */
@ -850,7 +861,7 @@ typedef enum {
/** Do not attempt to free custom-data pointer even if non-NULL. */
WM_TIMER_NO_FREE_CUSTOM_DATA = 1 << 0,
/* Internal falgs, should not be used outside of WM code. */
/* Internal flags, should not be used outside of WM code. */
/** This timer has been tagged for removal and deletion, handled by WM code to ensure timers are
* deleted in a safe context. */
WM_TIMER_TAGGED_FOR_REMOVAL = 1 << 16,

View File

@ -271,18 +271,18 @@ void WM_gizmotype_target_property_def(wmGizmoType *gzt,
int data_type,
int array_length)
{
wmGizmoPropertyType *mpt;
BLI_assert(WM_gizmotype_target_property_find(gzt, idname) == NULL);
const uint idname_size = strlen(idname) + 1;
mpt = MEM_callocN(sizeof(wmGizmoPropertyType) + idname_size, __func__);
memcpy(mpt->idname, idname, idname_size);
mpt->data_type = data_type;
mpt->array_length = array_length;
mpt->index_in_type = gzt->target_property_defs_len;
wmGizmoPropertyType *gz_prop_type = MEM_callocN(sizeof(wmGizmoPropertyType) + idname_size,
__func__);
memcpy(gz_prop_type->idname, idname, idname_size);
gz_prop_type->data_type = data_type;
gz_prop_type->array_length = array_length;
gz_prop_type->index_in_type = gzt->target_property_defs_len;
gzt->target_property_defs_len += 1;
BLI_addtail(&gzt->target_property_defs, mpt);
BLI_addtail(&gzt->target_property_defs, gz_prop_type);
}
/** \} */