WIP: Geometry Nodes: A Modeling Approach of Gizmo #108744

Closed
Iliya Katushenock wants to merge 29 commits from mod_moder:gizmos_bke_component into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
43 changed files with 529 additions and 262 deletions
Showing only changes of commit b9a3a2df34 - Show all commits

View File

@ -215,6 +215,7 @@ class GHOST_DeviceVK {
device_features.geometryShader = VK_TRUE;
device_features.dualSrcBlend = VK_TRUE;
device_features.logicOp = VK_TRUE;
device_features.imageCubeArray = VK_TRUE;
#endif
VkDeviceCreateInfo device_create_info = {};
@ -309,7 +310,7 @@ static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance,
#if STRICT_REQUIREMENTS
if (!device_vk.features.geometryShader || !device_vk.features.dualSrcBlend ||
!device_vk.features.logicOp)
!device_vk.features.logicOp || !device_vk.features.imageCubeArray)
{
continue;
}

View File

@ -109,9 +109,6 @@ class MESH_UL_vgroups(UIList):
layout.label(text="", icon_value=icon)
class MESH_UL_shape_keys(UIList):
def draw_item(self, _context, layout, _data, item, icon, active_data, _active_propname, index):
# assert(isinstance(item, bpy.types.ShapeKey))

View File

@ -2103,7 +2103,7 @@ class VIEW3D_MT_select_edit_curves(Menu):
layout.separator()
layout.operator("curves.select_random", text="Random")
layout.operator("curves.select_end", text="Endpoints")
layout.operator("curves.select_ends", text="Endpoints")
layout.operator("curves.select_linked", text="Linked")
layout.separator()
@ -2121,7 +2121,7 @@ class VIEW3D_MT_select_sculpt_curves(Menu):
layout.operator("curves.select_all", text="None").action = 'DESELECT'
layout.operator("curves.select_all", text="Invert").action = 'INVERT'
layout.operator("sculpt_curves.select_random", text="Random")
layout.operator("curves.select_end", text="Endpoints")
layout.operator("curves.select_ends", text="Endpoints")
layout.operator("sculpt_curves.select_grow", text="Grow")

View File

@ -733,7 +733,7 @@ enum {
typedef struct CustomDataTransferLayerMap {
struct CustomDataTransferLayerMap *next, *prev;
eCustomDataType data_type;
int data_type;
int mix_mode;
float mix_factor;
/** If non-NULL, array of weights, one for each dest item, replaces mix_factor. */

View File

@ -180,7 +180,6 @@ struct Mesh *BKE_mesh_new_nomain_from_curve_displist(const struct Object *ob,
bool BKE_mesh_attribute_required(const char *name);
float (*BKE_mesh_orco_verts_get(struct Object *ob))[3];
void BKE_mesh_orco_verts_transform(struct Mesh *me, float (*orco)[3], int totvert, int invert);

View File

@ -127,7 +127,7 @@ struct CornerNormalSpaceArray {
*/
Array<Array<int>> corners_by_space;
/** Whether to create the above map when calculating normals. */
bool create_corners_by_space;
bool create_corners_by_space = false;
};
void lnor_space_custom_normal_to_data(const CornerNormalSpace *lnor_space,

View File

@ -255,7 +255,7 @@ typedef struct PanelType {
char idname[BKE_ST_MAXNAME]; /* unique name */
char label[BKE_ST_MAXNAME]; /* for panel header */
char *description; /* for panel tooltip */
const char *description; /* for panel tooltip */
char translation_context[BKE_ST_MAXNAME];
char context[BKE_ST_MAXNAME]; /* for buttons window */
char category[BKE_ST_MAXNAME]; /* for category tabs */

View File

@ -4865,7 +4865,7 @@ static void customdata_data_transfer_interp_generic(const CustomDataTransferLaye
* more than 0.5 of weight. */
int best_src_idx = 0;
const eCustomDataType data_type = laymap->data_type;
const int data_type = laymap->data_type;
const int mix_mode = laymap->mix_mode;
size_t data_size;
@ -4883,7 +4883,7 @@ static void customdata_data_transfer_interp_generic(const CustomDataTransferLaye
data_size = laymap->data_size;
}
else {
const LayerTypeInfo *type_info = layerType_getInfo(data_type);
const LayerTypeInfo *type_info = layerType_getInfo(eCustomDataType(data_type));
data_size = size_t(type_info->size);
interp_cd = type_info->interp;
@ -4952,7 +4952,7 @@ static void customdata_data_transfer_interp_generic(const CustomDataTransferLaye
}
}
else if (!(int(data_type) & CD_FAKE)) {
CustomData_data_mix_value(data_type, tmp_dst, data_dst, mix_mode, mix_factor);
CustomData_data_mix_value(eCustomDataType(data_type), tmp_dst, data_dst, mix_mode, mix_factor);
}
/* Else we can do nothing by default, needs custom interp func!
* Note this is here only for sake of consistency, not expected to be used much actually? */
@ -4975,7 +4975,8 @@ void customdata_data_transfer_interp_normal_normals(const CustomDataTransferLaye
BLI_assert(weights != nullptr);
BLI_assert(count > 0);
const eCustomDataType data_type = laymap->data_type;
const eCustomDataType data_type = eCustomDataType(laymap->data_type);
BLI_assert(data_type == CD_NORMAL);
const int mix_mode = laymap->mix_mode;
SpaceTransform *space_transform = static_cast<SpaceTransform *>(laymap->interp_data);
@ -4985,8 +4986,6 @@ void customdata_data_transfer_interp_normal_normals(const CustomDataTransferLaye
float tmp_dst[3];
BLI_assert(data_type == CD_NORMAL);
if (!sources) {
/* Not supported here, abort. */
return;
@ -5007,7 +5006,7 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
MeshPairRemapItem *mapit = me_remap->items;
const int totelem = me_remap->items_num;
const eCustomDataType data_type = laymap->data_type;
const int data_type = laymap->data_type;
const void *data_src = laymap->data_src;
void *data_dst = laymap->data_dst;
@ -5036,7 +5035,7 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
data_offset = laymap->data_offset;
}
else {
const LayerTypeInfo *type_info = layerType_getInfo(data_type);
const LayerTypeInfo *type_info = layerType_getInfo(eCustomDataType(data_type));
/* NOTE: we can use 'fake' CDLayers for crease :/. */
data_size = size_t(type_info->size);

View File

@ -628,7 +628,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
if (mesh_remap_bvhtree_query_nearest(
&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist)) {
const int poly_index = looptri_polys[rayhit.index];
const int poly_index = looptri_polys[nearest.index];
if (mode == MREMAP_MODE_VERT_POLY_NEAREST) {
int index;
@ -886,7 +886,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
if (mesh_remap_bvhtree_query_nearest(&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist))
{
const int poly_index = looptri_polys[rayhit.index];
const int poly_index = looptri_polys[nearest.index];
const blender::IndexRange poly_src = polys_src[poly_index];
const int *corner_edge_src = &corner_edges_src[poly_src.start()];
int nloops = int(poly_src.size());

View File

@ -70,15 +70,8 @@ bool multiresModifier_reshapeFromObject(Depsgraph *depsgraph,
return false;
}
int num_deformed_verts;
float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(src_mesh_eval, &num_deformed_verts);
const bool result = multiresModifier_reshapeFromVertcos(
depsgraph, dst, mmd, deformed_verts, num_deformed_verts);
MEM_freeN(deformed_verts);
return result;
return multiresModifier_reshapeFromVertcos(
depsgraph, dst, mmd, BKE_mesh_vert_positions(src_mesh_eval), src_mesh_eval->totvert);
}
/** \} */

View File

@ -3449,7 +3449,6 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
EffectorWeights *clmd_effweights;
int totpoint;
int totedge;
float(*deformedVerts)[3];
bool realloc_roots;
if (!psys->clmd) {
@ -3505,12 +3504,14 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
psys->clmd->sim_parms->effector_weights = psys->part->effector_weights;
BKE_id_copy_ex(NULL, &psys->hair_in_mesh->id, (ID **)&psys->hair_out_mesh, LIB_ID_COPY_LOCALIZE);
deformedVerts = BKE_mesh_vert_coords_alloc(psys->hair_out_mesh, NULL);
clothModifier_do(
psys->clmd, sim->depsgraph, sim->scene, sim->ob, psys->hair_in_mesh, deformedVerts);
BKE_mesh_vert_coords_apply(psys->hair_out_mesh, deformedVerts);
MEM_freeN(deformedVerts);
clothModifier_do(psys->clmd,
sim->depsgraph,
sim->scene,
sim->ob,
psys->hair_in_mesh,
BKE_mesh_vert_positions_for_write(psys->hair_out_mesh));
BKE_mesh_tag_positions_changed(psys->hair_out_mesh);
/* restore cloth effector weights */
psys->clmd->sim_parms->effector_weights = clmd_effweights;
@ -5005,6 +5006,8 @@ static void particlesystem_modifiersForeachIDLink(void *user_data,
void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata)
{
ParticleTarget *pt;
LibraryForeachIDData *foreachid_data = userdata;
const int foreachid_data_flags = BKE_lib_query_foreachid_process_flags_get(foreachid_data);
func(psys, (ID **)&psys->part, userdata, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL);
func(psys, (ID **)&psys->target_ob, userdata, IDWALK_CB_NOP);
@ -5024,14 +5027,19 @@ void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func,
func(psys, (ID **)&pt->ob, userdata, IDWALK_CB_NOP);
}
/* Even though psys->part should never be NULL, this can happen as an exception during deletion.
* See ID_REMAP_SKIP/FORCE/FLAG_NEVER_NULL_USAGE in BKE_library_remap. */
if (psys->part && psys->part->phystype == PART_PHYS_BOIDS) {
/* In case `psys->part` is NULL (See ID_REMAP_SKIP/FORCE/FLAG_NEVER_NULL_USAGE in
* #BKE_library_remap), or accessing it is forbidden, always handle particles for potential boids
* data. Unfortunate, but for now there is no other proper way to do this. */
if (!(psys->part && (foreachid_data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0) ||
psys->part->phystype == PART_PHYS_BOIDS)
{
ParticleData *pa;
int p;
for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) {
func(psys, (ID **)&pa->boid->ground, userdata, IDWALK_CB_NOP);
if (pa->boid != NULL) {
func(psys, (ID **)&pa->boid->ground, userdata, IDWALK_CB_NOP);
}
}
}
}

View File

@ -513,7 +513,7 @@ static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit;
float *co = calc->vertexCos[i];
float tmp_co[3], tmp_no[3];
const float *tmp_co, *tmp_no;
float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
if (calc->invert_vgroup) {
@ -530,12 +530,12 @@ static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
/* These coordinates are deformed by vertexCos only for normal projection
* (to get correct normals) for other cases calc->verts contains undeformed coordinates and
* vertexCos should be used */
copy_v3_v3(tmp_co, calc->vert_positions[i]);
copy_v3_v3(tmp_no, calc->vert_normals[i]);
tmp_co = calc->vert_positions[i];
tmp_no = calc->vert_normals[i];
}
else {
copy_v3_v3(tmp_co, co);
copy_v3_v3(tmp_no, proj_axis);
tmp_co = co;
tmp_no = proj_axis;
}
hit->index = -1;
@ -1568,7 +1568,6 @@ void BKE_shrinkwrap_mesh_nearest_surface_deform(bContext *C, Object *ob_source,
void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
{
ShrinkwrapModifierData ssmd = {{nullptr}};
int totvert;
ssmd.target = ob_target;
ssmd.shrinkType = MOD_SHRINKWRAP_PROJECT;
@ -1581,13 +1580,11 @@ void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object
const float projLimitTolerance = 5.0f;
ssmd.projLimit = target_me->remesh_voxel_size * projLimitTolerance;
float(*vertexCos)[3] = BKE_mesh_vert_coords_alloc(src_me, &totvert);
ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;
calc.smd = &ssmd;
calc.numVerts = src_me->totvert;
calc.vertexCos = vertexCos;
calc.vertexCos = BKE_mesh_vert_positions_for_write(src_me);
calc.vert_normals = src_me->vert_normals();
calc.vgroup = -1;
calc.target = target_me;
@ -1602,7 +1599,5 @@ void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object
BKE_shrinkwrap_free_tree(&tree);
}
BKE_mesh_vert_coords_apply(src_me, vertexCos);
MEM_freeN(vertexCos);
BKE_mesh_tag_positions_changed(src_me);
}

View File

@ -45,9 +45,14 @@ class Context {
/* Get the node tree used for compositing. */
virtual const bNodeTree &get_node_tree() const = 0;
/* True if compositor should do write file outputs, false if only running for viewing. */
/* True if the compositor should write file outputs, false otherwise. */
virtual bool use_file_output() const = 0;
/* True if the compositor should write the composite output, otherwise, the compositor is assumed
* to not support the composite output and just displays its viewer output. In that case, the
* composite output will be used as a fallback viewer if no other viewer exists */
virtual bool use_composite_output() const = 0;
/* True if color management should be used for texture evaluation. */
virtual bool use_texture_color_management() const = 0;
@ -66,10 +71,14 @@ class Context {
* region. */
virtual rcti get_compositing_region() const = 0;
/* Get the texture representing the output where the result of the compositor should be
* written. This should be called by output nodes to get their target texture. */
/* Get the texture where the result of the compositor should be written. This should be called by
* the composite output node to get its target texture. */
virtual GPUTexture *get_output_texture() = 0;
/* Get the texture where the result of the compositor viewer should be written. This should be
* called by viewer output nodes to get their target texture. */
virtual GPUTexture *get_viewer_output_texture() = 0;
/* Get the texture where the given render pass is stored. This should be called by the Render
* Layer node to populate its outputs. */
virtual GPUTexture *get_input_texture(int view_layer, const char *pass_name) = 0;

View File

@ -8,6 +8,8 @@
#include "NOD_derived_node_tree.hh"
#include "COM_context.hh"
namespace blender::realtime_compositor {
using namespace nodes::derived_node_tree_types;
@ -18,6 +20,6 @@ using Schedule = VectorSet<DNode>;
/* Computes the execution schedule of the node tree. This is essentially a post-order depth first
* traversal of the node tree from the output node to the leaf input nodes, with informed order of
* traversal of dependencies based on a heuristic estimation of the number of needed buffers. */
Schedule compute_schedule(const DerivedNodeTree &tree);
Schedule compute_schedule(const Context &context, const DerivedNodeTree &tree);
} // namespace blender::realtime_compositor

View File

@ -72,7 +72,7 @@ void Evaluator::compile_and_evaluate()
return;
}
const Schedule schedule = compute_schedule(*derived_node_tree_);
const Schedule schedule = compute_schedule(context_, *derived_node_tree_);
CompileState compile_state(schedule);

View File

@ -13,6 +13,7 @@
#include "BKE_node.hh"
#include "BKE_node_runtime.hh"
#include "COM_context.hh"
#include "COM_scheduler.hh"
#include "COM_utilities.hh"
@ -72,55 +73,88 @@ static const DTreeContext *find_active_context(const DerivedNodeTree &tree)
return find_active_context_recursive(&tree.root_context(), NODE_INSTANCE_KEY_BASE);
}
/* Return the output node which is marked as NODE_DO_OUTPUT. If multiple types of output nodes are
* marked, then the preference will be CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER > CMP_NODE_COMPOSITE.
* If no output node exists, a null node will be returned. */
static DNode find_output_in_context(const DTreeContext *context)
/* Add the viewer node which is marked as NODE_DO_OUTPUT in the given context to the given stack.
* If multiple types of viewer nodes are marked, then the preference will be CMP_NODE_VIEWER >
* CMP_NODE_SPLITVIEWER. If no viewer nodes were found, composite nodes can be added as a fallback
* viewer node. */
static bool add_viewer_nodes_in_context(const DTreeContext *context, Stack<DNode> &node_stack)
{
const bNodeTree &tree = context->btree();
for (const bNode *node : tree.nodes_by_type("CompositorNodeViewer")) {
for (const bNode *node : context->btree().nodes_by_type("CompositorNodeViewer")) {
if (node->flag & NODE_DO_OUTPUT) {
return DNode(context, node);
node_stack.push(DNode(context, node));
return true;
}
}
for (const bNode *node : tree.nodes_by_type("CompositorNodeSplitViewer")) {
for (const bNode *node : context->btree().nodes_by_type("CompositorNodeSplitViewer")) {
if (node->flag & NODE_DO_OUTPUT) {
return DNode(context, node);
node_stack.push(DNode(context, node));
return true;
}
}
for (const bNode *node : tree.nodes_by_type("CompositorNodeComposite")) {
/* The active Composite node was already added, no need to add it again, see the next block. */
if (!node_stack.is_empty() && node_stack.peek()->type == CMP_NODE_COMPOSITE) {
return false;
}
/* No active viewers exist in this context, try to add the Composite node as a fallback viewer if
* it was not already added. */
for (const bNode *node : context->btree().nodes_by_type("CompositorNodeComposite")) {
if (node->flag & NODE_DO_OUTPUT) {
return DNode(context, node);
node_stack.push(DNode(context, node));
return true;
}
}
return DNode();
return false;
}
/* Compute the output node whose result should be computed. This node is the output node that
* satisfies the requirements in the find_output_in_context function. First, the active context is
* searched for an output node, if non was found, the root context is search. For more information
* on what contexts mean here, see the find_active_context function. */
static DNode compute_output_node(const DerivedNodeTree &tree)
/* Add the output nodes whose result should be computed to the given stack. This includes File
* Output, Composite, and Viewer nodes. Viewer nodes are a special case, as only the nodes that
* satisfies the requirements in the add_viewer_nodes_in_context function are added. First, the
* active context is searched for viewer nodes, if non were found, the root context is searched.
* For more information on what contexts mean here, see the find_active_context function. */
static void add_output_nodes(const Context &context,
const DerivedNodeTree &tree,
Stack<DNode> &node_stack)
{
const DTreeContext &root_context = tree.root_context();
/* Only add File Output nodes if the context supports them. */
if (context.use_file_output()) {
for (const bNode *node : root_context.btree().nodes_by_type("CompositorNodeOutputFile")) {
node_stack.push(DNode(&root_context, node));
}
}
/* Only add the Composite output node if the context supports composite outputs. The active
* Composite node may still be added as a fallback viewer output below. */
if (context.use_composite_output()) {
for (const bNode *node : root_context.btree().nodes_by_type("CompositorNodeComposite")) {
if (node->flag & NODE_DO_OUTPUT) {
node_stack.push(DNode(&root_context, node));
break;
}
}
}
const DTreeContext *active_context = find_active_context(tree);
const bool viewer_was_added = add_viewer_nodes_in_context(active_context, node_stack);
const DNode node = find_output_in_context(active_context);
if (node) {
return node;
/* An active viewer was added, no need to search further. */
if (viewer_was_added) {
return;
}
/* If the active context is the root one and no output node was found, we consider this node tree
* to have no output node, even if one of the non-active descendants have an output node. */
/* If the active context is the root one and no viewer nodes were found, we consider this node
* tree to have no viewer nodes, even if one of the non-active descendants have viewer nodes. */
if (active_context->is_root()) {
return DNode();
return;
}
/* The active context doesn't have an output node, search in the root context as a fallback. */
return find_output_in_context(&tree.root_context());
/* The active context doesn't have a viewer node, search in the root context as a fallback. */
add_viewer_nodes_in_context(&tree.root_context(), node_stack);
}
/* A type representing a mapping that associates each node with a heuristic estimation of the
@ -177,12 +211,12 @@ using NeededBuffers = Map<DNode, int>;
* implementation because it rarely affects the output and is done by very few nodes.
* - The compiler may decide to compiler the schedule differently depending on runtime information
* which we can merely speculate at scheduling-time as described above. */
static NeededBuffers compute_number_of_needed_buffers(DNode output_node)
static NeededBuffers compute_number_of_needed_buffers(Stack<DNode> &output_nodes)
{
NeededBuffers needed_buffers;
/* A stack of nodes used to traverse the node tree starting from the output node. */
Stack<DNode> node_stack = {output_node};
/* A stack of nodes used to traverse the node tree starting from the output nodes. */
Stack<DNode> node_stack = output_nodes;
/* Traverse the node tree in a post order depth first manner and compute the number of needed
* buffers for each node. Post order traversal guarantee that all the node dependencies of each
@ -301,23 +335,23 @@ static NeededBuffers compute_number_of_needed_buffers(DNode output_node)
* doesn't always guarantee an optimal evaluation order, as the optimal evaluation order is very
* difficult to compute, however, this method works well in most cases. Moreover it assumes that
* all buffers will have roughly the same size, which may not always be the case. */
Schedule compute_schedule(const DerivedNodeTree &tree)
Schedule compute_schedule(const Context &context, const DerivedNodeTree &tree)
{
Schedule schedule;
/* Compute the output node whose result should be computed. */
const DNode output_node = compute_output_node(tree);
/* A stack of nodes used to traverse the node tree starting from the output nodes. */
Stack<DNode> node_stack;
/* No output node, the node tree has no effect, return an empty schedule. */
if (!output_node) {
/* Add the output nodes whose result should be computed to the stack. */
add_output_nodes(context, tree, node_stack);
/* No output nodes, the node tree has no effect, return an empty schedule. */
if (node_stack.is_empty()) {
return schedule;
}
/* Compute the number of buffers needed by each node connected to the output. */
const NeededBuffers needed_buffers = compute_number_of_needed_buffers(output_node);
/* A stack of nodes used to traverse the node tree starting from the output node. */
Stack<DNode> node_stack = {output_node};
/* Compute the number of buffers needed by each node connected to the outputs. */
const NeededBuffers needed_buffers = compute_number_of_needed_buffers(node_stack);
/* Traverse the node tree in a post order depth first manner, scheduling the nodes in an order
* informed by the number of buffers needed by each node. Post order traversal guarantee that all

View File

@ -10,7 +10,19 @@ GPU_SHADER_CREATE_INFO(compositor_read_pass_shared)
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.compute_source("compositor_read_pass.glsl");
GPU_SHADER_CREATE_INFO(compositor_read_pass)
GPU_SHADER_CREATE_INFO(compositor_read_pass_float)
.additional_info("compositor_read_pass_shared")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("READ_EXPRESSION(pass_color)", "vec4(pass_color.r, vec3(0.0))")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_read_pass_vector)
.additional_info("compositor_read_pass_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("READ_EXPRESSION(pass_color)", "pass_color")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_read_pass_color)
.additional_info("compositor_read_pass_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("READ_EXPRESSION(pass_color)", "pass_color")

View File

@ -21,6 +21,7 @@
#include "BLI_utildefines.h"
#include "BKE_action.h"
#include "BKE_collection.h"
#include "RNA_prototypes.h"
@ -183,6 +184,14 @@ void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
flag |= ID_RECALC_NTREE_OUTPUT;
}
}
else {
/* Collection content might have changed (children collection might have been added or
* removed from the graph based on their inclusion and visibility flags). */
const ID_Type id_type = GS(id_node->id_cow->name);
if (id_type == ID_GR) {
BKE_collection_object_cache_free(reinterpret_cast<Collection *>(id_node->id_cow));
}
}
/* Restore recalc flags from original ID, which could possibly contain recalc flags set by
* an operator and then were carried on by the undo system. */
flag |= id_orig->recalc;

View File

@ -68,6 +68,14 @@ class Context : public realtime_compositor::Context {
return false;
}
/* The viewport compositor doesn't really support the composite output, it only displays the
* viewer output in the viewport. Settings this to false will make the compositor use the
* composite output as fallback viewer if no other viewer exists. */
bool use_composite_output() const override
{
return false;
}
bool use_texture_color_management() const override
{
return BKE_scene_check_color_management_enabled(DRW_context_state_get()->scene);
@ -145,6 +153,11 @@ class Context : public realtime_compositor::Context {
return DRW_viewport_texture_list_get()->color;
}
GPUTexture *get_viewer_output_texture() override
{
return DRW_viewport_texture_list_get()->color;
}
GPUTexture *get_input_texture(int view_layer, const char *pass_name) override
{
if (view_layer == 0 && STREQ(pass_name, RE_PASSNAME_COMBINED)) {

View File

@ -34,23 +34,7 @@ void ED_asset_handle_get_full_library_path(
/* `1024` for #FILE_MAX,
* rely on warnings to let us know if this gets out of sync. */
char r_full_lib_path[1024]);
bool ED_asset_handle_get_use_relative_path(const struct AssetHandle *asset);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
# include <optional>
# include "BLI_string_ref.hh"
/** The asset library may have an import method (e.g. append vs. link) defined to use. If so, this
* returns it. Otherwise a reasonable method should be used, usually "Append (Reuse Data)". */
std::optional<eAssetImportMethod> ED_asset_handle_get_import_method(
const struct AssetHandle *asset);
blender::StringRefNull ED_asset_handle_get_library_relative_identifier(const AssetHandle &asset);
#endif

View File

@ -24,7 +24,7 @@ bool ED_asset_filter_matches_asset(const AssetFilterSettings *filter,
ID_Type asset_type = asset.get_id_type();
uint64_t asset_id_filter = BKE_idtype_idcode_to_idfilter(asset_type);
if ((filter->id_types & asset_id_filter) == 0) {
if (filter->id_types && (filter->id_types & asset_id_filter) == 0) {
return false;
}
/* Not very efficient (O(n^2)), could be improved quite a bit. */

View File

@ -51,17 +51,6 @@ int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset)
return asset->file_data->preview_icon_id;
}
std::optional<eAssetImportMethod> ED_asset_handle_get_import_method(
const AssetHandle *asset_handle)
{
return AS_asset_representation_import_method_get(asset_handle->file_data->asset);
}
blender::StringRefNull ED_asset_handle_get_library_relative_identifier(const AssetHandle &asset)
{
return AS_asset_representation_library_relative_identifier_get(asset.file_data->asset);
}
void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle,
char r_full_lib_path[FILE_MAX])
{
@ -75,8 +64,3 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle,
BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX);
}
bool ED_asset_handle_get_use_relative_path(const AssetHandle *asset)
{
return AS_asset_representation_use_relative_path_get(asset->file_data->asset);
}

View File

@ -16,6 +16,8 @@
#include "BLI_utildefines.h"
#include "BLI_vector_set.hh"
#include "BLT_translation.h"
#include "ED_curves.h"
#include "ED_object.h"
#include "ED_screen.h"
@ -938,15 +940,15 @@ static void CURVES_OT_select_random(wmOperatorType *ot)
1.0f);
}
static int select_end_exec(bContext *C, wmOperator *op)
static int select_ends_exec(bContext *C, wmOperator *op)
{
VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
const bool end_points = RNA_boolean_get(op->ptr, "end_points");
const int amount = RNA_int_get(op->ptr, "amount");
const int amount_start = RNA_int_get(op->ptr, "amount_start");
const int amount_end = RNA_int_get(op->ptr, "amount_end");
for (Curves *curves_id : unique_curves) {
CurvesGeometry &curves = curves_id->geometry.wrap();
select_ends(curves, amount, end_points);
select_ends(curves, amount_start, amount_end);
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
* attribute for now. */
@ -957,24 +959,48 @@ static int select_end_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static void CURVES_OT_select_end(wmOperatorType *ot)
static void select_ends_ui(bContext * /*C*/, wmOperator *op)
{
ot->name = "Select End";
uiLayout *layout = op->layout;
uiLayoutSetPropSep(layout, true);
uiLayout *col = uiLayoutColumn(layout, true);
uiLayoutSetPropDecorate(col, false);
uiItemR(col, op->ptr, "amount_start", 0, IFACE_("Amount Start"), ICON_NONE);
uiItemR(col, op->ptr, "amount_end", 0, IFACE_("End"), ICON_NONE);
}
static void CURVES_OT_select_ends(wmOperatorType *ot)
{
ot->name = "Select Ends";
ot->idname = __func__;
ot->description = "Select end points of curves";
ot->exec = select_end_exec;
ot->exec = select_ends_exec;
ot->ui = select_ends_ui;
ot->poll = editable_curves_point_domain_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"end_points",
true,
"End Points",
"Select points at the end of the curve as opposed to the beginning");
RNA_def_int(
ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX);
RNA_def_int(ot->srna,
"amount_start",
0,
0,
INT32_MAX,
"Amount Front",
"Number of points to select from the front",
0,
INT32_MAX);
RNA_def_int(ot->srna,
"amount_end",
1,
0,
INT32_MAX,
"Amount Back",
"Number of points to select from the back",
0,
INT32_MAX);
}
static int select_linked_exec(bContext *C, wmOperator * /*op*/)
@ -1181,7 +1207,7 @@ void ED_operatortypes_curves()
WM_operatortype_append(CURVES_OT_set_selection_domain);
WM_operatortype_append(CURVES_OT_select_all);
WM_operatortype_append(CURVES_OT_select_random);
WM_operatortype_append(CURVES_OT_select_end);
WM_operatortype_append(CURVES_OT_select_ends);
WM_operatortype_append(CURVES_OT_select_linked);
WM_operatortype_append(CURVES_OT_select_more);
WM_operatortype_append(CURVES_OT_select_less);

View File

@ -222,7 +222,7 @@ void select_all(bke::CurvesGeometry &curves, const eAttrDomain selection_domain,
}
}
void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points)
void select_ends(bke::CurvesGeometry &curves, int amount_start, int amount_end)
{
const bool was_anything_selected = has_anything_selected(curves);
const OffsetIndices points_by_curve = curves.points_by_curve();
@ -240,12 +240,9 @@ void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points)
MutableSpan<T> selection_typed = selection.span.typed<T>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
if (end_points) {
selection_typed.slice(points_by_curve[curve_i].drop_back(amount)).fill(T(0));
}
else {
selection_typed.slice(points_by_curve[curve_i].drop_front(amount)).fill(T(0));
}
selection_typed
.slice(points_by_curve[curve_i].drop_front(amount_start).drop_back(amount_end))
.fill(T(0));
}
});
}

View File

@ -150,10 +150,10 @@ void select_all(bke::CurvesGeometry &curves, eAttrDomain selection_domain, int a
/**
* Select the ends (front or back) of all the curves.
*
* \param amount: The amount of points to select from the front or back.
* \param end_points: If true, select the last point(s), if false, select the first point(s).
* \param amount_start: The amount of points to select from the front.
* \param amount_end: The amount of points to select from the back.
*/
void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points);
void select_ends(bke::CurvesGeometry &curves, int amount_start, int amount_end);
/**
* Select the points of all curves that have at least one point selected.

View File

@ -1056,7 +1056,6 @@ void ED_view3d_check_mats_rv3d(struct RegionView3D *rv3d);
struct RV3DMatrixStore *ED_view3d_mats_rv3d_backup(struct RegionView3D *rv3d);
void ED_view3d_mats_rv3d_restore(struct RegionView3D *rv3d, struct RV3DMatrixStore *rv3dmat);
struct RenderEngineType *ED_view3d_engine_type(const struct Scene *scene, int drawtype);
bool ED_view3d_context_activate(struct bContext *C);

View File

@ -226,8 +226,6 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_vertex_weight_normalize_active_vertex);
WM_operatortype_append(OBJECT_OT_vertex_weight_copy);
WM_operatortype_append(TRANSFORM_OT_vertex_warp);
WM_operatortype_append(OBJECT_OT_move_to_collection);

View File

@ -4,6 +4,7 @@
#include "testing/testing.h"
#include "GPU_context.h"
#include "GPU_framebuffer.h"
#include "gpu_testing.hh"
@ -199,4 +200,52 @@ static void test_framebuffer_scissor_test()
}
GPU_TEST(framebuffer_scissor_test);
/* Color each side of a cubemap with a different color. */
static void test_framebuffer_cube()
{
const int SIZE = 32;
GPU_render_begin();
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
GPUTexture *tex = GPU_texture_create_cube("tex", SIZE, 1, GPU_RGBA32F, usage, nullptr);
const float4 clear_colors[6] = {
{0.5f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.5f, 0.0f, 1.0f},
{0.0f, 1.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.5f, 1.0f},
{0.0f, 0.0f, 1.0f, 1.0f},
};
GPUFrameBuffer *framebuffers[6] = {nullptr};
for (int i : IndexRange(6)) {
GPU_framebuffer_ensure_config(&framebuffers[i],
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE_CUBEFACE(tex, i),
});
GPU_framebuffer_bind(framebuffers[i]);
GPU_framebuffer_clear_color(framebuffers[i], clear_colors[i]);
};
float4 *data = (float4 *)GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
for (int side : IndexRange(6)) {
for (int pixel_index : IndexRange(SIZE * SIZE)) {
int index = pixel_index + (SIZE * SIZE) * side;
EXPECT_EQ(clear_colors[side], data[index]);
}
}
MEM_freeN(data);
GPU_texture_free(tex);
for (int i : IndexRange(6)) {
GPU_FRAMEBUFFER_FREE_SAFE(framebuffers[i]);
}
GPU_render_end();
}
GPU_TEST(framebuffer_cube)
} // namespace blender::gpu::tests

View File

@ -63,6 +63,52 @@ static void test_texture_read()
}
GPU_TEST(texture_read)
static void test_texture_cube()
{
const int SIZE = 32;
GPU_render_begin();
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
GPUTexture *tex = GPU_texture_create_cube("tex", SIZE, 1, GPU_RGBA32F, usage, nullptr);
float4 clear_color(1.0f, 0.5f, 0.2f, 1.0f);
GPU_texture_clear(tex, GPU_DATA_FLOAT, clear_color);
float4 *data = (float4 *)GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
for (int index : IndexRange(SIZE * SIZE * 6)) {
EXPECT_EQ(clear_color, data[index]);
}
MEM_freeN(data);
GPU_texture_free(tex);
GPU_render_end();
}
GPU_TEST(texture_cube)
static void test_texture_cube_array()
{
const int SIZE = 32;
const int ARRAY = 2;
GPU_render_begin();
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
GPUTexture *tex = GPU_texture_create_cube_array(
"tex", SIZE, ARRAY, 1, GPU_RGBA32F, usage, nullptr);
float4 clear_color(1.0f, 0.5f, 0.2f, 1.0f);
GPU_texture_clear(tex, GPU_DATA_FLOAT, clear_color);
float4 *data = (float4 *)GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
for (int index : IndexRange(SIZE * SIZE * 6 * ARRAY)) {
EXPECT_EQ(clear_color, data[index]);
}
MEM_freeN(data);
GPU_texture_free(tex);
GPU_render_end();
}
GPU_TEST(texture_cube_array)
static void test_texture_copy()
{
const int SIZE = 128;

View File

@ -108,7 +108,10 @@ void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_
command_buffer.dispatch(groups_x_len, groups_y_len, groups_z_len);
}
void VKBackend::compute_dispatch_indirect(StorageBuf * /*indirect_buf*/) {}
void VKBackend::compute_dispatch_indirect(StorageBuf * /*indirect_buf*/)
{
NOT_YET_IMPLEMENTED;
}
Context *VKBackend::context_alloc(void *ghost_window, void *ghost_context)
{

View File

@ -585,17 +585,22 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size, GPUVertFe
VkImageType to_vk_image_type(const eGPUTextureType type)
{
/* See
* https://vulkan.lunarg.com/doc/view/1.3.243.0/linux/1.3-extensions/vkspec.html#resources-image-views-compatibility
* for reference */
switch (type) {
case GPU_TEXTURE_1D:
case GPU_TEXTURE_BUFFER:
case GPU_TEXTURE_1D_ARRAY:
return VK_IMAGE_TYPE_1D;
case GPU_TEXTURE_2D:
case GPU_TEXTURE_2D_ARRAY:
return VK_IMAGE_TYPE_2D;
case GPU_TEXTURE_3D:
case GPU_TEXTURE_CUBE:
case GPU_TEXTURE_CUBE_ARRAY:
return VK_IMAGE_TYPE_2D;
case GPU_TEXTURE_3D:
return VK_IMAGE_TYPE_3D;
case GPU_TEXTURE_ARRAY:
@ -607,7 +612,7 @@ VkImageType to_vk_image_type(const eGPUTextureType type)
return VK_IMAGE_TYPE_1D;
}
VkImageViewType to_vk_image_view_type(const eGPUTextureType type)
VkImageViewType to_vk_image_view_type(const eGPUTextureType type, const eImageViewUsage view_type)
{
switch (type) {
case GPU_TEXTURE_1D:
@ -618,13 +623,15 @@ VkImageViewType to_vk_image_view_type(const eGPUTextureType type)
case GPU_TEXTURE_3D:
return VK_IMAGE_VIEW_TYPE_3D;
case GPU_TEXTURE_CUBE:
return VK_IMAGE_VIEW_TYPE_CUBE;
return view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
VK_IMAGE_VIEW_TYPE_CUBE;
case GPU_TEXTURE_1D_ARRAY:
return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
case GPU_TEXTURE_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case GPU_TEXTURE_CUBE_ARRAY:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
return view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case GPU_TEXTURE_ARRAY:
/* GPU_TEXTURE_ARRAY should always be used together with 1D, 2D, or CUBE*/

View File

@ -23,13 +23,27 @@
namespace blender::gpu {
/**
* Based on the usage of an Image View a different image view type should be created.
*
* When using a GPU_TEXTURE_CUBE as an frame buffer attachment it will be used as a
* GPU_TEXTURE_2D_ARRAY. eg only a single side of the cube map will be attached. But when bound as
* a shader resource the cubemap will be used.
*/
enum class eImageViewUsage {
/** Image View will be used as a bindable shader resource. */
ShaderBinding,
/** Image View will be used as an framebuffer attachment. */
Attachment,
};
VkImageAspectFlagBits to_vk_image_aspect_flag_bits(const eGPUTextureFormat format);
VkFormat to_vk_format(const eGPUTextureFormat format);
VkFormat to_vk_format(const GPUVertCompType type,
const uint32_t size,
const GPUVertFetchMode fetch_mode);
VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat format);
VkImageViewType to_vk_image_view_type(const eGPUTextureType type);
VkImageViewType to_vk_image_view_type(const eGPUTextureType type, eImageViewUsage view_type);
VkImageType to_vk_image_type(const eGPUTextureType type);
VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data);
VkIndexType to_vk_index_type(const GPUIndexBufType index_type);

View File

@ -404,7 +404,7 @@ void VKFrameBuffer::render_pass_create()
/* Ensure texture is allocated to ensure the image view. */
VKTexture &texture = *static_cast<VKTexture *>(unwrap(attachment.tex));
texture.ensure_allocated();
image_views_.append(VKImageView(texture, attachment.mip, name_));
image_views_.append(VKImageView(texture, attachment.layer, attachment.mip, name_));
image_views[attachment_location] = image_views_.last().vk_handle();
VkAttachmentDescription &attachment_description =

View File

@ -15,8 +15,8 @@
namespace blender::gpu {
VKImageView::VKImageView(VKTexture &texture, int mip_level, StringRefNull name)
: vk_image_view_(create_vk_image_view(texture, mip_level, name))
VKImageView::VKImageView(VKTexture &texture, int layer, int mip_level, StringRefNull name)
: vk_image_view_(create_vk_image_view(texture, layer, mip_level, name))
{
BLI_assert(vk_image_view_ != VK_NULL_HANDLE);
}
@ -41,6 +41,7 @@ VKImageView::~VKImageView()
}
}
VkImageView VKImageView::create_vk_image_view(VKTexture &texture,
int layer,
int mip_level,
StringRefNull name)
{
@ -49,14 +50,15 @@ VkImageView VKImageView::create_vk_image_view(VKTexture &texture,
VkImageViewCreateInfo image_view_info = {};
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.image = texture.vk_image_handle();
image_view_info.viewType = to_vk_image_view_type(texture.type_get());
image_view_info.viewType = to_vk_image_view_type(texture.type_get(),
eImageViewUsage::Attachment);
image_view_info.format = to_vk_format(texture.format_get());
image_view_info.components = to_vk_component_mapping(texture.format_get());
image_view_info.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(texture.format_get());
image_view_info.subresourceRange.baseMipLevel = mip_level;
image_view_info.subresourceRange.levelCount = 1;
image_view_info.subresourceRange.baseArrayLayer = 0;
image_view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
image_view_info.subresourceRange.baseArrayLayer = layer == -1 ? 0 : layer;
image_view_info.subresourceRange.layerCount = 1;
const VKDevice &device = VKBackend::get().device_get();
VkImageView image_view = VK_NULL_HANDLE;

View File

@ -20,7 +20,7 @@ class VKImageView : NonCopyable {
VkImageView vk_image_view_ = VK_NULL_HANDLE;
public:
VKImageView(VKTexture &texture, int mip_level, StringRefNull name);
VKImageView(VKTexture &texture, int layer, int mip_level, StringRefNull name);
/**
* Wrap the given vk_image_view handle. Note that the vk_image_view handle ownership is
@ -38,7 +38,10 @@ class VKImageView : NonCopyable {
}
private:
static VkImageView create_vk_image_view(VKTexture &texture, int mip_level, StringRefNull name);
static VkImageView create_vk_image_view(VKTexture &texture,
int layer,
int mip_level,
StringRefNull name);
};
} // namespace blender::gpu

View File

@ -157,6 +157,15 @@ void VKTexture::mip_range_set(int min, int max)
flags_ |= IMAGE_VIEW_DIRTY;
}
int VKTexture::layer_count()
{
int layers = 1;
if (ELEM(type_, GPU_TEXTURE_CUBE, GPU_TEXTURE_CUBE_ARRAY)) {
layers = d_;
}
return layers;
}
void VKTexture::read_sub(int mip, eGPUDataFormat format, const int area[4], void *r_data)
{
VKContext &context = *VKContext::get();
@ -165,7 +174,7 @@ void VKTexture::read_sub(int mip, eGPUDataFormat format, const int area[4], void
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
VKBuffer staging_buffer;
size_t sample_len = area[2] * area[3];
size_t sample_len = area[2] * area[3] * layer_count();
size_t device_memory_size = sample_len * to_bytesize(format_);
staging_buffer.create(
@ -179,7 +188,7 @@ void VKTexture::read_sub(int mip, eGPUDataFormat format, const int area[4], void
region.imageExtent.depth = 1;
region.imageSubresource.aspectMask = to_vk_image_aspect_flag_bits(format_);
region.imageSubresource.mipLevel = mip;
region.imageSubresource.layerCount = 1;
region.imageSubresource.layerCount = layer_count();
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.copy(staging_buffer, *this, Span<VkBufferImageCopy>(&region, 1));
@ -192,7 +201,7 @@ void *VKTexture::read(int mip, eGPUDataFormat format)
{
int mip_size[3] = {1, 1, 1};
mip_size_get(mip, mip_size);
size_t sample_len = mip_size[0] * mip_size[1];
size_t sample_len = mip_size[0] * mip_size[1] * layer_count();
size_t host_memory_size = sample_len * to_bytesize(format_, format);
void *data = MEM_mallocN(host_memory_size, __func__);
@ -372,6 +381,17 @@ static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
return result;
}
static VkImageCreateFlagBits to_vk_image_create(const eGPUTextureType texture_type)
{
VkImageCreateFlagBits result = static_cast<VkImageCreateFlagBits>(0);
if (ELEM(texture_type, GPU_TEXTURE_CUBE, GPU_TEXTURE_CUBE_ARRAY)) {
result = static_cast<VkImageCreateFlagBits>(result | VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT);
}
return result;
}
bool VKTexture::allocate()
{
BLI_assert(vk_image_ == VK_NULL_HANDLE);
@ -379,17 +399,23 @@ bool VKTexture::allocate()
int extent[3] = {1, 1, 1};
mip_size_get(0, extent);
int layers = 1;
if (ELEM(type_, GPU_TEXTURE_CUBE, GPU_TEXTURE_CUBE_ARRAY)) {
layers = extent[2];
extent[2] = 1;
}
VKContext &context = *VKContext::get();
const VKDevice &device = VKBackend::get().device_get();
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.flags = to_vk_image_create(type_);
image_info.imageType = to_vk_image_type(type_);
image_info.extent.width = extent[0];
image_info.extent.height = extent[1];
image_info.extent.depth = extent[2];
image_info.mipLevels = max_ii(mipmaps_, 1);
image_info.arrayLayers = 1;
image_info.arrayLayers = layers;
image_info.format = to_vk_format(format_);
/* Some platforms (NVIDIA) requires that attached textures are always tiled optimal.
*
@ -536,14 +562,15 @@ void VKTexture::image_view_update()
VkImageViewCreateInfo image_view_info = {};
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.image = vk_image_;
image_view_info.viewType = to_vk_image_view_type(type_);
image_view_info.viewType = to_vk_image_view_type(type_, eImageViewUsage::ShaderBinding);
image_view_info.format = to_vk_format(format_);
image_view_info.components = to_vk_component_mapping(format_);
image_view_info.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_);
IndexRange mip_range = mip_map_range();
image_view_info.subresourceRange.baseMipLevel = mip_range.first();
image_view_info.subresourceRange.levelCount = mip_range.size();
image_view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
image_view_info.subresourceRange.layerCount =
ELEM(type_, GPU_TEXTURE_CUBE, GPU_TEXTURE_CUBE_ARRAY) ? d_ : VK_REMAINING_ARRAY_LAYERS;
const VKDevice &device = VKBackend::get().device_get();
VkImageView image_view = VK_NULL_HANDLE;

View File

@ -86,6 +86,8 @@ class VKTexture : public Texture {
*/
bool allocate();
int layer_count();
VkImageViewType vk_image_view_type() const;
/* -------------------------------------------------------------------- */

View File

@ -357,7 +357,6 @@ static void rna_AssetHandle_file_data_set(PointerRNA *ptr,
static void rna_AssetHandle_get_full_library_path(
// AssetHandle *asset,
FileDirEntry *asset_file,
AssetLibraryReference *UNUSED(asset_library), /* Deprecated. */
char r_result[/*FILE_MAX_LIBEXTRA*/])
{
AssetHandle asset = {.file_data = asset_file};
@ -537,13 +536,6 @@ static void rna_def_asset_handle_api(StructRNA *srna)
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_pointer(
func,
"asset_library_ref",
"AssetLibraryReference",
"",
"The asset library containing the given asset. Deprecated and optional argument, will be "
"ignored. Kept for API compatibility only");
parm = RNA_def_string(func, "result", NULL, FILE_MAX_LIBEXTRA, "result", "");
RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
RNA_def_function_output(func, parm);

View File

@ -72,7 +72,9 @@
struct Vert2GeomData {
/* Read-only data */
float (*v_cos)[3];
const float (*v_cos)[3];
const int *indices;
const SpaceTransform *loc2trgt;
@ -106,7 +108,7 @@ static void vert2geom_task_cb_ex(void *__restrict userdata,
int i;
/* Convert the vertex to tree coordinates. */
copy_v3_v3(tmp_co, data->v_cos[iter]);
copy_v3_v3(tmp_co, data->v_cos[data->indices ? data->indices[iter] : iter]);
BLI_space_transform_apply(data->loc2trgt, tmp_co);
for (i = 0; i < ARRAY_SIZE(data->dist); i++) {
@ -145,7 +147,8 @@ static void vert2geom_task_cb_ex(void *__restrict userdata,
* Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c).
*/
static void get_vert2geom_distance(int verts_num,
float (*v_cos)[3],
const float (*v_cos)[3],
const int *indices,
float *dist_v,
float *dist_e,
float *dist_f,
@ -185,6 +188,7 @@ static void get_vert2geom_distance(int verts_num,
}
data.v_cos = v_cos;
data.indices = indices;
data.loc2trgt = loc2trgt;
data.treeData[0] = &treeData_v;
data.treeData[1] = &treeData_e;
@ -215,8 +219,12 @@ static void get_vert2geom_distance(int verts_num,
* Returns the real distance between a vertex and another reference object.
* Note that it works in final world space (i.e. with constraints etc. applied).
*/
static void get_vert2ob_distance(
int verts_num, float (*v_cos)[3], float *dist, Object *ob, Object *obr)
static void get_vert2ob_distance(int verts_num,
const float (*v_cos)[3],
const int *indices,
float *dist,
Object *ob,
Object *obr)
{
/* Vertex and ref object coordinates. */
float v_wco[3];
@ -224,7 +232,7 @@ static void get_vert2ob_distance(
while (i-- > 0) {
/* Get world-coordinates of the vertex (constraints and anim included). */
mul_v3_m4v3(v_wco, ob->object_to_world, v_cos[i]);
mul_v3_m4v3(v_wco, ob->object_to_world, v_cos[indices ? indices[i] : i]);
/* Return distance between both coordinates. */
dist[i] = len_v3v3(v_wco, obr->object_to_world[3]);
}
@ -421,7 +429,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md;
MDeformWeight **dw, **tdw;
float(*v_cos)[3] = nullptr; /* The vertices coordinates. */
const float(*v_cos)[3] = nullptr; /* The vertices coordinates. */
Object *ob = ctx->object;
Object *obr = nullptr; /* Our target object. */
int defgrp_index;
@ -516,16 +524,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MEM_freeN(tidx);
/* Get our vertex coordinates. */
if (index_num != verts_num) {
const float(*tv_cos)[3] = BKE_mesh_vert_positions(mesh);
v_cos = static_cast<float(*)[3]>(MEM_malloc_arrayN(index_num, sizeof(float[3]), __func__));
for (i = 0; i < index_num; i++) {
copy_v3_v3(v_cos[i], tv_cos[indices[i]]);
}
}
else {
v_cos = BKE_mesh_vert_coords_alloc(mesh, nullptr);
}
v_cos = BKE_mesh_vert_positions(mesh);
/* Compute wanted distances. */
if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) {
@ -562,7 +561,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BLI_SPACE_TRANSFORM_SETUP(&loc2trgt, ob, obr);
get_vert2geom_distance(
index_num, v_cos, dists_v, dists_e, dists_f, target_mesh, &loc2trgt);
index_num, v_cos, indices, dists_v, dists_e, dists_f, target_mesh, &loc2trgt);
for (i = 0; i < index_num; i++) {
new_w[i] = dists_v ? dists_v[i] : FLT_MAX;
if (dists_e) {
@ -579,11 +578,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
/* Else, fall back to default obj2vert behavior. */
else {
get_vert2ob_distance(index_num, v_cos, new_w, ob, obr);
get_vert2ob_distance(index_num, v_cos, indices, new_w, ob, obr);
}
}
else {
get_vert2ob_distance(index_num, v_cos, new_w, ob, obr);
get_vert2ob_distance(index_num, v_cos, indices, new_w, ob, obr);
}
}
@ -632,7 +631,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MEM_freeN(org_w);
MEM_freeN(new_w);
MEM_freeN(dw);
MEM_freeN(v_cos);
MEM_SAFE_FREE(indices);
#ifdef USE_TIMEIT

View File

@ -825,36 +825,47 @@ class RenderLayerOperation : public NodeOperation {
void execute() override
{
const int view_layer = bnode().custom1;
GPUTexture *pass_texture = context().get_input_texture(view_layer, RE_PASSNAME_COMBINED);
GPUTexture *combined_texture = context().get_input_texture(view_layer, RE_PASSNAME_COMBINED);
execute_image(pass_texture);
execute_alpha(pass_texture);
execute_pass("Image", combined_texture, "compositor_read_pass_color");
execute_pass("Alpha", combined_texture, "compositor_read_pass_alpha");
/* Other output passes are not supported for now, so allocate them as invalid. */
for (const bNodeSocket *output : this->node()->output_sockets()) {
if (!STR_ELEM(output->identifier, "Image", "Alpha")) {
Result &unsupported_result = get_result(output->identifier);
if (unsupported_result.should_compute()) {
unsupported_result.allocate_invalid();
context().set_info_message("Viewport compositor setup not fully supported");
}
if (STR_ELEM(output->identifier, "Image", "Alpha")) {
continue;
}
GPUTexture *pass_texture = context().get_input_texture(view_layer, output->identifier);
if (output->type == SOCK_FLOAT) {
execute_pass(output->identifier, pass_texture, "compositor_read_pass_float");
}
else if (output->type == SOCK_VECTOR) {
execute_pass(output->identifier, pass_texture, "compositor_read_pass_vector");
}
else if (output->type == SOCK_RGBA) {
execute_pass(output->identifier, pass_texture, "compositor_read_pass_color");
}
else {
BLI_assert_unreachable();
}
}
}
void execute_image(GPUTexture *pass_texture)
void execute_pass(const char *pass_name, GPUTexture *pass_texture, const char *shader_name)
{
Result &image_result = get_result("Image");
Result &image_result = get_result(pass_name);
if (!image_result.should_compute()) {
return;
}
if (pass_texture == nullptr) {
/* Pass not rendered (yet). */
/* Pass not rendered yet, or not supported by viewport. */
image_result.allocate_invalid();
context().set_info_message("Viewport compositor setup not fully supported");
return;
}
GPUShader *shader = shader_manager().get("compositor_read_pass");
GPUShader *shader = shader_manager().get(shader_name);
GPU_shader_bind(shader);
/* The compositing space might be limited to a subset of the pass texture, so only read that
@ -876,41 +887,6 @@ class RenderLayerOperation : public NodeOperation {
GPU_texture_unbind(pass_texture);
image_result.unbind_as_image();
}
void execute_alpha(GPUTexture *pass_texture)
{
Result &alpha_result = get_result("Alpha");
if (!alpha_result.should_compute()) {
return;
}
if (pass_texture == nullptr) {
/* Pass not rendered (yet). */
alpha_result.allocate_invalid();
return;
}
GPUShader *shader = shader_manager().get("compositor_read_pass_alpha");
GPU_shader_bind(shader);
/* The compositing space might be limited to a subset of the pass texture, so only read that
* compositing region into an appropriately sized texture. */
const rcti compositing_region = context().get_compositing_region();
const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin);
GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound);
const int input_unit = GPU_shader_get_sampler_binding(shader, "input_tx");
GPU_texture_bind(pass_texture, input_unit);
const int2 compositing_region_size = context().get_compositing_region_size();
alpha_result.allocate_texture(Domain(compositing_region_size));
alpha_result.bind_as_image(shader, "output_img");
compute_dispatch_threads_at_least(shader, compositing_region_size);
GPU_shader_unbind();
GPU_texture_unbind(pass_texture);
alpha_result.unbind_as_image();
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)

View File

@ -77,7 +77,7 @@ class ViewerOperation : public NodeOperation {
const Result &second_image = get_input("Image_001");
second_image.bind_as_texture(shader, "second_image_tx");
GPUTexture *output_texture = context().get_output_texture();
GPUTexture *output_texture = context().get_viewer_output_texture();
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
GPU_texture_image_bind(output_texture, image_unit);

View File

@ -105,7 +105,7 @@ class ViewerOperation : public NodeOperation {
color.w = alpha.get_float_value();
}
GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
GPU_texture_clear(context().get_viewer_output_texture(), GPU_DATA_FLOAT, color);
}
/* Executes when the alpha channel of the image is ignored. */
@ -123,7 +123,7 @@ class ViewerOperation : public NodeOperation {
const Result &image = get_input("Image");
image.bind_as_texture(shader, "input_tx");
GPUTexture *output_texture = context().get_output_texture();
GPUTexture *output_texture = context().get_viewer_output_texture();
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
GPU_texture_image_bind(output_texture, image_unit);
@ -151,7 +151,7 @@ class ViewerOperation : public NodeOperation {
const Result &image = get_input("Image");
image.bind_as_texture(shader, "input_tx");
GPUTexture *output_texture = context().get_output_texture();
GPUTexture *output_texture = context().get_viewer_output_texture();
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
GPU_texture_image_bind(output_texture, image_unit);
@ -181,7 +181,7 @@ class ViewerOperation : public NodeOperation {
const Result &alpha = get_input("Alpha");
alpha.bind_as_texture(shader, "alpha_tx");
GPUTexture *output_texture = context().get_output_texture();
GPUTexture *output_texture = context().get_viewer_output_texture();
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
GPU_texture_image_bind(output_texture, image_unit);

View File

@ -2,9 +2,13 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstring>
#include "BLI_threads.h"
#include "BLI_vector.hh"
#include "MEM_guardedalloc.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_node.hh"
@ -12,6 +16,9 @@
#include "DRW_engine.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "COM_context.hh"
#include "COM_evaluator.hh"
@ -63,6 +70,9 @@ class Context : public realtime_compositor::Context {
/* Output combined texture. */
GPUTexture *output_texture_ = nullptr;
/* Viewer output texture. */
GPUTexture *viewer_output_texture_ = nullptr;
public:
Context(const Scene &scene,
const RenderData &render_data,
@ -81,7 +91,8 @@ class Context : public realtime_compositor::Context {
virtual ~Context()
{
GPU_texture_free(output_texture_);
GPU_TEXTURE_FREE_SAFE(output_texture_);
GPU_TEXTURE_FREE_SAFE(viewer_output_texture_);
}
const bNodeTree &get_node_tree() const override
@ -94,6 +105,11 @@ class Context : public realtime_compositor::Context {
return use_file_output_;
}
bool use_composite_output() const override
{
return true;
}
bool use_texture_color_management() const override
{
return BKE_scene_check_color_management_enabled(&scene_);
@ -121,7 +137,7 @@ class Context : public realtime_compositor::Context {
GPUTexture *get_output_texture() override
{
/* TODO: support outputting for viewers and previews.
/* TODO: support outputting for previews.
* TODO: just a temporary hack, needs to get stored in RenderResult,
* once that supports GPU buffers. */
if (output_texture_ == nullptr) {
@ -138,6 +154,25 @@ class Context : public realtime_compositor::Context {
return output_texture_;
}
GPUTexture *get_viewer_output_texture() override
{
/* TODO: support outputting previews.
* TODO: just a temporary hack, needs to get stored in RenderResult,
* once that supports GPU buffers. */
if (viewer_output_texture_ == nullptr) {
const int2 size = get_render_size();
viewer_output_texture_ = GPU_texture_create_2d("compositor_viewer_output_texture",
size.x,
size.y,
1,
GPU_RGBA16F,
GPU_TEXTURE_USAGE_GENERAL,
NULL);
}
return viewer_output_texture_;
}
GPUTexture *get_input_texture(int view_layer_id, const char *pass_name) override
{
/* TODO: eventually this should get cached on the RenderResult itself when
@ -224,6 +259,10 @@ class Context : public realtime_compositor::Context {
void output_to_render_result()
{
if (!output_texture_) {
return;
}
Render *re = RE_GetSceneRender(&scene_);
RenderResult *rr = RE_AcquireResultWrite(re);
@ -253,6 +292,55 @@ class Context : public realtime_compositor::Context {
BKE_image_signal(G.main, image, nullptr, IMA_SIGNAL_FREE);
BLI_thread_unlock(LOCK_DRAW_IMAGE);
}
void viewer_output_to_viewer_image()
{
if (!viewer_output_texture_) {
return;
}
Image *image = BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node");
ImageUser image_user = {0};
image_user.multi_index = BKE_scene_multiview_view_id_get(&render_data_, view_name_);
if (BKE_scene_multiview_is_render_view_first(&render_data_, view_name_)) {
BKE_image_ensure_viewer_views(&render_data_, image, &image_user);
}
BLI_thread_lock(LOCK_DRAW_IMAGE);
void *lock;
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user, &lock);
const int2 render_size = get_render_size();
if (image_buffer->x != render_size.x || image_buffer->y != render_size.y) {
imb_freerectImBuf(image_buffer);
imb_freerectfloatImBuf(image_buffer);
IMB_freezbuffloatImBuf(image_buffer);
image_buffer->x = render_size.x;
image_buffer->y = render_size.y;
imb_addrectfloatImBuf(image_buffer, 4);
image_buffer->userflags |= IB_DISPLAY_BUFFER_INVALID;
}
BKE_image_release_ibuf(image, image_buffer, lock);
BLI_thread_unlock(LOCK_DRAW_IMAGE);
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
float *output_buffer = (float *)GPU_texture_read(viewer_output_texture_, GPU_DATA_FLOAT, 0);
std::memcpy(image_buffer->float_buffer.data,
output_buffer,
render_size.x * render_size.y * 4 * sizeof(float));
MEM_freeN(output_buffer);
BKE_image_partial_update_mark_full_update(image);
if (node_tree_.runtime->update_draw) {
node_tree_.runtime->update_draw(node_tree_.runtime->udh);
}
}
};
/* Render Realtime Compositor */
@ -289,6 +377,7 @@ void RealtimeCompositor::execute()
DRW_render_context_enable(&render_);
evaluator_->evaluate();
context_->output_to_render_result();
context_->viewer_output_to_viewer_image();
DRW_render_context_disable(&render_);
}